/*****************************************************************************
* Copyright (c) 1990-99 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others, 
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/


// TreeDialog.cpp : implementation file
//

#include "stdafx.h"
#include "DigiInf.h"
#include "TreeDialog.h"
#include "ObjectFactory.h"

#ifdef NT5
#pragma message ("BUILDING FOR NT5")
#include "DriverServices.h"
#endif

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

#if defined NT5
   #define  pAdapter (m_pAdapter)
#else // #if defined NT5
   #define  pAdapter (m_pApp->m_pAdapter)
#endif // #if defined NT5

/////////////////////////////////////////////////////////////////////////////
// CTreeDialog dialog


#ifdef NT5
#define BASECLASS CPropertyPage
IMPLEMENT_DYNCREATE(CTreeDialog, CPropertyPage)
#else
#define BASECLASS CDialog
#endif

#ifdef NT5
CTreeDialog::CTreeDialog() : CPropertyPage(CTreeDialog::IDD)
#else
CTreeDialog::CTreeDialog(CWnd* pParent /*=NULL*/) : CDialog(CTreeDialog::IDD, pParent)
#endif
{
	//{{AFX_DATA_INIT(CTreeDialog)
	m_strInformation = _T("");
	//}}AFX_DATA_INIT

   m_pApp = NULL;
   m_fConfigIsDirty = FALSE;
   m_fConfigHasChanged = FALSE;
   m_fDeletingTree = FALSE;
   m_pSelectedObject = NULL;
   m_hSelectedItem = NULL;

#if defined NT5
   m_pAdapter = NULL;
   m_strAdapterType = "";
   m_strAdapterRegPath = "";
   m_pDevInfoData = NULL;
   m_hDevInfoSet = NULL;
#endif // #if defined NT5
}

CTreeDialog::~CTreeDialog()
{
#if defined NT5
   if (NULL != pAdapter)
   {
      DeleteConfigObject((CConfigObject **)(&(pAdapter)));
   }
#endif // #if defined NT5
}

void CTreeDialog::DoDataExchange(CDataExchange* pDX)
{
	BASECLASS::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CTreeDialog)
#ifndef NT5
	DDX_Control(pDX, ID_APPLY_NOW, m_btnApplyNow);
#endif
	DDX_Control(pDX, IDC_WizardButton, m_btnWizard);
	DDX_Control(pDX, IDC_RemoveButton, m_btnRemove);
	DDX_Control(pDX, IDC_PropertiesButton, m_btnProperties);
	DDX_Control(pDX, IDC_AddButton, m_btnAdd);
	DDX_Control(pDX, IDC_Tree, m_ctrlTree);
	DDX_Text(pDX, IDC_Information, m_strInformation);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CTreeDialog, BASECLASS)
	//{{AFX_MSG_MAP(CTreeDialog)
	ON_NOTIFY(TVN_SELCHANGED, IDC_Tree, OnSelchangedTree)
	ON_NOTIFY(NM_DBLCLK, IDC_Tree, OnDoubleClickTree)
	ON_NOTIFY(NM_RCLICK, IDC_Tree, OnRclickTree)
	ON_COMMAND(ID_POPUP_ADD, OnPopupAdd)
	ON_COMMAND(ID_POPUP_REMOVE, OnPopupRemove)
	ON_COMMAND(ID_POPUP_PROPERTIES, OnPopupProperties)
	ON_BN_CLICKED(IDC_AddButton, OnAddButton)
	ON_BN_CLICKED(IDC_RemoveButton, OnRemoveButton)
	ON_BN_CLICKED(IDC_PropertiesButton, OnPropertiesButton)
	ON_BN_CLICKED(IDC_WizardButton, OnWizardButton)
	ON_WM_HELPINFO()
	ON_BN_CLICKED(ID_APPLY_NOW, OnApplyNow)
	ON_WM_DESTROY()
	//}}AFX_MSG_MAP
#if defined NT5
   ON_NOTIFY(PSN_HELP, 0, OnNotifyHelp)
