www.pudn.com > SuperOPC-vc7.rar > AddItemDlg.cpp
// AddItemDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "SuperOPC.h"
#include "AddItemDlg.h"
#include ".\additemdlg.h"
// Registry sections;
static LPCTSTR lpszRegSection = _T("Add Items");
static LPCTSTR lpszAutoValidate = _T("Auto Validate");
static LPCTSTR lpszBrowseFlat = _T("Browse Flat");
// Default add item list size:
#define DEF_ITEM_LIST_SIZE 16
// Branch dummy item data:
#define NULL_ITEM_NAME _T("_QCNULL_DUMMY")
#define NULL_ITEM_DATA 0xFFFFFFFF
// Image list indices:
#define ILI_BRANCH 8
#define ILI_SELBRANCH 9
#define ILI_LEAF 3
// Access rights filter combo box indices:
#define CB_ACCESS_ANY 0
#define CB_ACCESS_READONLY 1
#define CB_ACCESS_WRITEONLY 2
#define CB_ACCESS_READWRITE 4
// CAddItemDlg 对话框
IMPLEMENT_DYNAMIC(CAddItemDlg, CDialog)
CAddItemDlg::CAddItemDlg(CHotOpcGroup *pGroup, IOPCBrowseServerAddressSpace *pIBrowse, CWnd* pParent /*=NULL*/)
: CDialog(CAddItemDlg::IDD, pParent)
, m_strItemID(_T(""))
{
ASSERT (pGroup != NULL);
m_pGroup = pGroup;
m_pIBrowse = pIBrowse;
m_vtDataType = VT_EMPTY;
m_nListIndex = 0;
m_nSelIndex = 0;
m_bModified = false;
m_pIItemMgt = NULL;
// Load registry settings. These are settings we may want to use each time
// we add some items:
m_bAutoValidate = FALSE;
m_bBrowseFlat = FALSE;
// Allocate memory for add item list:
try
{
m_cItemList.SetSize (DEF_ITEM_LIST_SIZE, DEF_ITEM_LIST_SIZE);
}
catch (...)
{
ASSERT (FALSE);
}
}
CAddItemDlg::~CAddItemDlg()
{
}
void CAddItemDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
// Transfer data type combo box index to vartype:
CComboBox *pCombo = (CComboBox *)GetDlgItem (IDC_DATATYPE);
CListCtrl *pList = (CListCtrl *)GetDlgItem(IDC_LEAFLIST);
pList->ModifyStyle(NULL, LVS_REPORT | LVS_SORTASCENDING ,0);
pList->SetExtendedStyle(LVS_EX_FULLROWSELECT);
// If save and validate, transfer data type combo box index to
// vartype member variable:
if (pDX->m_bSaveAndValidate)
{
CString strType;
pCombo->GetLBText (pCombo->GetCurSel (), strType);
m_vtDataType = VartypeFromString (strType);
}
// else use current member variable value to make combo box selection:
else
{
CString strType;
// Convert data type to combo box entry string:
switch (m_vtDataType & ~VT_ARRAY)
{
case VT_BOOL: strType = _T("Boolean"); break;
case VT_UI1: strType = _T("Byte"); break;
case VT_I1: strType = _T("Char"); break;
case VT_UI2: strType = _T("Word"); break;
case VT_I2: strType = _T("Short"); break;
case VT_UI4: strType = _T("DWord"); break;
case VT_I4: strType = _T("Long"); break;
case VT_R4: strType = _T("Float"); break;
case VT_R8: strType = _T("Double"); break;
case VT_BSTR: strType = _T("String"); break;
default: strType = _T("Native"); break;
}
// Update array flag data:
if ((m_vtDataType & VT_ARRAY) != 0)
strType += _T(" Array");
// Select combo box entry:
pCombo->SelectString (-1, strType);
}
DDX_Text(pDX, IDC_ACCESSPATH, m_strAccessPath);
DDX_Text(pDX, IDC_ITEMID, m_strItemID);
}
BEGIN_MESSAGE_MAP(CAddItemDlg, CDialog)
ON_NOTIFY(TVN_ITEMEXPANDING, IDC_BRANCHLIST, OnTvnItemexpandingBranchlist)
ON_NOTIFY(TVN_SELCHANGED, IDC_BRANCHLIST, OnTvnSelchangedBranchlist)
ON_CBN_SELCHANGE(IDC_DATATYPE, OnCbnSelchangeDatatype)
ON_EN_CHANGE(IDC_ACCESSPATH, OnEnChangeAccesspath)
ON_EN_CHANGE(IDC_ITEMID, OnEnChangeItemid)
ON_WM_SHOWWINDOW()
ON_NOTIFY(NM_DBLCLK, IDC_LEAFLIST, OnNMDblclkLeaflist)
END_MESSAGE_MAP()
// CAddItemDlg 消息处理程序
BOOL CAddItemDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Limit the access path to 255 chars:
((CEdit *)GetDlgItem (IDC_ACCESSPATH))->LimitText (255);
// Allow 10 levels of 32 character names plus a tag name of 31 characters:
((CEdit *)GetDlgItem (IDC_ITEMID))->LimitText (10*32 + 31);
// Get our group's item management interface pointer for the group:
m_pIItemMgt = m_pGroup->GetIItemMgt ();
// Intialize our item browser:
InitializeBrowser ();
// Intialize control status:
UpdateStatus ();
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
// **************************************************************************
// AutoIncrementID ()
//
// Description:
// Create a new item ID by incrementing current ID.
//
// Parameters:
// bool bDotBitAddress Set to true if item has "dot bit number"
// address format.
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::AutoIncrementID (bool bDotBitAddress)
{
static TCHAR szBuffer [MAX_PATH];
static TCHAR *pch = NULL;
// If the item ID is defined, we will modify it by incrementing:
if (!m_strItemID.IsEmpty ())
{
// Make a local copy of the current item ID:
lstrcpyn (szBuffer, m_strItemID, sizeof (szBuffer) / sizeof (TCHAR));
// Ensure it is null terminated:
szBuffer [MAX_PATH - 1] = '\0';
// Get a pointer to the last character of the string (search for
// NULL terminator, then back up a character):
pch = _tcschr (szBuffer, _T('\0'));
pch--;
// If it is a digit we can simply increment this address:
if (_istdigit (*pch))
{
int nNumber;
// Scan backwards from last character until we encounter
// a non-digit:
while (_istdigit (*pch) && pch != szBuffer)
pch--;
// Obtain the number we are peeling off (go to next character
// which will be the first digit, then convert text to right
// to an integer):
pch++;
nNumber = _ttol (pch);
// Increment the number:
nNumber++;
// If we are dealing with a "dot bit number" address,
// make sure we do not increment bit number beyond 15.
// Increment bit numbers to 15, then increment offset
// and make bit number 0.
// If bDotBitAddress is set, then character to the left
// of current pointer should be the "dot".
if (bDotBitAddress && *(pch - 1) == _T('.'))
{
// Assume dot specifier is a bit and increment 0 - 15.
// If nNumber is greater than 15, determine offset, and
// increment that instead of the bit number:
if (nNumber > 15)
{
// Roll offset ahead:
pch = _tcsrchr (szBuffer, _T('.'));
pch--;
// Peel back looking for the first non-digit:
while (_istdigit (*pch) && pch != szBuffer)
pch--;
// Go to next character and convert to integer. This will
// be the offset since _ttol will not use any characters to
// the right of the dot. Increment the result by one. This
// will be our new offset.
pch++;
nNumber = _ttol (pch) + 1;
// Replace old offset and bit number with incremented
// offset and bit number "00".
_stprintf (pch, _T("%d.00"), nNumber);
}
// Otherwise just replace the bit number with incremeted
// bit number:
else
_stprintf (pch, _T("%02d"), nNumber);
}
// If not a dot bit number address, make sure we do not lose
// any leading zeros (as in modbus addresses):
else
{
// We should currently be pointing at first character of
// trailing digit portion of address. Check to see if
// this is a zero.
if (*pch == _T('0'))
{
// We found a leading zero.
TCHAR *pZeros = pch;
int cnDigits = 0;
// Scan forwared until we find a non-zero character.
// Count each zero along the way:
while (*pZeros++ != _T('\0'))
cnDigits++;
// Replace the trailing digits of the adress with
// the new incremented number, keeping the same
// number of leading zeros:
_stprintf (pch, _T("%0*d"), cnDigits, nNumber);
}
// Else if no leading zeros, just replace the trailing
// digits with the new incremented number:
else
_stprintf (pch, _T("%d"), nNumber);
}
}
// else if last character is not a digit, append a good starting offset:
else
*(pch + 1) = '0';
// Update item ID:
m_strItemID = szBuffer;
// Update controls:
UpdateData (false);
// Set modifed flag:
m_bModified = true;
}
}
// **************************************************************************
// UpdateStatus ()
//
// Description:
// Update status of dialog controls based on current item selected.
//
// Parameters:
// none
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::UpdateStatus ()
{
// Determine the focus so that if we disable the focused control
// we can move it.
HWND hWnd = ::GetFocus ();
// Enable the next button if the selected item has an item ID defined:
// Update the browse controls:
m_pBranchList->EnableWindow (m_pIBrowse != NULL);
m_pLeafList->EnableWindow (m_pIBrowse != NULL);
// Set the focus to the first control if the previous control with the
// focus just lost it:
if (hWnd && !::IsWindowEnabled (hWnd))
GetDlgItem (IDC_ITEMID)->SetFocus ();
}
// **************************************************************************
// InitializeBrowser ()
//
// Description:
// Initialize things related to the browser.
//
// Parameters:
// none
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::InitializeBrowser ()
{
// Get pointers to our browse controls for easy access:
m_pBranchList = (CTreeCtrl *) GetDlgItem (IDC_BRANCHLIST);
ASSERT (m_pBranchList != NULL);
m_pLeafList = (CListCtrl *) GetDlgItem (IDC_LEAFLIST);
ASSERT (m_pLeafList != NULL);
// Insert a column for our leaf item list control:
CRect rc;
m_pLeafList->GetClientRect (&rc);
m_pLeafList->InsertColumn (0, _T(""), LVCFMT_LEFT, rc.Width ());
// Intialize browse controls if browsing is supported, otherwise
// just disable the controls:
if (m_pIBrowse != NULL)
{
// Create a wait cursor object. This will cause the wait cursor,
// usually an hourglass, to be displayed. When this object goes
// out of scope, its destructor will restore the previous cursor
// type.
CWaitCursor wc;
HRESULT hr;
// Query the server name space:
hr = m_pIBrowse->QueryOrganization (&m_cOpcNameSpace);
if (SUCCEEDED (hr))
{
// Browse root level:
switch (m_cOpcNameSpace)
{
case OPC_NS_HIERARCHIAL:
case OPC_NS_FLAT:
BrowseRootLevel ();
break;
// Did the foundation add a new type?
default:
ASSERT (FALSE);
break;
}
}
// We will assume a hierarchial search if the server fails this request:
// (This allows us to work with any server that uses the ICONICS toolkit.)
else
{
TRACE (_T("OTC: Attempting agressive browsing since the server failed on QueryOrganization ()\r\n"));
m_cOpcNameSpace = OPC_NS_HIERARCHIAL;
BrowseRootLevel ();
}
}
}
// **************************************************************************
// BrowseRootLevel ()
//
// Description:
// Reset the browser to the root object.
//
// Parameters:
// none
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::BrowseRootLevel ()
{
HRESULT hr = E_FAIL;
HTREEITEM hItem = NULL;
try
{
// If hierarchial namespace:
if (m_cOpcNameSpace == OPC_NS_HIERARCHIAL)
{
// Re-initialize server's browse position to the root:
do
{
// Use the OPC_BROWSE_UP rather than the OPC_BROWSE_TO which
// is only supported in OOPC version 2.0. We will have to
// browse up to root one level at a time. Function will
// fail when we are at root.
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_UP, L"\0");
} while (SUCCEEDED (hr));
// Insert our root level item:
hItem = m_pBranchList->InsertItem ((m_pGroup->GetParentServer ())->GetServerName(),
ILI_BRANCH, ILI_SELBRANCH, TVI_ROOT);
// Set the item data and add a dummy child branch:
m_pBranchList->SetItemData (hItem, DWORD (-1));
AddDummyBranch (hItem);
}
// Else flat namespace:
else
{
ASSERT (m_cOpcNameSpace == OPC_NS_FLAT);
// Insert our root level item (there is no need to add any dummy
// branches since a flat space will only have one level of leaves)
hItem = m_pBranchList->InsertItem ((m_pGroup->GetParentServer ())->GetServerName (),
ILI_BRANCH, ILI_SELBRANCH, TVI_ROOT);
}
// Select root item:
if (hItem)
m_pBranchList->SelectItem (hItem);
}
catch (...)
{
m_pIBrowse = NULL;
UpdateStatus ();
}
}
// **************************************************************************
// AddBranches ()
//
// Description:
// Add branch to tree control.
//
// Parameters:
// LPENUMSTRING pIEnumString Stores the interface pointer
// HTREEITEM hParent Handle of parent tree item
// DWORD dwData Item data value to give new branches
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::AddBranches (LPENUMSTRING pIEnumString, HTREEITEM hParent, DWORD dwData)
{
ASSERT (hParent != NULL);
ULONG celt = 1;
LPOLESTR rgelt;
ULONG celtFetched = 0;
TCHAR szBuffer [MAX_PATH];
// Remove the dummy branch if one exists:
RemoveDummyBranch (hParent);
// Start at the beginning of the list:
pIEnumString->Reset ();
pIEnumString->Next (celt, &rgelt, &celtFetched);
// Add each branch to the browse control:
while (celtFetched > 0)
{
HTREEITEM hNewItem = NULL;
// COM requis that all strings be sent in UNICODE format.
// Convert if necessary and copy to szBuffer:
#ifdef _UNICODE
lstrcpyn (szBuffer, rgelt, sizeof (szBuffer) / sizeof (TCHAR));
#else
_wcstombsz (szBuffer, rgelt, sizeof (szBuffer) / sizeof (TCHAR));
#endif
// Insert the branch:
hNewItem = m_pBranchList->InsertItem (szBuffer, ILI_BRANCH, ILI_SELBRANCH, hParent);
m_pBranchList->SetItemData (hNewItem, dwData);
// Always fake each branch into having a sub item in the tree:
AddDummyBranch (hNewItem);
// Free the branch name:
CoTaskMemFree (rgelt);
// Re-initialize and get the next item:
celt = 1;
celtFetched = 0;
pIEnumString->Next (celt, &rgelt, &celtFetched);
}
}
// **************************************************************************
// ExpandBranch ()
//
// Description:
// Called to expand branch.
//
// Parameters:
// HTREEITEM hItem Handle of tree control branch to expand.
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::ExpandBranch (HTREEITEM hItem)
{
ASSERT (hItem != NULL);
int nPos;
HRESULT hr;
LPENUMSTRING pIEnumString;
#ifndef _UNICODE
WCHAR szFilter [MAX_PATH];
#endif
// Get the new browse position from the item that was previously
// selected:
nPos = (int) m_pBranchList->GetItemData (hItem);
nPos++;
ASSERT (nPos >= 0);
try
{
// Re-intialize the server's position to the root level.
do
{
// Use the OPC_BROWSE_UP rather than the OPC_BROWSE_TO which
// is only supported in OOPC version 2.0. We will have to
// browse up to root one level at a time. Function will
// fail when we are at root.
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_UP, L"\0");
} while (SUCCEEDED (hr));
// Now browse down to the new position:
CStringArray strBranches;
HTREEITEM hParentItem;
strBranches.SetSize (nPos + 1);
hParentItem = hItem;
for (int i = 0; i <= nPos; i++)
{
ASSERT (hItem);
strBranches [i] = m_pBranchList->GetItemText (hParentItem);
hParentItem = m_pBranchList->GetParentItem (hParentItem);
}
hr = S_OK;
// > 0 we do not want to include the "Root" item since the
// client only uses this branch:
while (SUCCEEDED (hr) && nPos-- > 0)
{
#ifdef _UNICODE
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_DOWN, strBranches [nPos]);
#else
WCHAR szBranch [MAX_PATH];
_mbstowcsz (szBranch, strBranches [nPos], sizeof (szBranch) / sizeof (WCHAR));
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_DOWN, szBranch);
#endif
}
// Browse for root level:
#ifdef _UNICODE
hr = m_pIBrowse->BrowseOPCItemIDs (OPC_BRANCH, // provide items with children
m_strFilterBranch, // id filtering
VT_EMPTY, // no datatype filtering on a branch
0, // no access filtering on a branch
&pIEnumString); // store the interface pointer here
#else
_mbstowcsz (szFilter, m_strFilterBranch, sizeof (szFilter) / sizeof (WCHAR));
hr = m_pIBrowse->BrowseOPCItemIDs (OPC_BRANCH, // provide items with children
szFilter, // id filtering
VT_EMPTY, // no datatype filtering on a branch
0, // no access filtering on a branch
&pIEnumString); // store the interface pointer here
#endif
// On success add the branches to the root:
if (SUCCEEDED (hr) && pIEnumString)
{
AddBranches (pIEnumString, hItem, m_pBranchList->GetItemData (hItem) + 1);
pIEnumString->Release ();
}
else
{
RemoveDummyBranch (hItem);
throw (-1);
}
}
catch (...)
{
m_pIBrowse = NULL;
UpdateStatus ();
}
}
// **************************************************************************
// SelectBranch ()
//
// Description:
// Select a branch in tree control.
//
// Parameters:
// HTREEITEM hItem Handle of tree control's item to select
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::SelectBranch (HTREEITEM hItem)
{
ASSERT (hItem != NULL);
int nPos;
HRESULT hr;
LPENUMSTRING pIEnumString;
CStringArray strBranches;
HTREEITEM hParentItem;
#ifndef _UNICODE
WCHAR szFilter [MAX_PATH];
#endif
// Get the new browse position from the item that was previously
// selected:
nPos = (int) m_pBranchList->GetItemData (hItem);
nPos++;
ASSERT (nPos >= 0);
try
{
// Re-intialize the server's position to the root level:
do
{
// Use the OPC_BROWSE_UP rather than the OPC_BROWSE_TO which
// is only supported in OOPC version 2.0. We will have to
// browse up to root one level at a time. Function will
// fail when we are at root.
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_UP, L"\0");
} while (SUCCEEDED (hr));
// Now browse down to the new position:
strBranches.SetSize (nPos + 1);
hParentItem = hItem;
for (int i = 0; i <= nPos; i++)
{
ASSERT (hItem);
strBranches [i] = m_pBranchList->GetItemText (hParentItem);
hParentItem = m_pBranchList->GetParentItem (hParentItem);
}
hr = S_OK;
// > 0 we do not want to include the "Root" item since the client
// only uses this branch:
while (SUCCEEDED (hr) && nPos-- > 0)
{
#ifdef _UNICODE
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_DOWN, strBranches [nPos]);
#else
WCHAR szBranch [MAX_PATH];
_mbstowcsz (szBranch, strBranches [nPos], sizeof (szBranch) / sizeof (WCHAR));
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_DOWN, szBranch);
#endif
}
// Browse for root level:
#ifdef _UNICODE
hr = m_pIBrowse->BrowseOPCItemIDs (
m_bBrowseFlat ? OPC_FLAT : OPC_LEAF, // provide items with children
m_strFilterLeaf, // item id filtering
m_vtFilterType, // datatype filter
m_dwFilterAccessRights, // access rights filtering
&pIEnumString); // store the interface pointer here
#else
_mbstowcsz (szFilter, m_strFilterLeaf, sizeof (szFilter) / sizeof (WCHAR));
hr = m_pIBrowse->BrowseOPCItemIDs (
m_bBrowseFlat ? OPC_FLAT : OPC_LEAF, // provide items with children
szFilter, // item id filtering
m_vtFilterType, // datatype filter
m_dwFilterAccessRights, // access rights filtering
&pIEnumString); // store the interface pointer here
#endif
// On success add the branches to the root:
if (SUCCEEDED (hr) && pIEnumString)
{
AddLeaves (pIEnumString);
pIEnumString->Release ();
}
else
throw (-1);
}
catch (...)
{
m_pIBrowse = NULL;
UpdateStatus ();
}
}
// **************************************************************************
// AddDummyBranch ()
//
// Description:
// Add a dummy branch to tree control.
//
// Parameters:
// HTREEITEM hParent Handle to parent item.
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::AddDummyBranch (HTREEITEM hParent)
{
ASSERT (hParent != NULL);
HTREEITEM hDummyItem;
// Insert a dummy item:
hDummyItem = m_pBranchList->InsertItem (NULL_ITEM_NAME, hParent);
ASSERT (hDummyItem != NULL);
m_pBranchList->SetItemData (hDummyItem, NULL_ITEM_DATA);
}
// **************************************************************************
// RemoveDummyBranch ()
//
// Description:
// Remove a dummy branch from tree control.
//
// Parameters:
// HTREEITEM hParent Handle of parent item.
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::RemoveDummyBranch (HTREEITEM hParent)
{
ASSERT (hParent != NULL);
HTREEITEM hDummyItem;
// Get child item:
hDummyItem = m_pBranchList->GetChildItem (hParent);
while (hDummyItem)
{
CString strItem = m_pBranchList->GetItemText (hDummyItem);
if (strItem.CompareNoCase (NULL_ITEM_NAME) == 0)
{
if (m_pBranchList->GetItemData (hDummyItem) == NULL_ITEM_DATA)
{
m_pBranchList->DeleteItem (hDummyItem);
break;
}
}
hDummyItem = m_pBranchList->GetNextSiblingItem (hDummyItem);
}
}
// **************************************************************************
// DeleteChildBranches ()
//
// Description:
// Deletes all child branches of specified item in tree control
//
// Parameters:
// HTREEITEM hParent Handle to parent item.
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::DeleteChildBranches (HTREEITEM hParent)
{
ASSERT (hParent != NULL);
HTREEITEM hItem;
hItem = m_pBranchList->GetChildItem (hParent);
while (hItem)
{
m_pBranchList->DeleteItem (hItem);
hItem = m_pBranchList->GetChildItem (hParent);
}
AddDummyBranch (hParent);
}
// **************************************************************************
// AddLeaves ()
//
// Description:
// Add leaves to tree control.
//
// Parameters:
// LPENUMSTRING pIEnumString Stores the interface pointer
//
// Returns:
// void
// **************************************************************************
void CAddItemDlg::AddLeaves (LPENUMSTRING pIEnumString)
{
ULONG celt = 1;
LPOLESTR rgelt;
ULONG celtFetched = 0;
int nIndex = 0;
#ifndef _UNICODE
TCHAR szBuffer [MAX_PATH];
#endif
// Delete any leaves that are presently being displayed:
m_pLeafList->DeleteAllItems ();
// Start at the beginning of the list:
pIEnumString->Reset ();
pIEnumString->Next (celt, &rgelt, &celtFetched);
// Add each leaf to the leaf control:
while (celtFetched > 0)
{
// Insert the leaf:
#ifdef _UNICODE
m_pLeafList->InsertItem (nIndex++, rgelt, ILI_LEAF);
#else
_wcstombsz (szBuffer, rgelt, sizeof (szBuffer) / sizeof (TCHAR));
m_pLeafList->InsertItem (nIndex++, szBuffer, ILI_LEAF);
#endif
// Free the branch name:
CoTaskMemFree (rgelt);
// Re-initialize and get the next item:
celt = 1;
celtFetched = 0;
pIEnumString->Next (celt, &rgelt, &celtFetched);
}
// Select first leaf by default:
if (m_pLeafList->GetItemCount ())
m_pLeafList->SetItemState (0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
}
void CAddItemDlg::BrowseToRoot ()
{
try
{
// Try to browse to root in one step using "OPC_BROWSE_TO" (this
// is not supported and will fail for OPC 1.0 servers):
HRESULT hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_TO, L"");
// If that fails, browse to root one level at a time using "OPC_BROWSE_UP".
// (Browse up will fail when we are at root.)
if (FAILED (hr))
{
do
{
hr = m_pIBrowse->ChangeBrowsePosition (OPC_BROWSE_UP, L"\0");
} while (SUCCEEDED (hr));
}
}
catch (...)
{
}
}
void CAddItemDlg::OnTvnItemexpandingBranchlist(NMHDR *pNMHDR, LRESULT *pResult)
{
// Cast notification header to tree view notification header:
NM_TREEVIEW* pnmtv = (NM_TREEVIEW*) pNMHDR;
// Expand branch:
if (pnmtv->action & TVE_EXPAND)
{
ExpandBranch (pnmtv->itemNew.hItem);
}
// else delete child branches on collapse:
else if (pnmtv->action & TVE_COLLAPSE)
{
DeleteChildBranches (pnmtv->itemNew.hItem);
}
*pResult = 0;
}
void CAddItemDlg::OnTvnSelchangedBranchlist(NMHDR *pNMHDR, LRESULT *pResult)
{
NM_TREEVIEW* pnmtv = (NM_TREEVIEW*)pNMHDR;
// Select the branch:
SelectBranch (pnmtv->itemNew.hItem);
*pResult = 0;
}
void CAddItemDlg::OnCancel()
{
for (int nIndex = 0; nIndex < m_nListIndex; nIndex++)
{
// Get next element in list:
CHotOpcItem *pItem = (CHotOpcItem *) m_cItemList.GetAt (nIndex);
ASSERT (pItem != NULL);
// Delete it:
delete pItem;
}
CDialog::OnCancel();
}
void CAddItemDlg::OnCbnSelchangeDatatype()
{
UpdateData (true);
// We will consider ourselves modified if there is an item ID defined:
m_bModified = !m_strItemID.IsEmpty ();
// Update control status:
UpdateStatus ();
}
void CAddItemDlg::OnEnChangeAccesspath()
{
// TODO: 如果该控件是 RICHEDIT 控件,则它将不会
// 发送该通知,除非重写 CDialog::OnInitDialog()
// 函数并调用 CRichEditCtrl().SetEventMask(),
// 同时将 ENM_CHANGE 标志“或”运算到掩码中。
UpdateData (true);
// We will consider ourselves modified if there is an item ID defined:
m_bModified = !m_strItemID.IsEmpty ();
// Update control status:
UpdateStatus ();
}
void CAddItemDlg::OnEnChangeItemid()
{
UpdateData (true);
// We will consider ourselves modified if there is an item ID defined:
m_bModified = !m_strItemID.IsEmpty ();
// Update control status:
UpdateStatus ();
}
void CAddItemDlg::OnOK()
{
if (OnApplyChange ())
{
// Terminate the list. Will allow us to process list using
// "while (element)" loop if we want:
if (m_nListIndex > 0)
m_cItemList.SetAtGrow (m_nListIndex, NULL);
CDialog::OnOK();
}
}
void CAddItemDlg::OnShowWindow(BOOL bShow, UINT nStatus)
{
CDialog::OnShowWindow(bShow, nStatus);
// If showing dialog, set focus to item ID edit box:
if (bShow)
GetDlgItem (IDC_ITEMID)->SetFocus ();
}
void CAddItemDlg::OnNMDblclkLeaflist(NMHDR *pNMHDR, LRESULT *pResult)
{
int nSelItem;
// Get selected item index:
nSelItem = m_pLeafList->GetNextItem (-1, LVNI_ALL | LVNI_SELECTED);
// If index looks good, get properties of selected item and
// update all other controls accordingly:
if (nSelItem >= 0)
{
HRESULT hr;
WCHAR szItemID [MAX_PATH];
LPWSTR lpszQualifiedID;
// COM requires all strings to be in UNICODE format. Convert
// item ID if needed, then copy to allocated buffer:
#ifdef _UNICODE
lstrcpyn (szItemID, m_pLeafList->GetItemText (nSelItem, 0), sizeof (szItemID) / sizeof (WCHAR));
#else
_mbstowcsz (szItemID, m_pLeafList->GetItemText (nSelItem, 0), sizeof (szItemID) / sizeof (WCHAR));
#endif
try
{
// If we are using a flat browse space, then we need to browse to
// root first:
if (m_bBrowseFlat)
BrowseToRoot ();
// User browser to get item's fully qualified ID:
hr = m_pIBrowse->GetItemID (szItemID, &lpszQualifiedID);
// If we succeeded, update controls:
if (SUCCEEDED (hr) && lpszQualifiedID)
{
// Update selector to end of the list:
m_nSelIndex = m_nListIndex;
// Re-initialize for new item:
m_strItemID = lpszQualifiedID;
m_strAccessPath.Empty ();
UpdateData (false);
// Apply new dataset:
m_bModified = true;
OnApplyChange ();
// Update control state:
UpdateStatus ();
// Free server allocation for qualified item id:
CoTaskMemFree (lpszQualifiedID);
}
// If we didn't get qualified ID, issue a trace statement for debugging:
else
{
TRACE (_T("OTC: Unable to get the qualified item id for %s\r\n"),
m_pLeafList->GetItemText (nSelItem, 0));
}
}
// Catch exceptions:
catch (...)
{
m_pIBrowse = NULL;
UpdateStatus ();
}
}
*pResult = 0;
}
bool CAddItemDlg::OnApplyChange ()
{
// No need to apply changes unless we are modified:
if (m_bModified)
{
CHotOpcItem *pItem = NULL;
// Make sure member variables have values currently displayed in
// associated controls:
UpdateData (true);
// If auto validating, validate item before applying changes.
if (m_bAutoValidate)
{
// Validate currently selected item:
HRESULT hr = Validate ();
// If item is invalid, then inform user of problem and give him
// option of continuing:
if (FAILED (hr))
{
// Define a string format for the validation return code:
CString strHR;
strHR.Format (_T("0x%08X"), hr);
// Define a string format for the validation return code:
CString strFailure;
strFailure.FormatMessage ("IDS_VALIDATE_ITEM_FAILED_CONTINUE %s %s", m_strItemID, strHR);
// Show message bow with error string. Give user of option of
// continuing. If user clicks "NO", return now without applying
// changes:
if (AfxMessageBox (strFailure, MB_YESNO | MB_ICONQUESTION) == IDNO)
return (false);
}
}
// If we make it here, it is OK to apply changes.
// If selected index is the same as list control index, then we
// need to create a new item and add to the list:
if (m_nSelIndex == m_nListIndex)
{
try
{
// Create a new item object and add it to the list:
pItem = new CHotOpcItem (m_pGroup);
m_cItemList.SetAtGrow (m_nListIndex++, pItem);
}
catch (...)
{
ASSERT (FALSE);
}
}
// else we are modifying an item already in list:
else
{
pItem = (CHotOpcItem *) m_cItemList.GetAt (m_nSelIndex);
ASSERT (pItem != NULL);
}
// Set item properties:
pItem->SetAccessPath (m_strAccessPath);
pItem->SetActive (TRUE);
pItem->SetDataType (m_vtDataType);
pItem->SetItemID (m_strItemID);
// Set modified flag:
m_bModified = false;
}
// Return true to indicate success:
return (true);
}
HRESULT CAddItemDlg::Validate (bool *pbDotBitAddress /*= NULL */)
{
// Assume invalid until proven otherwisw:
HRESULT hr = E_FAIL;
// Make sure data in member variables represents current control settings:
UpdateData (true);
// Allocate memory for our item definition:
OPCITEMDEF *pItems = (OPCITEMDEF *) CoTaskMemAlloc (sizeof (OPCITEMDEF));
// If allocation succeeds, perform validation:
if (pItems)
{
// Initialize some variables:
HRESULT *pErrors = NULL;
OPCITEMRESULT *pResults = NULL;
WCHAR *pszItemID = NULL;
WCHAR *pszAccessPath = NULL;
// Check to make sure we have an item ID (debug only):
ASSERT (!m_strItemID.IsEmpty ());
// Allocate memory for item ID string:
pszItemID = (WCHAR *) CoTaskMemAlloc ((m_strItemID.GetLength () + 1) * sizeof (WCHAR));
// COM requires us to put strings in UNICODE format.
#ifdef _UNICODE
// This is a UNICODE build so just copy item ID string "as is"
// to allocated memory:
lstrcpyn (pszItemID, m_strItemID, m_strItemID.GetLength () + 1);
#else
// This is an ANSI build so convert item ID string to UNICODE and
// place result in allocated memory:
_mbstowcsz (pszItemID, m_strItemID, m_strItemID.GetLength () + 1);
#endif
// If an access path is specified, then convert its string to UNICODE
// and copy it to allocated memory:
if (!m_strAccessPath.IsEmpty ())
{
// First allocate memory for the access path string:
pszAccessPath = (WCHAR *) CoTaskMemAlloc ((m_strAccessPath.GetLength () + 1) * sizeof (WCHAR));
// Convert if necessary and copy result to allocated memory:
#ifdef _UNICODE
lstrcpyn (pszAccessPath, m_strAccessPath, m_strAccessPath.GetLength () + 1);
#else
_mbstowcsz (pszAccessPath, m_strAccessPath, m_strAccessPath.GetLength () + 1);
#endif
}
// Fill in item def structure:
pItems->bActive = true;
pItems->dwBlobSize = 0;
pItems->hClient = NULL;
pItems->pBlob = NULL;
pItems->szAccessPath = pszAccessPath;
pItems->szItemID = pszItemID;
pItems->vtRequestedDataType = m_vtDataType;
// Validate the single item, (no blob requested):
try
{
// Request server to validate item described initem def structure
// through the group's item management interface:
hr = m_pIItemMgt->ValidateItems (1, pItems, FALSE, &pResults, &pErrors);
// If server reports that item is valid, we may still have to modify
// the data type. We will also validate "dot bit address" format.
if (hr == S_OK)
{
// Modify the data type to what the server will use:
m_vtDataType = pResults->vtCanonicalDataType;
// Update the dialog controls to reflect data type change:
UpdateData (false);
// Flag as modified:
m_bModified = true;
// Verify dot bit address setting:
if (pbDotBitAddress)
{
// Assume false until proven otherwise:
*pbDotBitAddress = false;
// Data type must be VT_BOOL for dot bit address, It it's not,
// fall through with flag set to false:
if (m_vtDataType == VT_BOOL)
{
// To determine if truly a dot bit address, strip off
// bit number and see if its still valid.
// First free previous results,
if (pResults)
{
CoTaskMemFree (pResults);
pResults = NULL;
}
// and previous errors:
if (pErrors)
{
CoTaskMemFree (pErrors);
pErrors = NULL;
}
// Get position of "dot" character:
WCHAR *pwch = wcsrchr (pItems->szItemID, L'.');
if (pwch)
{
// Replace "dot" with NULL terminator, effectively stripping
// off the whole "dot bit number" portion of the address:
*pwch = L'\0';
// Reset the requested data type to VT_EMPTY. We are only
// interested in seeing if the address is valid for some
// unspecified data type.
pItems->vtRequestedDataType = VT_EMPTY;
// Request server to validate the "stripped" address:
if (SUCCEEDED (m_pIItemMgt->ValidateItems (1, pItems, FALSE, &pResults, &pErrors)))
{
// If the server reports that the "stipped" address is valid, and
// that it corresponds to some data type other than VT_BOOL,
// then the original address is truely a "dot bit" address:
if (pResults->vtCanonicalDataType != VT_EMPTY && pResults->vtCanonicalDataType != VT_BOOL)
*pbDotBitAddress = true;
}
}
}
}
}
// else if server reports that item is not valid. Assign item
// error for return:
else
{
hr = pErrors [0];
}
}
// Catch exceptions:
catch (...)
{
// Probably something wrong with interface pointer, so invalidate it:
m_pIItemMgt = NULL;
// Update dialog control status:
UpdateStatus ();
}
// Free allocations (COM requires us to do this):
CoTaskMemFree (pItems);
if (pszItemID)
CoTaskMemFree (pszItemID);
if (pszAccessPath)
CoTaskMemFree (pszAccessPath);
if (pResults)
CoTaskMemFree (pResults);
if (pErrors)
CoTaskMemFree (pErrors);
}
// Return validation return code:
return (hr);
}