#endif // #if defined NT5
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CTreeDialog message handlers

BOOL CTreeDialog::OnInitDialog() 
{
   try
   {
	   BASECLASS::OnInitDialog();

      AffirmThrow(m_pApp != NULL);

#if defined NT5
	   TRACE("CTreeDialog::OnInitDialog: initializing local adapter\n");
      {
         HKEY hParamsKey;
         AffirmThrow(OpenAdapterParamsKey(m_hDevInfoSet, m_pDevInfoData, &hParamsKey));
         AffirmThrow(RegReadStrValue(hParamsKey,DXB_REGVAL_OPTION,m_strAdapterType));
         TRACE("    adapter = %s\n",m_strAdapterType);
         AffirmThrow(RegReadStrValue(hParamsKey,DXB_REGVAL_REGPATH,m_strAdapterRegPath));
         TRACE("    regpath = %s\n",m_strAdapterRegPath);
         RegCloseKey(hParamsKey);
      }

      TRACE("CTreeDialog::OnInitDialog: creating the adapter\n");
      AffirmThrow(NewConfigObject((CConfigObject **)(&pAdapter), CConfigObject::ADAPTER, m_strAdapterType, HKEY_LOCAL_MACHINE, m_strAdapterRegPath, NULL));
      AffirmThrow(NULL != pAdapter);

#endif // #if defined NT5

      BOOL fHasWizard = pAdapter->AdapterHasWizard();

      SetWindowText(pAdapter->GetDisplayName());

      // If we don't have a wizard, hide the wiz button...
      if (!(pAdapter->AdapterHasWizard()))
      {
         m_btnWizard.EnableWindow(FALSE);
         m_btnWizard.ShowWindow(SW_HIDE);
      }

      m_ilTreeIcons.Create(IDB_TreeIcons, 16, 0, RGB(255, 255, 255)); 
      m_ctrlTree.SetImageList(&m_ilTreeIcons, TVSIL_NORMAL);
      RefreshTree();	
   }
   catch (CAffirmException ae)
   {
   }
	
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

INT_PTR CTreeDialog::DoModal() 
{
   AffirmThrow(m_pApp != NULL);                 // can't do anything without one of these
   AffirmThrow(pAdapter != NULL);     // or one of these
	return BASECLASS::DoModal();
}

int CTreeDialog::GetItemLevel(HTREEITEM const hTreeItem)
{
   int nLevel = 0;

   try
   {
      AffirmThrow(hTreeItem != NULL);
      HTREEITEM hUp = hTreeItem;
      while ((hUp = m_ctrlTree.GetParentItem(hUp)) != NULL) nLevel++;
   }
   catch (CAffirmException ae)
   {
   }

   return nLevel;
}

void CTreeDialog::PopulateTree(HTREEITEM const hParent, CTreeNode const & Node)
{
   try
   {
      AffirmThrow(hParent != NULL);

      HTREEITEM hChild;

      // if the object is of type ROUTER, display it at the Root level
      CConfigObject* pObject = Node.GetConfigObject();
      if (pObject->GetType() == CConfigObject::ROUTER)
         hChild = m_ctrlTree.InsertItem(Node.GetDisplayName(), TVI_ROOT);
      else
         hChild = m_ctrlTree.InsertItem(Node.GetDisplayName(), hParent);

      AffirmThrow(hChild != NULL);

      m_ctrlTree.SetItemData(hChild, (DWORD_PTR)(void *)Node.GetConfigObject());

      int nLevel = GetItemLevel(hChild);
      if (nLevel < m_ilTreeIcons.GetImageCount()) m_ctrlTree.SetItemImage(hChild, nLevel, nLevel);

      // TRACE("CTreeDialog::PopulateTree() %s Level:%d\n", Node.GetDisplayName(), nLevel);

      for (int i = 0; i < Node.GetBranchCount(); i++)
      {
         PopulateTree(hChild, Node.GetBranch(i));
      }

      // find out if the current object belongs to the item that was previously
      // the currently selected item. If so, make it the 
      if (pObject == m_pSelectedObject)
         m_hSelectedItem = hChild;

      // search in our array of visible objects for the object associated with the 
      // current item. If a match is found, we make that item visible
      BOOL fObjFound = FALSE;
      INT_PTR nSize = m_aVisibleItems.GetSize();
      for (i = 0; i < nSize && !fObjFound; i++)
      {
         if (pObject == (CConfigObject*)m_aVisibleItems.GetAt(i))
         {
            m_ctrlTree.EnsureVisible(hChild);
            fObjFound = TRUE;
         }
      }

      // if no match made then display at line level or the very first level if no line
      if ( (!fObjFound) && ( (pObject->GetType() == CConfigObject::LINE) || (1 == nLevel) ) )
         {
            m_ctrlTree.EnsureVisible(hChild);
         }
   }
   catch (CAffirmException ae)
   {
   }
}

void CTreeDialog::RefreshTree(void)
{
   try
   {
      AffirmThrow(m_pApp != NULL);
      AffirmThrow(pAdapter != NULL);

      CTreeNode Node;
      AffirmThrow(pAdapter->BuildDisplayTree(&Node));

      // if the tree is non empty, build an array holding all visible items
      // Visible is really a misnomer. Its really all the last visisble items
      // The array holds copies of CConfigObject* that are associated with 
      // the tree items.
      m_pSelectedObject = NULL;
      if (m_ctrlTree.GetCount() > 0)
      {
         m_aVisibleItems.RemoveAll();
         HTREEITEM hItem = m_ctrlTree.GetFirstVisibleItem();
         BuildVisibleItemArray(hItem);
         // also get currently selected item and its object
         m_hSelectedItem = m_ctrlTree.GetSelectedItem();
         m_pSelectedObject = (CConfigObject*)m_ctrlTree.GetItemData(m_hSelectedItem);
      }

      // null this out. Now its used as a marker. If after calling populate tree
      // its still NULL, then we select the first visible item
      m_hSelectedItem = NULL;
      m_fDeletingTree = TRUE;
      m_ctrlTree.DeleteAllItems();
      m_fDeletingTree = FALSE;

      PopulateTree(TVI_ROOT, Node);

      if (m_hSelectedItem == NULL)
         m_hSelectedItem = m_ctrlTree.GetFirstVisibleItem();

      m_ctrlTree.SelectItem(m_hSelectedItem);
   }
   catch (CAffirmException ae)
   {
   }
}

CConfigObject * CTreeDialog::GetConfigObject(HTREEITEM const hTreeItem)
{
   CConfigObject * pObj = NULL;

   try
   {
      AffirmThrow(hTreeItem != NULL);
      pObj = (CConfigObject *)(void *)m_ctrlTree.GetItemData(hTreeItem);
      AffirmThrow(pObj != NULL);
   }
   catch (CAffirmException ae)
   {
   }

   return pObj;
}

HTREEITEM CTreeDialog::RecursivelyFindHTreeItem(HTREEITEM const hStartHere, CConfigObject const * const pObjToFind)
{
   HTREEITEM hItem = hStartHere;

   try
   {
      AffirmThrow(pObjToFind != NULL);

      if (hItem != NULL)
      {
         CConfigObject * pObjInTree = GetConfigObject(hItem);

         if (pObjInTree != pObjToFind)
         {
            hItem = RecursivelyFindHTreeItem(m_ctrlTree.GetNextSiblingItem(hStartHere), pObjToFind);
            if (hItem == NULL) hItem = RecursivelyFindHTreeItem(m_ctrlTree.GetChildItem(hStartHere), pObjToFind);
         }
      }
   }
   catch (CAffirmException ae)
   {
   }

   return hItem;
}

HTREEITEM CTreeDialog::GetHTreeItem(CConfigObject const * const pObjToFind)
{
   HTREEITEM hItem = NULL;

   try
   {
      AffirmThrow(pObjToFind != NULL);
      hItem = RecursivelyFindHTreeItem(m_ctrlTree.GetRootItem(), pObjToFind);
   }
   catch (CAffirmException ae)
   {
   }

   return hItem;
}

BOOL CTreeDialog::CanDoAdd(HTREEITEM const hSelected, CConfigObject const * const pSelectedObj)
{
   BOOL fCanDo = FALSE;

   try
   {
      AffirmThrow(hSelected != NULL);
      AffirmThrow(pSelectedObj != NULL);

      fCanDo = !!(pSelectedObj->GetAbility() & CConfigObject::ADDCHILD);
   }
   catch (CAffirmException ae)
   {
   }

   return fCanDo;
}

BOOL CTreeDialog::CanDoRemove(HTREEITEM const hSelected, CConfigObject const * const pSelectedObj)
{
   BOOL fCanDo = FALSE;

   try
   {
      AffirmThrow(hSelected != NULL);
      AffirmThrow(pSelectedObj != NULL);

      HTREEITEM hParent = m_ctrlTree.GetParentItem(hSelected);
      if (hParent != NULL)
      {
         CConfigObject * pParent = GetConfigObject(hParent);
         AffirmThrow(pParent != NULL);
         fCanDo = !!(pParent->GetAbility() & CConfigObject::DELETECHILD);
      }
   }
   catch (CAffirmException ae)
   {
   }

   return fCanDo;
}

BOOL CTreeDialog::CanDoProperties(HTREEITEM const hSelected, CConfigObject const * const pSelectedObj)
{
   BOOL fCanDo = FALSE;

   try
   {
      AffirmThrow(hSelected != NULL);
      AffirmThrow(pSelectedObj != NULL);

      fCanDo = !!(pSelectedObj->GetAbility() & CConfigObject::DOPROPERTIES);
   }
   catch (CAffirmException ae)
   {
   }

   return fCanDo;
}

void CTreeDialog::UpdateButtonStates(HTREEITEM const hSelected, CConfigObject const * const pSelectedObj)
{
   try
   {
      AffirmThrow(pSelectedObj != NULL);

      m_btnAdd.EnableWindow(CanDoAdd(hSelected, pSelectedObj));
      m_btnRemove.EnableWindow(CanDoRemove(hSelected, pSelectedObj));
      m_btnProperties.EnableWindow(CanDoProperties(hSelected, pSelectedObj));
   }
   catch (CAffirmException ae)
   {
   }
}

void CTreeDialog::OnSelchangedTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
   try
   {
      if (!m_fDeletingTree)
      {
         NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
         AffirmThrow(pNMTreeView != NULL);

         HTREEITEM hSelected = pNMTreeView->itemNew.hItem;
         AffirmThrow(hSelected != NULL);

         CConfigObject * pSelectedObj = GetConfigObject(hSelected);
         AffirmThrow(pSelectedObj != NULL);

         UpdateButtonStates(hSelected, pSelectedObj);

         m_strInformation = pSelectedObj->GetInformationText();
         UpdateData(FALSE);
      }
   }
   catch (CAffirmException ae)
   {
   }
	
	*pResult = 0;
}

void CTreeDialog::OnRclickTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
   try
   {
      CPoint ptCursor;
      GetCursorPos(&ptCursor);

      // 1st find the tree item that the cursor is on
      // which may NOT be the currently selected item
      CPoint ptHitTest = ptCursor;
      m_ctrlTree.ScreenToClient(&ptHitTest);

      UINT fHitTest = 0;
      HTREEITEM hRightClicked = m_ctrlTree.HitTest(ptHitTest, &fHitTest);

      if (fHitTest & TVHT_ONITEM)
      {
         AffirmThrow(hRightClicked != NULL);

         // Now it appears as though we're on a tree item

         // Select this item
         m_ctrlTree.SelectItem(hRightClicked);

         // Now pop up the menu
         CConfigObject * pRightClickedObj = GetConfigObject(hRightClicked);
         AffirmThrow(pRightClickedObj != NULL);

         CMenu Menu;
         AffirmThrow(Menu.LoadMenu(IDR_PopupMenu));

         CMenu * pPopup = Menu.GetSubMenu(0);
         AffirmThrow(pPopup != NULL);

         pPopup->EnableMenuItem(ID_POPUP_ADD, (CanDoAdd(hRightClicked, pRightClickedObj) ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);
         pPopup->EnableMenuItem(ID_POPUP_REMOVE, (CanDoRemove(hRightClicked, pRightClickedObj) ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);
         pPopup->EnableMenuItem(ID_POPUP_PROPERTIES, (CanDoProperties(hRightClicked, pRightClickedObj) ? MF_ENABLED : MF_GRAYED) | MF_BYCOMMAND);

         pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, ptCursor.x, ptCursor.y, this);
      }
   }
   catch (CAffirmException ae)
   {
   }

   *pResult = 0;
}

void CTreeDialog::OnPopupAdd() 
{
   OnAddButton();
}

void CTreeDialog::OnPopupRemove() 
{
   OnRemoveButton();
}

void CTreeDialog::OnPopupProperties() 
{
   OnPropertiesButton();
}

void CTreeDialog::OnAddButton() 
{
	try
	{
		HTREEITEM hSelected = m_ctrlTree.GetSelectedItem();
		AffirmThrow(hSelected != NULL);

		CConfigObject * pSelectedObj = GetConfigObject(hSelected);
		AffirmThrow(pSelectedObj != NULL);
		
		TRACE("Adding child\n");
		if (pSelectedObj->AddChild())
		{
			m_fConfigIsDirty = TRUE;
#ifndef NT5
			m_btnApplyNow.EnableWindow(TRUE);
#endif
			RefreshTree();
		}
	}
	catch (CAffirmException ae)
	{
	}
}

void CTreeDialog::OnRemoveButton() 
{
	try
	{
		HTREEITEM hSelected = m_ctrlTree.GetSelectedItem();
		AffirmThrow(hSelected != NULL);
		CConfigObject* pSelectedObj = GetConfigObject(hSelected);
		AffirmThrow(pSelectedObj != NULL);

		HTREEITEM hParent = m_ctrlTree.GetParentItem(hSelected);
		AffirmThrow(hParent != NULL);
		CConfigObject* pParentObj = GetConfigObject(hParent);
		AffirmThrow(hParent != NULL);

		TRACE("Deleting object\n");
		if (pParentObj->DeleteChild(pSelectedObj))
		{
			m_fConfigIsDirty = TRUE;
#ifndef NT5
			m_btnApplyNow.EnableWindow(TRUE);
#endif
			RefreshTree();
		}
	}
	catch( CAffirmException ae)
	{
	}
}

void CTreeDialog::OnPropertiesButton() 
{
   try
   {
      HTREEITEM hSelected = m_ctrlTree.GetSelectedItem();
      AffirmThrow(hSelected != NULL);

      CConfigObject * pSelectedObj = GetConfigObject(hSelected);
      AffirmThrow(pSelectedObj != NULL);

      TRACE("Launching properties\n");
      if (pSelectedObj->DoProperties())
      {
         TRACE("Something changed\n");
         m_fConfigIsDirty = TRUE;
#ifndef NT5
         m_btnApplyNow.EnableWindow(TRUE);
#endif
         RefreshTree();
      }
   }
   catch (CAffirmException ae)
   {
   }
}

void CTreeDialog::OnDoubleClickTree(NMHDR* pNMHDR, LRESULT* pResult)
{
    OnPropertiesButton();

   *pResult = 0;
}

void CTreeDialog::OnWizardButton() 
{
   try
   {
      TRACE("Launching wizard\n");

      AffirmThrow(m_pApp != NULL);
      AffirmThrow(pAdapter != NULL);

      BOOL fRunWizard = TRUE;

      if (m_fConfigIsDirty)
      {
         CString strMsg;
         AffirmThrow(strMsg.LoadString(IDS_SaveBeforeWizard));
         if (MessageBox(strMsg, pAdapter->GetDisplayName(), MB_YESNO | MB_ICONQUESTION) == IDYES)
         {
            OnApplyNow();
         }
         else
         {
            fRunWizard = FALSE;
         }
      }

      if (fRunWizard)
      {
#if defined NT5
         CConfigWizSheet ws(TEXT(""), this);
         AffirmThrow(pAdapter->AddWizardPages(&ws));
         switch(ws.DoModal())
         {
            case ID_WIZFINISH:
               TRACE("The wizard finished properly\n");
      		   m_fConfigIsDirty = TRUE;
               break;

            case IDCANCEL:
               TRACE("CTreeDialog::OnWizardButton: The user canceled out of the wizard! Refreshing the adapter\n");
               DeleteConfigObject((CConfigObject **)(&(pAdapter)));
               AffirmThrow(NewConfigObject((CConfigObject **)(&pAdapter), CConfigObject::ADAPTER, m_strAdapterType, HKEY_LOCAL_MACHINE, m_strAdapterRegPath, NULL));
               AffirmThrow(NULL != pAdapter);
               break;

            default: TRACE("Install wizard failed!\n"); break;
         }
#else // #if defined NT5
         if (m_pApp->RunInstallWizard(pAdapter, FALSE) == IDOK)
         {
            m_fConfigIsDirty = TRUE;
            m_btnApplyNow.EnableWindow(TRUE);
         }
         else
         {
            // The user canceled from the wizard, but our objects in memory have been
            // modified.  Since the registry represents what our objects were just before
            // running the wizard, all we have to do refresh.
            m_pApp->RefreshAdapter();
         }
#endif // #if defined NT5

         RefreshTree();
      }
   }
   catch (CAffirmException ae)
   {
   }
}

#ifdef NT5
// Taken from DigiRas.hm
#define HIDD_TreeDialog 0x2044D
void CTreeDialog::OnNotifyHelp(NMHDR* pNMHDR, LRESULT* pResult)
{
   TRACE("CTreeDialog::OnNotifyHelp() called\n");
   // NOTE: NOTE: We only ever respond to this with the Digi Config Help
   // Hardcoded since the framework passes us nothing in the parameters
   HWND hwndHtmlHelp = HtmlHelp(NULL, DIGIHELPFILE, HH_HELP_CONTEXT, HIDD_TreeDialog);
}
#endif // #if defined NT5

BOOL CTreeDialog::OnHelpInfo(HELPINFO* pHelpInfo) 
{
   TRACE("CTreeDialog::OnHelpInfo() called: pHelpInfo->dwContextId:0x%x\n", pHelpInfo->dwContextId);
#ifdef NT5
   // NOTE: NOTE: We only ever respond to this with the Digi Config Help
   // Hardcoded since the framework passes us nothing in the parameters
   HWND hwndHtmlHelp = HtmlHelp(NULL, DIGIHELPFILE, HH_HELP_CONTEXT, HIDD_TreeDialog);
   return TRUE;
#else // #if defined NT5
   // if (pHelpInfo->iContextType == HELPINFO_WINDOW) AfxGetApp()->WinHelp(pHelpInfo->dwContextId, HELP_CONTEXTPOPUP);
	return BASECLASS::OnHelpInfo(pHelpInfo);
#endif // #if defined NT5
}

void CTreeDialog::OnApplyNow() 
{
   try
   {
	  TRACE("Apply Now\n");
      AffirmThrow(m_fConfigIsDirty);
      AffirmThrow(m_pApp != NULL);
      AffirmThrow(pAdapter != NULL);

      Affirm(pAdapter->SetParameters());

      m_fConfigIsDirty = FALSE;
      m_fConfigHasChanged = TRUE;

#ifndef NT5
      m_btnApplyNow.EnableWindow(FALSE);
#endif
   }
   catch (CAffirmException ae)
   {
   }
}

// Ok, the following may look a little weird, but here's how it works.
// If we have saved a configuration at any time while we were running,
// m_fConfigHasChanged will be TRUE and we want to return IDOK back to
// the caller so that NT4 NCPA will ask for a reboot.  If nothing
// has changed (i.e. m_fConfigHasChanged == FALSE), we want to return
// IDCANCEL so that in NT4 NCPA doesn't request a reboot.  I've added
// that logic in the OnOK() handler, and simply have the OnCancel()
// handler call it.  In OnCancel() I set m_fConfigIsDirty to FALSE
// so that if the config in memory has been modified, it isn't saved.

void CTreeDialog::OnOK() 
{
   try
   {
	  TRACE("On OK\n");
      if (m_fConfigIsDirty) OnApplyNow();

      if (m_fConfigHasChanged) 
      {
#ifdef NT5
         // Check for any configuration messages the adapter may have
         // This gives us a chance to inform the user of an unusual circumstance
         //   which may require their attention
         if (pAdapter->AdapterHasConfigMessage())
         {
            CString strMsg = pAdapter->GetConfigMessage();
            AfxMessageBox(strMsg, MB_OK|MB_ICONINFORMATION);
         }

         // for now: if do use the cached value to restart DXB adapter
         // Restart removed from Adapter::SetParameters();
         AffirmThrow(m_pDevInfoData != NULL);
         AffirmThrow(m_hDevInfoSet != NULL);
         TRACE("Restart Driver\n");
         RestartDriver(m_hDevInfoSet, m_pDevInfoData);
#endif
         BASECLASS::OnOK();
      }
      else
      {
         BASECLASS::OnCancel();     // return IDCANCEL
      }

   }
   catch (CAffirmException ae)
   {
   }
}

void CTreeDialog::OnCancel() 
{
   // if (m_fConfigIsDirty) MessageBox();       Maybe we should warn the user here?
   m_fConfigIsDirty = FALSE;                 // Don't let OnOK() call OnApplyNow()
   OnOK();
}

void CTreeDialog::OnDestroy() 
{
   TRACE("CTreeDialog::OnDestroy()\n");
#ifndef NT5
   // NOTE: call our app's WinHelp(), not CWnd's
   AfxGetApp()->WinHelp(0, HELP_QUIT);
#endif

#if defined NT5
   // Delete the Adapter object now...
   if (NULL != pAdapter)
   {
      DeleteConfigObject((CConfigObject **)(&(pAdapter)));
      pAdapter = NULL;
   }
#endif // #if defined NT5

	BASECLASS::OnDestroy();
}

// a recursive routine that walks the tree control looking for items that
// were the last unexpanded ones. i.e. if a->b->c where a and b are expanded items
// then c would be the last expanded item. The Object associated with this item
// is stored in a ptr array.
void CTreeDialog::BuildVisibleItemArray(HTREEITEM hItem)
{
	if (m_ctrlTree.GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED)
	{
		if (m_ctrlTree.ItemHasChildren(hItem))
		{
			HTREEITEM hChildItem = m_ctrlTree.GetChildItem(hItem);
			do
			{
				BuildVisibleItemArray(hChildItem);
				hChildItem = m_ctrlTree.GetNextSiblingItem(hChildItem);
			} while(hChildItem);
		}
	}
	else
	{
		m_aVisibleItems.Add((void *)m_ctrlTree.GetItemData(hItem));
	}
}

#if defined NT5
void CTreeDialog::SetDevInfo(const HDEVINFO hDevInfoSet, const PSP_DEVINFO_DATA pDevInfoData)
{
	ASSERT(hDevInfoSet);
	ASSERT(pDevInfoData);
	m_hDevInfoSet = hDevInfoSet;
	m_pDevInfoData = pDevInfoData;
}
#endif // #if defined NT5
