// DlgObjectBalance.cpp : implementation file
//

#include "stdafx.h"
#include "mw4gameed.h"
#include <stdarg.h>
#include <stuff\notationfile.hpp>
#include <stuff\vector3d.hpp>
#include "DlgObjectBalance.h"


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

const int c_Max_Objects_Supported = 100;
extern CMW4GameEdApp theApp;

//
// User passes in an array of data to the dialog at startup.
// this array type is...CDOPPROPERTIES_TO_EDIT_ARRAY
//

                   

extern LPCSTR MyLoadStringTempBuffer(UINT uiID);
extern void EndDirectoryWithBackSlash(CString & strDir);

/////////////////////////////////////////////////////////////////////////////
// CDlgObjectBalance dialog

//
//
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

//
// Rather than have the calling function have to fill in the structure manually they can call this 
// function with all the propeties they want to edit. Then they just pass this in to the appropriate
// function to specify what properties to get. They will have to clear up the memory with free though
//
CDOPPROPERTIES_TO_EDIT_ARRAY * CDlgObjectBalance::CreatePropertiesToEditArray(int nCount, ...)
{

   va_list marker;
   CDOPPROPERTIES_TO_EDIT_ARRAY * pRet = Allocate_CDOPPROPERTIES_TO_EDIT_ARRAY(nCount);


   va_start( marker, nCount );     /* Initialize variable arguments. */
   pRet->nCount = nCount;
   for (int i = 0 ; i < nCount; i++)
   {
      pRet->ppProperties_To_Edit_Entry[i]->ePropertyType = va_arg(marker, CDOPVARTYPE);
      strcpy(pRet->ppProperties_To_Edit_Entry[i]->szPropertyTag, va_arg(marker, char *));
      strcpy(pRet->ppProperties_To_Edit_Entry[i]->szPropertyTagForDisplay, va_arg(marker, char *));
      pRet->ppProperties_To_Edit_Entry[i]->uiReserved1_CTRLID = 0;
   }
   va_end( marker );              /* Reset variable arguments.      */
   return pRet; 
}
//
// Since I am using a pointer to represent an array the structure has to be allocated with enough size to
// store the array. In addition the internal pointer variable must be directed to within
// the allocation itself. This way I can use array notation To then on.
//
CDOPPROPERTIES_TO_EDIT_ARRAY * CDlgObjectBalance::Allocate_CDOPPROPERTIES_TO_EDIT_ARRAY(int nPropertyCount, CDOPPROPERTIES_TO_EDIT_ARRAY * * ppCDOPPROPERTIES_TO_EDIT_ARRAY)
{
  CDOPPROPERTIES_TO_EDIT_ARRAY * pNew = (CDOPPROPERTIES_TO_EDIT_ARRAY *)
    malloc(sizeof(CDOPPROPERTIES_TO_EDIT_ARRAY) + (nPropertyCount) * sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY *) + 
    (nPropertyCount) * sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY)); // we could - 1 from the count since one is allocated as part of the structure, but
                                                                                                      // I'm electing to have the 4 byte buffer instead of worrying about nPropertyCount == 0

  for (int i = 0; i < nPropertyCount; i++)
  {
    pNew->ppProperties_To_Edit_Entry[i] = (CDOPPROPERTIES_TO_EDIT_ENTRY *)(void *)(((DWORD)&(pNew->ppProperties_To_Edit_Entry)) + 
          sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY *) * (nPropertyCount) + i * sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY));
  }
  if (ppCDOPPROPERTIES_TO_EDIT_ARRAY)
    *ppCDOPPROPERTIES_TO_EDIT_ARRAY = pNew;
  return pNew;
}

//
// Since I am using a pointer to represent an array the structure has to be allocated with enough size to
// store the array. In addition the internal pointer variable must be directed to within
// the allocation itself. This way I can use array notation To then on.
//
CDOPPROPERTIES_OBJECT_PROPERTIES * CDlgObjectBalance::Allocate_CDOPPROPERTIES_OBJECT_PROPERTIES(int nPropertyCount, CDOPPROPERTIES_OBJECT_PROPERTIES * * ppCDOPPROPERTIES_OBJECT_PROPERTIES)
{
   CDOPPROPERTIES_OBJECT_PROPERTIES * pNew = (CDOPPROPERTIES_OBJECT_PROPERTIES *)
     malloc (sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES) + nPropertyCount * sizeof(CDOPPROPERTYVALUE *) +  
          nPropertyCount * sizeof(CDOPPROPERTYVALUE));
   for (int i = 0 ; i < nPropertyCount; i++)
   {
     pNew->ppObjectProperties[i] = (CDOPPROPERTYVALUE * )(void *)(((DWORD)&(pNew->ppObjectProperties)) + 
       sizeof(CDOPPROPERTYVALUE *) * nPropertyCount + sizeof(CDOPPROPERTYVALUE) * i);
   }
   if (ppCDOPPROPERTIES_OBJECT_PROPERTIES)
     *ppCDOPPROPERTIES_OBJECT_PROPERTIES = pNew;
   return pNew;
}

//
// Since I am using a pointer to represent an array the structure has to be allocated with enough size to
// store the array. In addition the internal pointer variable must be directed to within
// the allocation itself. This way I can use array notation To then on.
//
CDOPPROPERTIES_OBJECT_LIST * CDlgObjectBalance::Allocate_CDOPPROPERTIES_OBJECT_LIST(int nObjectCount, int nPropertyCount, CDOPPROPERTIES_OBJECT_LIST ** ppCDOPPROPERTIES_OBJECT_LIST )
{

  // This one is the most complex since it has to allocate enough room for all the objects, to have all the properties and have
  // all the data for those properties

   CDOPPROPERTIES_OBJECT_LIST * pNew = (CDOPPROPERTIES_OBJECT_LIST *)
     malloc(sizeof(CDOPPROPERTIES_OBJECT_LIST) + nObjectCount * sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES *)
        +(nObjectCount * (sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES) + nPropertyCount * sizeof(CDOPPROPERTYVALUE *)))
        +(nObjectCount * (nPropertyCount * sizeof(CDOPPROPERTYVALUE)))
          );             
   for (int i = 0; i < nObjectCount; i++)
   {
     // For the object array set up the pointers to each properties array
     pNew->ppPropertiesArray[i] = (CDOPPROPERTIES_OBJECT_PROPERTIES * )(void *)(((DWORD)&(pNew->ppPropertiesArray))  
           +sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES * ) * nObjectCount 
           + i * (sizeof(CDOPPROPERTIES_OBJECT_PROPERTIES) + nPropertyCount * (sizeof(CDOPPROPERTYVALUE  *))  
                 +nPropertyCount * sizeof(CDOPPROPERTYVALUE)));
     // For each object set up the pointer list for the properties array
     for (int j = 0; j < nPropertyCount; j++)
     {
       pNew->ppPropertiesArray[i]->ppObjectProperties[j] = (CDOPPROPERTYVALUE  *)(void *)(((DWORD)&(pNew->ppPropertiesArray[i]->ppObjectProperties))  
            + nPropertyCount * sizeof(CDOPPROPERTYVALUE  *) 
            + j * sizeof(CDOPPROPERTYVALUE));
     }
   }

   if (ppCDOPPROPERTIES_OBJECT_LIST)
     *ppCDOPPROPERTIES_OBJECT_LIST = pNew;
   return pNew;

}



//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectBalance::Copy_CDOPPROPERTIES_TO_EDIT_ARRAY(CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY_DEST, CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC)
{
  pCDOPPROPERTIES_TO_EDIT_ARRAY_DEST->nCount = pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC->nCount;
  for (int i = 0; i < pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC->nCount; i++)
  {
    memcpy(pCDOPPROPERTIES_TO_EDIT_ARRAY_DEST->ppProperties_To_Edit_Entry[i], 
           pCDOPPROPERTIES_TO_EDIT_ARRAY_SRC->ppProperties_To_Edit_Entry[i], 
           sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY));

  }
  return TRUE;

}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectBalance::Copy_CDOPPROPERTIES_OBJECT_PROPERTIES(CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST, CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC)
{
   pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->fDirty = pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->fDirty;
   pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->fReadOnly = pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->fReadOnly;
   pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->nProperties = pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->nProperties;
   memcpy(pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->szObjectName, 
          pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->szObjectName, 
          sizeof(pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->szObjectName));

   for (int i = 0 ; i < pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->nProperties; i++)
   {
     memcpy(pCDOPPROPERTIES_OBJECT_PROPERTIES_DEST->ppObjectProperties[i], 
            pCDOPPROPERTIES_OBJECT_PROPERTIES_SRC->ppObjectProperties[i], 
            sizeof(CDOPPROPERTYVALUE));
   }
   return TRUE;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectBalance::Copy_CDOPPROPERTIES_OBJECT_LIST(CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST_DEST, CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST_SRC)
{
   pCDOPPROPERTIES_OBJECT_LIST_DEST->nObjectCount = pCDOPPROPERTIES_OBJECT_LIST_SRC->nObjectCount;
   for (int i = 0; i < pCDOPPROPERTIES_OBJECT_LIST_DEST->nObjectCount; i++)
   {
     Copy_CDOPPROPERTIES_OBJECT_PROPERTIES(pCDOPPROPERTIES_OBJECT_LIST_DEST->ppPropertiesArray[i], 
                                           pCDOPPROPERTIES_OBJECT_LIST_SRC->ppPropertiesArray[i]);
   }
   return TRUE;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsEqual_CDOPPROPERTIES_TO_EDIT_ARRAY(CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY1, CDOPPROPERTIES_TO_EDIT_ARRAY * pCDOPPROPERTIES_TO_EDIT_ARRAY2)
{
  if (pCDOPPROPERTIES_TO_EDIT_ARRAY1->nCount != pCDOPPROPERTIES_TO_EDIT_ARRAY2->nCount)
    return FALSE;

  for (int i = 0; i < pCDOPPROPERTIES_TO_EDIT_ARRAY1->nCount; i++)
  {
    if (memcmp(pCDOPPROPERTIES_TO_EDIT_ARRAY1->ppProperties_To_Edit_Entry[i], 
           pCDOPPROPERTIES_TO_EDIT_ARRAY2->ppProperties_To_Edit_Entry[i], 
           sizeof(CDOPPROPERTIES_TO_EDIT_ENTRY)))
           return FALSE;

  }
  return TRUE;

}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsEqual_CDOPPROPERTIES_OBJECT_PROPERTIES(CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES1, CDOPPROPERTIES_OBJECT_PROPERTIES * pCDOPPROPERTIES_OBJECT_PROPERTIES2)
{
  if (pCDOPPROPERTIES_OBJECT_PROPERTIES1->nProperties != pCDOPPROPERTIES_OBJECT_PROPERTIES2->nProperties ||
      pCDOPPROPERTIES_OBJECT_PROPERTIES1->fDirty != pCDOPPROPERTIES_OBJECT_PROPERTIES2->fDirty  ||
      pCDOPPROPERTIES_OBJECT_PROPERTIES1->fReadOnly != pCDOPPROPERTIES_OBJECT_PROPERTIES2->fReadOnly ||
      memcmp(pCDOPPROPERTIES_OBJECT_PROPERTIES1->szObjectName, 
          pCDOPPROPERTIES_OBJECT_PROPERTIES2->szObjectName, 
          sizeof(pCDOPPROPERTIES_OBJECT_PROPERTIES2->szObjectName)))
    return FALSE;


  for (int i = 0 ; i < pCDOPPROPERTIES_OBJECT_PROPERTIES2->nProperties; i++)
  {
     if (memcmp(pCDOPPROPERTIES_OBJECT_PROPERTIES1->ppObjectProperties[i], 
            pCDOPPROPERTIES_OBJECT_PROPERTIES2->ppObjectProperties[i], 
            sizeof(CDOPPROPERTYVALUE)))
     {
       return FALSE;
     }

  }
  return TRUE;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsEqual_CDOPPROPERTIES_OBJECT_LIST(CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST1, CDOPPROPERTIES_OBJECT_LIST * pCDOPPROPERTIES_OBJECT_LIST2)
{
   if (pCDOPPROPERTIES_OBJECT_LIST1->nObjectCount != pCDOPPROPERTIES_OBJECT_LIST2->nObjectCount)
    return FALSE;

  for (int i = 0 ; i < pCDOPPROPERTIES_OBJECT_LIST2->nObjectCount; i++)
  {
    if (!IsEqual_CDOPPROPERTIES_OBJECT_PROPERTIES(pCDOPPROPERTIES_OBJECT_LIST1->ppPropertiesArray[i], 
          pCDOPPROPERTIES_OBJECT_LIST2->ppPropertiesArray[i])) 
          return FALSE;
  }
  return TRUE;
}




//****************************************************************************************
//
//
//
//****************************************************************************************
CDlgObjectBalance::~CDlgObjectBalance()
{
  if (m_pObjectList)
  {
    free(m_pObjectList);
  }
}


//****************************************************************************************
//
//
//
//****************************************************************************************
CDlgObjectBalance::CDlgObjectBalance(CWnd* pParent /*=NULL*/)
	: CDialog(CDlgObjectBalance::IDD, pParent)
{
	//{{AFX_DATA_INIT(CDlgObjectBalance)
	//}}AFX_DATA_INIT

  m_pObjectList = NULL;
  ClearObjectDataSet();

  // This will be passed in later with a list of properties to edit
  m_paPropertiesToEdit = NULL;

  m_fnFilterFunction = NULL;
  m_fShowFullObjectName = FALSE;
  m_fShowFullPropertyName = FALSE;
  m_fShowPageOnly = FALSE;
  m_eSearchType = eSingleSubdirectory;
  m_fDirty = FALSE;
  m_fAllowPresentToChange = TRUE;
  m_nHorzControlSize = 0;
  m_nLastControlID = IDC_VARIABLE_VALUE_PLACEHOLDER;
}

//****************************************************************************************
//
//
//
//****************************************************************************************
int CDlgObjectBalance::DoModal()
{
  // 
  // Check to make sure we have all the data set that we need
  //
  if (
      m_strFileNameToSearchSubdirectoriesFor.IsEmpty() ||
      m_strObjectFilesRootDirectory.IsEmpty() ||
      m_paPropertiesToEdit == NULL ||
      !m_paPropertiesToEdit->nCount // have to have at least one property
      )
  {
    return IDABORT;
  }
  return CDialog::DoModal();

}

//****************************************************************************************
//
//
//
//****************************************************************************************
void CDlgObjectBalance::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDlgObjectBalance)
	DDX_Control(pDX, IDC_DOP_PROPERTIES, m_staticPropertiesGroup);
	DDX_Control(pDX, ID_DOB_SCROLLBAR, m_sbScrollBarVert);
	DDX_Control(pDX, ID_DOB_SCROLLBAR2, m_sbScrollBarHorz);
	//}}AFX_DATA_MAP
}

//****************************************************************************************
//
//
//
//****************************************************************************************

BEGIN_MESSAGE_MAP(CDlgObjectBalance, CDialog)
	//{{AFX_MSG_MAP(CDlgObjectBalance)
	ON_BN_CLICKED(IDC_VALUE_PLUS, OnValuePlus)
	ON_BN_CLICKED(IDC_VALUE_MULTIPLY, OnValueMultiply)
	ON_BN_CLICKED(IDC_VALUE_MINUS, OnValueMinus)
	ON_BN_CLICKED(IDC_VALUE_DIVIDE, OnValueDivide)
	ON_WM_VSCROLL()
	ON_WM_HSCROLL()
	ON_BN_CLICKED(IDC_FILL_ALL_VALUES, OnFillAllValues)
	ON_BN_CLICKED(IDC_CHECKALL_APPLY_BUTTONS, OnCheckallApplyButtons)
	//}}AFX_MSG_MAP
  ON_WM_ERASEBKGND()
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDlgObjectBalance message handlers

BOOL CDlgObjectBalance::OnInitDialog() 
{
	CDialog::OnInitDialog();

  m_fDirty = FALSE;
  ClearObjectDataSet();

  //
  // For now we just have a fixed size data set maximum. Otherwise we would have to 
  // iterate all the files once or create a link list of objects instead of an array.
  // Any of these can be done, the array was simply created to save time getting this 
  // code up to speed.
  //
  if (m_pObjectList)
    free(m_pObjectList);
  m_pObjectList = Allocate_CDOPPROPERTIES_OBJECT_LIST(c_Max_Objects_Supported, m_paPropertiesToEdit->nCount);
  m_pObjectList->nObjectCount = 0; // this many being used currently
  
  //
  // Search for the weapons subsytem files
  //
  LoadObjectData(m_strObjectFilesRootDirectory, m_eSearchType);


  //
  // Create all the sub-controls that represent the data that we are going to be editting in the
  // dialog
  // Note: We are not allowed to get here if we don't have a properties list since DoModal should kick
  //       us out, and this dialog is not meant to be modeless.
  CreateSubControlsForProperties();
  ClearModifierCells();

  UpdateControlData(TRUE);
  UpdateData(FALSE);


	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::CreateSubControlsForProperties()
{
  // 
  // Go through the list of properties we are editting and place all the controls.
  // Use the reserved value in the property array passed in to store the starting ID
  // of each properties control set. Offsets To there are based on the type of variable
  // that is being used.
  m_nLastControlID = IDC_VARIABLE_VALUE_PLACEHOLDER;
  
  //
  // The following hidden objects have been added to the resource dialog in order to 
  // make formatting fully adjustable without having to modify constants in the code
  // We get the base size, and position for all controls To these
  // IDC_DOP_PROPNAME, IDC_DOP_PROPPRESENT, IDC_DOP_PROPEDIT1, IDC_DOP_PROPEDIT2, IDC_DOP_PROPEDIT3 
  //
  // Use the dummy hidden controls to place our new ones, unhide the dummy controls
  // for the first property, and then build the rest
  CWnd * pControlPlus                = GetDlgItem(IDC_VALUE_PLUS);
  CWnd * pControlPresentPlaceHolder  = GetDlgItem(IDC_VARIABLE_PRESENT_PLACEHOLDER);
  CWnd * pControlValuePlaceHolder    = GetDlgItem(IDC_VARIABLE_VALUE_PLACEHOLDER);
  CWnd * pControlObjectNameHeader    = GetDlgItem(IDC_OBJECT_NAME_HEADER);
  CWnd * pControlApplyChangesPlaceHolder = GetDlgItem(IDC_APPLYCHANGESPLACEHOLDER);
  CWnd * pControlGroupboxArea            = GetDlgItem(IDC_DOP_PROPERTIES);

  if (!pControlPlus || !pControlPresentPlaceHolder || !pControlValuePlaceHolder)
    return; // badness has occurred, but no way out of it at this point

  //CFont * pFontControlPlus               = pControlPlus              ->GetFont();              
  CFont * pFontControlPresentPlaceHolder = pControlPresentPlaceHolder->GetFont();
  CFont * pFontControlValuePlaceHolder   = pControlValuePlaceHolder  ->GetFont();  
  CFont * pFontControlObjectNameHeader   = pControlObjectNameHeader  ->GetFont();  

  CRect crRectDlg;
  CRect crControlPlus;              
  CRect crControlPresentPlaceHolder;
  CRect crControlValuePlaceHolder;  
  CRect crControlObjectNameHeader;  
  CRect crControlApplyChangesPlaceHolder;  
  CRect crControlGroupboxArea;


  GetWindowRect(crRectDlg);
  pControlPlus->GetWindowRect(crControlPlus);
  pControlPresentPlaceHolder->GetWindowRect(crControlPresentPlaceHolder);
  pControlValuePlaceHolder->GetWindowRect(crControlValuePlaceHolder);
  pControlObjectNameHeader->GetWindowRect(crControlObjectNameHeader);
  pControlApplyChangesPlaceHolder->GetWindowRect(crControlApplyChangesPlaceHolder);
  pControlGroupboxArea->GetWindowRect(crControlGroupboxArea);
  pControlApplyChangesPlaceHolder->GetWindowRect(crControlApplyChangesPlaceHolder);
  //
  // Offset the rects relative to the dialog
  //
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crControlPlus, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crControlPresentPlaceHolder, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crControlValuePlaceHolder, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crControlObjectNameHeader, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crControlGroupboxArea, 2);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crControlApplyChangesPlaceHolder, 2);
  
  //
  // If the caller wants to override the edit control size then they can.
  //
  if (m_nHorzControlSize > 0)
  {
    crControlValuePlaceHolder.right = crControlValuePlaceHolder.left + m_nHorzControlSize;
  }


  int nCurrentProperty = 0;
  // if we didn't have at least one item in the property list DoModal() should have kicked us out
  // so it should be safe to reference this without checking here.

  // Next root ID is 1 after this set
  int nCurrObject = 0;

  //
  // For the first pass we create a set of controls on the top to allow the multiple adding/multiplying
  // factors used for balancing.
  //
  //
  int nCurrX = crControlValuePlaceHolder.left;
  int nCurrY = crControlPlus.top;
  const int nYBufferSizeAfterRow       = 0;
  const int nXBufferSizeAfterPresent   = 2;
  const int nXBufferSizeAfterSmallEdit = 1;
  const char * szStaticText = NULL;
  //
  // The ID's for the controls are based on the total number possible per row to make it easier, 
  // 1 for the name of the object and 4 for the 1 present checkbox and 3 edit controls
  // It starts with the base controls then adds a whole row of values. Then those are applied to the upper row
  // so the first row of actual values is two rows after the place holder base value.
  int nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (1) );
  const int nXBufferSizeAfterFullEdit  = 4;
  HWND hWndTemp = NULL;
  while (nCurrentProperty < m_paPropertiesToEdit->nCount)
  {
  // Create a property name header

    if (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTagForDisplay[0] )
    {
      szStaticText = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTagForDisplay;
    }
    else
    {
      szStaticText = 
        m_fShowFullPropertyName ? 
                m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTag 
                : (m_fShowPageOnly ? 
                      GetPageNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTag )
                    : GetEntryNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->szPropertyTag ));
    }
      
    hWndTemp = ::CreateWindow("STATIC", 
        szStaticText, 
        WS_VISIBLE | SS_LEFT | WS_CHILD, 
        nCurrX, crControlObjectNameHeader.top, crControlValuePlaceHolder.Width(), crControlObjectNameHeader.Height(),  
        GetSafeHwnd(), 
        // This is now located on the second row of ID's that I am concerned about
        (HMENU)((int) nCurrID + 1 + ((m_paPropertiesToEdit->nCount * 4) + 1)), theApp.m_hInstance, NULL); 
    ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlObjectNameHeader, (LPARAM)FALSE);


    CDOPVARTYPE eVarType = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType;
    switch (eVarType)
    {
    case eVector3D:
      hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
          "", 
          WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD,
          nCurrX, nCurrY, crControlValuePlaceHolder.Width()/3, crControlValuePlaceHolder.Height(), 
          GetSafeHwnd(), (HMENU)(int)(nCurrID+1), theApp.m_hInstance, NULL); 
      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);
      nCurrX += crControlValuePlaceHolder.Width()/3 + nXBufferSizeAfterSmallEdit; 
      hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
          "", 
          WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD ,
          nCurrX, nCurrY, crControlValuePlaceHolder.Width()/3, crControlValuePlaceHolder.Height(), 
          GetSafeHwnd(), (HMENU)(int)(nCurrID+2), theApp.m_hInstance, NULL); 
      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);
      nCurrX += crControlValuePlaceHolder.Width()/3 + nXBufferSizeAfterSmallEdit; 
      hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
          "", 
          WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD ,
          nCurrX, nCurrY, crControlValuePlaceHolder.Width()/3, crControlValuePlaceHolder.Height(), 
          GetSafeHwnd(), (HMENU)(int)(nCurrID+3), theApp.m_hInstance, NULL); 
      nCurrX += crControlValuePlaceHolder.Width()/3 + nXBufferSizeAfterFullEdit
             + crControlPresentPlaceHolder.Width() + nXBufferSizeAfterPresent;
      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);

      break;
    case efloat:
    case eint:
    case eLPSTR:
    case eBOOL:
      hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
          "", 
          WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD,
          nCurrX, nCurrY, crControlValuePlaceHolder.Width(), crControlValuePlaceHolder.Height(), 
          GetSafeHwnd(), (HMENU)(int)(nCurrID+1), theApp.m_hInstance, NULL); 
      nCurrX += crControlValuePlaceHolder.Width() + nXBufferSizeAfterFullEdit
             + crControlPresentPlaceHolder.Width() + nXBufferSizeAfterPresent;
      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);
      break;

    }

    nCurrID += 4;
    nCurrentProperty++;
  }

  m_nMaxVisibleVert = crControlGroupboxArea.Height()/(crControlValuePlaceHolder.Height() + nYBufferSizeAfterRow);
  m_nCurScrollPosVert = 0;
  m_nScrollIncrementVert = (crControlValuePlaceHolder.Height() + nYBufferSizeAfterRow); 

  m_sbScrollBarVert.SetScrollPos(0, FALSE);
  m_sbScrollBarVert.SetScrollRange(0, max(0, m_pObjectList->nObjectCount - m_nMaxVisibleVert + 2), TRUE); 

  m_nMaxVisibleHorz = (crControlGroupboxArea.Width() - (crControlPresentPlaceHolder.left))
                      /(crControlValuePlaceHolder.Width() + nXBufferSizeAfterPresent +   nXBufferSizeAfterFullEdit
                      + crControlPresentPlaceHolder.Width());

  m_nCurScrollPosHorz = 0;
  m_nScrollIncrementHorz = (crControlValuePlaceHolder.Width() + nXBufferSizeAfterPresent + nXBufferSizeAfterSmallEdit + crControlPresentPlaceHolder.Width()); 

  m_sbScrollBarHorz.SetScrollPos(0, FALSE);
  m_sbScrollBarHorz.SetScrollRange(0, max(0, m_paPropertiesToEdit->nCount  - m_nMaxVisibleHorz + 2), TRUE); 

  //
  // Set up the static window's children to work as part of the dialog itself.
  //
  long wsWindowStyle = ::GetWindowLong(pControlGroupboxArea->GetSafeHwnd(), GWL_EXSTYLE);
  wsWindowStyle |= WS_EX_CONTROLPARENT;
  ::SetWindowLong(pControlGroupboxArea->GetSafeHwnd(), GWL_EXSTYLE, wsWindowStyle);

  //
  // The ID's for the controls are based on the total number possible per row to make it easier, 
  // 1 for the name of the object and 4 for the 1 present checkbox and 3 edit controls
  // It starts with the base controls then adds a whole row of values. Then those are applied to the upper row
  // so the first row of actual values is two rows after the place holder base value.
  nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (nCurrObject + 3) );

  nCurrY = crControlValuePlaceHolder.top;

  while (nCurrObject < m_pObjectList->nObjectCount)
  {
    BOOL fReadOnly = m_pObjectList->ppPropertiesArray[nCurrObject]->fReadOnly;
    UINT uiDisabledFlagIfNecessary = fReadOnly ? WS_DISABLED : 0;

    nCurrX = crControlPlus.left;
    
    szStaticText = 
      m_fShowFullObjectName ? 
      m_pObjectList->ppPropertiesArray[nCurrObject]->szObjectName :
      GetShortObjectName(m_pObjectList->ppPropertiesArray[nCurrObject]->szObjectName);
    hWndTemp = ::CreateWindow("STATIC", 
        szStaticText, 
        WS_VISIBLE | SS_LEFT | WS_CHILD, 
        crControlObjectNameHeader.left - crControlGroupboxArea.left + 2, nCurrY - crControlGroupboxArea.top, crControlObjectNameHeader.Width(), crControlValuePlaceHolder.Height(), 
        pControlGroupboxArea->GetSafeHwnd(), 
        (HMENU)IDC_STATIC, theApp.m_hInstance, NULL); 
    ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlObjectNameHeader, (LPARAM)FALSE);


   
    HWND hWndTemp = NULL;
    nCurrentProperty = 0; // reset for use in the next pass
    nCurrX = crControlPresentPlaceHolder.left;
    //
    // Create the check boxes that allow you to control which weapons are effected by a given change.
    //
    // Since the CurrID is the present button, and represents the beginning of data storage, nCurrID -1 will be the apply checkbox
    nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (nCurrObject + 3));
    hWndTemp = ::CreateWindow("BUTTON", 
        "", 
        WS_VISIBLE | BS_CHECKBOX | WS_CHILD | BS_AUTOCHECKBOX | uiDisabledFlagIfNecessary, 
        crControlApplyChangesPlaceHolder.left - crControlGroupboxArea.left, nCurrY - crControlGroupboxArea.top, crControlApplyChangesPlaceHolder.Width(), crControlApplyChangesPlaceHolder.Height(), 
        pControlGroupboxArea->GetSafeHwnd(), 
        (HMENU)(int)(nCurrID-1), 
        theApp.m_hInstance, NULL); 
    ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlPresentPlaceHolder, (LPARAM)FALSE);
    if (fReadOnly)
    {
      ::SendMessage(hWndTemp, BM_SETCHECK, BST_UNCHECKED, 0);
    }




    // Start the ID count for the controls at the set above 0, to skip the place holders
    // It could be made tighter than this but isn't necessary.

    while (nCurrentProperty < m_paPropertiesToEdit->nCount)
    {
      
      nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (nCurrObject + 3) + (nCurrentProperty * 4));
      hWndTemp = ::CreateWindow("BUTTON", 
          "", 
          WS_VISIBLE | BS_CHECKBOX | WS_CHILD | BS_AUTOCHECKBOX | uiDisabledFlagIfNecessary | (m_fAllowPresentToChange ? 0 : WS_DISABLED), 
          nCurrX - crControlGroupboxArea.left, nCurrY - crControlGroupboxArea.top, crControlPresentPlaceHolder.Width(), crControlPresentPlaceHolder.Height(), 
          pControlGroupboxArea->GetSafeHwnd(), 
          (HMENU)(int)(nCurrID+0), 
          theApp.m_hInstance, NULL); 
      ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlPresentPlaceHolder, (LPARAM)FALSE);
      nCurrX += crControlPresentPlaceHolder.Width() + nXBufferSizeAfterPresent;
      
      CDOPVARTYPE eVarType = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType;
      switch (eVarType)
      {
      case eVector3D:
        hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
            "", 
            WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD | uiDisabledFlagIfNecessary,
            nCurrX - crControlGroupboxArea.left, nCurrY - crControlGroupboxArea.top, crControlValuePlaceHolder.Width()/3, crControlValuePlaceHolder.Height(), 
            pControlGroupboxArea->GetSafeHwnd(), 
            (HMENU)(int)(nCurrID+1), 
            theApp.m_hInstance, NULL); 
        ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);
        nCurrX += crControlValuePlaceHolder.Width()/3 + nXBufferSizeAfterSmallEdit; 
        hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
            "", 
            WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD | uiDisabledFlagIfNecessary,
            nCurrX - crControlGroupboxArea.left, nCurrY - crControlGroupboxArea.top, crControlValuePlaceHolder.Width()/3, crControlValuePlaceHolder.Height(), 
            pControlGroupboxArea->GetSafeHwnd(), 
            (HMENU)(int)(nCurrID+2), 
            theApp.m_hInstance, NULL); 
        ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);
        nCurrX += crControlValuePlaceHolder.Width()/3 + nXBufferSizeAfterSmallEdit; 
        hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
            "", 
            WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD | uiDisabledFlagIfNecessary,
            nCurrX - crControlGroupboxArea.left, nCurrY - crControlGroupboxArea.top, crControlValuePlaceHolder.Width()/3, crControlValuePlaceHolder.Height(), 
            pControlGroupboxArea->GetSafeHwnd(), 
            (HMENU)(int)(nCurrID+3), 
            theApp.m_hInstance, NULL); 
        nCurrX += crControlValuePlaceHolder.Width()/3 + nXBufferSizeAfterFullEdit;
        ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);

        break;
      case efloat:
      case eint:
      case eLPSTR:
      case eBOOL:
        hWndTemp = ::CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_NOPARENTNOTIFY, "EDIT", 
            "", 
            WS_VISIBLE | WS_BORDER | ES_LEFT | WS_CHILD | uiDisabledFlagIfNecessary,
            nCurrX - crControlGroupboxArea.left, nCurrY - crControlGroupboxArea.top, crControlValuePlaceHolder.Width(), crControlValuePlaceHolder.Height(), 
            pControlGroupboxArea->GetSafeHwnd(), 
            (HMENU)(int)(nCurrID+1), 
            theApp.m_hInstance, NULL); 
          nCurrX += crControlValuePlaceHolder.Width() + nXBufferSizeAfterFullEdit;
        ::SendMessage(hWndTemp, WM_SETFONT,  (WPARAM)(HFONT)*pFontControlValuePlaceHolder, (LPARAM)FALSE);
        break;

      }  // switch on property type
      
      nCurrID += 4;
      nCurrentProperty++;
    } // while currentproperty
    nCurrY += crControlValuePlaceHolder.Height() + nYBufferSizeAfterRow;
    nCurrObject++;
  } // while nCurrentObject
  m_nLastControlID = nCurrID;
} // end of CreateSubControlsForProperties()



//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnOK()
{
  // 
  // Validate the data and save any changes made since the last sel change
  //
  UpdateControlData(FALSE); 
  //
  // Ask them if they want to commit the changes? Or should pressing OK be enough? Should there be a
  // safety net?
  //
  if (m_fDirty)
  {
    int ret = MessageBox( MyLoadStringTempBuffer(IDS_SAVE_WEAPONS_CHANGES), MyLoadStringTempBuffer(IDS_SAVE_WEAPONS_CHANGES_CAPTION), MB_YESNO);
    if (ret != IDYES)
    {
      return;
    }
    //
    // save the changes, exit if failed without ending the dialog
    //
    if (!SaveObjectData())
    {
      MessageBox(MyLoadStringTempBuffer(IDS_SAVE_WEAPONS_FAILED), MyLoadStringTempBuffer(IDS_ERROR_CAPTION), MB_OK | MB_ICONEXCLAMATION);
    }
  }

  // 
  // If we got here, everything should be saved
  //
  CDialog::OnOK();
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnCancel()
{
  // 
  // It's safe to exit, as long as all the notation files are closed out, which should be the case 
  //
  CDialog::OnCancel();
}


//****************************************************************************************
// 
// Sets the root directory to search under. Only goes one level deep currently
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetObjectFilesDirectory(LPCSTR lpszDirectoryPath)
{
  m_strObjectFilesRootDirectory = lpszDirectoryPath;
  EndDirectoryWithBackSlash(m_strObjectFilesRootDirectory );
  return TRUE;
}
//****************************************************************************************
//
// Sets the root file name type to search for like "*.data"
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetFileNameToSearchSubdirectoriesFor(LPCSTR lpszFileSearchString)
{
  m_strFileNameToSearchSubdirectoriesFor = lpszFileSearchString;
  return TRUE;
}


//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::ClearObjectDataSet()
{
  //
  // If you didn't allocate this structure as a whole you have to walk the tree and kill the internal
  // pointer memory before you do this, but if you used the allocate_... function provided above it was
  // all allocated as one huge chunk instead of in parts.
  //
  if (m_pObjectList)
  {
    free(m_pObjectList);
    m_pObjectList = NULL;
  }

}

//****************************************************************************************
//
//
//****************************************************************************************
LPCSTR CDlgObjectBalance::GetPageNameToPropertyName(LPCSTR szPropertyName)
{
  static char s_szPageNameBuffer[128];
  // get everything up to the first \ in the string
  const char * pTempSrc = szPropertyName;
  char * pTempDest = s_szPageNameBuffer;

  while (*pTempSrc && *pTempSrc != '\\') *(pTempDest++) = *(pTempSrc++);
  *pTempDest = '\0';
  return s_szPageNameBuffer;
}

//****************************************************************************************
//
//
//****************************************************************************************
LPCSTR CDlgObjectBalance::GetEntryNameToPropertyName(LPCSTR szPropertyName)
{
  static char s_szEntryNameBuffer[128];
  // get everything after the last \ in the string
  const char * pTempSrc = szPropertyName;
  const char * pLastBackSlash = szPropertyName;
  while (*pTempSrc)
  {
    if (*pTempSrc == '\\')
      pLastBackSlash = pTempSrc;
    pTempSrc++;
  }
  if (*pLastBackSlash == '\\')
    pLastBackSlash++;

  strcpy(s_szEntryNameBuffer, pLastBackSlash);
  return s_szEntryNameBuffer;
}


//****************************************************************************************
//
// Save weapons data will save all the information back to the appropriate notation files. It only saves
// out the weapons that are changed.
// 
//****************************************************************************************
BOOL CDlgObjectBalance::SaveObjectData()
{
  int nFailures = 0;

  for (int i = 0; i< m_pObjectList->nObjectCount;i++)
  {
    if (!m_pObjectList->ppPropertiesArray[i]->fDirty) // not an error condition, just a shortcut
      continue; // don't worry about saving this one if it hasn't changed.
    if (!m_pObjectList->ppPropertiesArray[i]->szObjectName[0])
    {
      nFailures++;
      continue;
    }
    if (m_pObjectList->ppPropertiesArray[i]->fReadOnly) // shouldn't have been allowed to change this
    {
      nFailures++;
      continue;
    }
    //
    // Loop through all the properties and save them
    //
    Page * page = NULL;
    char szCurrentPageName[128] = ""; // used to prevent repetitively searching for the same page
    NotationFile outfile((LPCSTR)m_pObjectList->ppPropertiesArray[i]->szObjectName, NotationFile::Raw);
    for (int j=0; j < m_pObjectList->ppPropertiesArray[i]->nProperties; j++)
    {
      const char * szPageName = GetPageNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
      if (!page || strcmp(szCurrentPageName, szPageName))
      {
        strcpy(szCurrentPageName, szPageName);
        page = outfile.FindPage(szPageName);
        if (!page) // if for some reason we can't find the page, jump out
          continue;
      }
      const char * szEntryName = GetEntryNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
      //
      // For each item in the data list, if it shouldn't be there, delete it. If it supposed to be there set it
      // Assumption is made that there are not two of any particular value key
      //
	    if (!m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->fPresent)
      {
        page->DeleteNote(szEntryName);
      }
      else
      {
        page->DeleteNote(szEntryName); // temporary kludge to force the dirty bit. This can be removed
                                       // after we decide if it is safe to make "SetEntry" set the dirty bit itself
        switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->ePropertyType)
        {
        case efloat:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.fltValue);
          break;
        case eint:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.nValue);
          break;
        case eLPSTR:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.szValue);
          break;
        case eBOOL:
          page->SetEntry(szEntryName, m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.fValue);
          break;
        case eVector3D:
          {
            Vector3D v3D(m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.v3dValues.x,
                         m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.v3dValues.y,
                         m_pObjectList->ppPropertiesArray[i]->ppObjectProperties[j]->uPropValue.v3dValues.z);
          page->SetEntry(szEntryName, (Vector3D)v3D);
          break;
          }
        } // switch for property type
      } // else, we are setting a property instead of removing one (ie.. fPresent true)
    } // for loop iterating properties
     
    outfile.Save();
  } // for loop iterating objects   

  return (!nFailures);
}

//****************************************************************************************
// LoadObjectData()
//
// This routine will search the content\weaponsubsystems directory for subdirectories containing .data files
// It will then open those .data files to read in weapon data. It is a simplistic algorithm that should be replaced
// by either searching through resources.build or some other weapons reference file if they are made to be fully up 
// to date with their references to the weapons.
//
// Recursive function so watch what kind of initializations you do, or how you effect
// global variables.
//
// Directory passed in must end in a backslash
//****************************************************************************************
BOOL CDlgObjectBalance::LoadObjectData(LPCSTR szDirectoryToSearchFor, DIRECTORYSEARCHTYPE eSearchType)
{
  CString strRootDirSearchString = szDirectoryToSearchFor;
  strRootDirSearchString += "*.*";
  CString strFullSubdirectoryName ;
  BOOL fRet = TRUE;
  //
  // If we are only supposed to seach in the root directory pass this to the next function that finds
  // the actual files
  //
  if (eSearchType == eRootOnly)
  {
    fRet = LoadDataFilesFromSubdirectory(szDirectoryToSearchFor);
    return fRet;  // return so we don't do any more searching
  }
  
  //
  // If we are doing all directories then we should search in the root as well, then
  // proceed down to the next level.
  //
  if (eSearchType == eAllSubdirectories)
  {
    fRet = LoadDataFilesFromSubdirectory(szDirectoryToSearchFor);
  }
  //
  // If we got an error, exit now, we don't continue searching the subdirectories
  //
  if (!fRet)
    return fRet;

  // 
  // If we got here we need to search for subdirectories of the current one
  // Then call this function recursively on each of those directories
  //

  //
  // Now we iterate through the current directory looking for subdirectories.
  //
  //
  WIN32_FIND_DATA wfdFindFileData;

  HANDLE hFileSearch = FindFirstFile((LPCSTR)strRootDirSearchString , &wfdFindFileData);
  if (hFileSearch == INVALID_HANDLE_VALUE)
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    return TRUE;
  }
  if (wfdFindFileData.cFileName[0] == '\0')
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    FindClose(hFileSearch); 
    return TRUE;
  }
  while (wfdFindFileData.cFileName[0])
  {
    if (!stricmp(wfdFindFileData.cFileName, ".") || !stricmp(wfdFindFileData.cFileName, ".."))
    {
      goto NextFile;
    }

    if (!(wfdFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
    {
      goto NextFile;
    }

    strFullSubdirectoryName = szDirectoryToSearchFor;
    strFullSubdirectoryName += wfdFindFileData.cFileName;
    EndDirectoryWithBackSlash(strFullSubdirectoryName);
    if (eSearchType == eAllSubdirectories)
    {
      LoadObjectData(strFullSubdirectoryName, eAllSubdirectories);
    }
    else // since eRootOnly was already weeded out above, this means "eSingleSubdirectory"
    {
      LoadObjectData(strFullSubdirectoryName, eRootOnly);
    }



NextFile:

    if (!FindNextFile(hFileSearch, &wfdFindFileData))
    {
      break;
    }

  } // while

  FindClose(hFileSearch);

  fRet = TRUE;
  //
  // Now the list box should be updated. We need a default selection, and then that will trigger the 
  // the fields to update correctly on the dialog box.
  //
  return fRet;
}



//****************************************************************************************
//
//****************************************************************************************
LPCSTR CDlgObjectBalance::GetShortObjectName(LPCSTR szObjectName)
{
  //
  // CString is used to remove the directory info To the weapons name. Find the last \
  //
  CString strObjectNameOnly(szObjectName);
  int nPos = 0;
  if (!m_fShowFullObjectName)
  {
    nPos = strObjectNameOnly.ReverseFind('\\');
    nPos ++; // goes to 0 if not found, otherwise the character after the \ or the null terminator if that's last
  }

  return (szObjectName + nPos);
}
//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::LoadDataFilesFromSubdirectory(CString strSubdirectory)
{
  EndDirectoryWithBackSlash(strSubdirectory);
  CString strFullFilename ;
  CString strRootDirSearchString = strSubdirectory;

  strRootDirSearchString += m_strFileNameToSearchSubdirectoriesFor;
  //
  // Now we iterate through the subdirectory looking for files
  //
  //

  WIN32_FIND_DATA wfdFindFileData;

  HANDLE hFileSearch = FindFirstFile((LPCSTR)strRootDirSearchString , &wfdFindFileData);
  if (hFileSearch == INVALID_HANDLE_VALUE)
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    return TRUE;
  }
  if (wfdFindFileData.cFileName[0] == '\0')
  {
//    strOutput.Format("\r\nError : Didn't find any files in the source dir to copy.");
//    OutputString(strOutput);
    FindClose(hFileSearch); 
    return TRUE;
  }
  while (wfdFindFileData.cFileName[0])
  {
    if (!stricmp(wfdFindFileData.cFileName, ".") || !stricmp(wfdFindFileData.cFileName, ".."))
    {
      goto NextFile;
    }

    if (wfdFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
    {
      goto NextFile;
    }

    strFullFilename  = strSubdirectory;
    strFullFilename  += wfdFindFileData.cFileName;
    
    //
    // See if there is an additional filter function to call, and if so, call it
    //
    if (m_fnFilterFunction && !m_fnFilterFunction((LPCSTR)strFullFilename))
    {
      goto NextFile;
    }
    // 
    // We have a .data file, so see if it is appropriately setup and load into a data structure and 
    // add to the list box. We need to know if it is read only so we know whether or not we can 
    // alter the data
    // The data structure will also have to store a variable to know if the data has been altered.
    // 
    if (LoadObjectDataFromFile(strFullFilename , (wfdFindFileData.dwFileAttributes & FILE_ATTRIBUTE_READONLY)))
    {
      // 
      // If we successfully loaded the weapon, add it to the list box
      //
      // Alias some of the data for readability of the code
      int nCurrObject = m_pObjectList->nObjectCount;
      CDOPPROPERTIES_OBJECT_PROPERTIES * pObjectPropertiesArray = (m_pObjectList->ppPropertiesArray[nCurrObject]);
      
      //
      // CString is used to remove the directory info To the weapons name. Find the last \
      //
      CString strObjectNameOnly(pObjectPropertiesArray->szObjectName);
      int nPos = 0;
      if (!m_fShowFullObjectName)
      {
        nPos = strObjectNameOnly.ReverseFind('\\');
        nPos ++; // goes to 0 if not found, otherwise the character after the \ or the null terminator if that's last
      }

      m_pObjectList->nObjectCount++; // only increment on success since we don't want the index off in the list
    }



NextFile:

    if (!FindNextFile(hFileSearch, &wfdFindFileData))
    {
      break;
    }
  
  } // while

  FindClose(hFileSearch);
  return TRUE;
}

//****************************************************************************************
//
//
//
//
//
//****************************************************************************************
BOOL CDlgObjectBalance::LoadObjectDataFromFile(CString strFullObjectFilename, BOOL fReadOnly )
{
//  CString strOutput;

  NotationFile infile(strFullObjectFilename, NotationFile::Raw);

  //
  // Alias some of the data for readability of the code
  int nCurrObject = m_pObjectList->nObjectCount;
  CDOPPROPERTIES_OBJECT_PROPERTIES * pObjectPropertiesArray = (m_pObjectList->ppPropertiesArray[nCurrObject]);
  int nTotalProperties = m_paPropertiesToEdit->nCount;
  //
  // allocate the memory for the properties for this object
  //
  pObjectPropertiesArray->fDirty = FALSE;
  pObjectPropertiesArray->fReadOnly = fReadOnly;
  pObjectPropertiesArray->nProperties = nTotalProperties;
  strcpy(pObjectPropertiesArray->szObjectName, (LPCSTR)strFullObjectFilename);
  
// already allocated
//  pObjectPropertiesArray->ppObjectProperties = Allocate_CDOPPROPERTIES_OBJECT_PROPERTIES(nTotalProperties);

  //
  // Now go through and for each item in the properties list we need to get the page and entry
  // we will cache the page name so we don't keep getting the same one if it repeats
  //


  Page * page = NULL;
  char szCurrentPageName[128] = ""; // used to prevent repetitively searching for the same page
  for (int j=0; j < nTotalProperties; j++)
  {
    pObjectPropertiesArray->ppObjectProperties[j]->fPresent = FALSE; // by default...
    // put in default values for each type
    switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->ePropertyType)
    {
      case efloat:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fltValue = 0.0f;
        break;
      case eint:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.nValue = 0;
        break;
      case eLPSTR:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.szValue[0] = 0;
        break;
      case eBOOL:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fValue = FALSE;
        break;
      case eVector3D:
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.x = 0.0f;
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.y = 0.0f;
          pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.z = 0.0f;
        break;
    } // switch for property type
    
    
    const char * szPageName = GetPageNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
    if (!page || strcmp(szCurrentPageName, szPageName))
    {
      strcpy(szCurrentPageName, szPageName);
      page = infile.FindPage(szPageName);
      if (!page) // if for some reason we can't find the page, jump out
        continue;
    }
    const char * szEntryName = GetEntryNameToPropertyName(m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->szPropertyTag);
    //
    // For each item in the data list, if it shouldn't be there, delete it. If it supposed to be there set it
    // Assumption is made that there are not two of any particular value key
    //
    switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[j]->ePropertyType)
    {
      case efloat:
        if (page->GetEntry(szEntryName, &(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fltValue)))
        {
          pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
        }
        break;
      case eint:
        if (page->GetEntry(szEntryName, &(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.nValue)))
        {
          pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
        }
        break;
      case eLPSTR:
        {
          const char * szTemp; 
          if (page->GetEntry(szEntryName, &szTemp))
          {
            strcpy(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.szValue, szTemp) ;
            pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
          }
        }
        break;
      case eBOOL:
        if (page->GetEntry(szEntryName, &(pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.fValue)))
        {
          pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
        }
        break;
      case eVector3D:
        {
          Vector3D v3D(0.0f, 0.0f, 0.0f);
          if (page->GetEntry(szEntryName, (Vector3D *)&v3D))
          {
            pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.x = v3D.x;
            pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.y = v3D.y;
            pObjectPropertiesArray->ppObjectProperties[j]->uPropValue.v3dValues.z = v3D.z;
            pObjectPropertiesArray->ppObjectProperties[j]->fPresent = TRUE;
          }
        break;
        }
    } // switch for property type

  } // for loop iterating properties


  //
  // This is a good place to make any known automated updates to the data. If we have just made them all 
  // add a new variable, or we removed a line, or we changed the type of data stored, we can do it at load
  // time, then mark the dirty flag. Beware to honor the readonly flag here though. We don't want the read only
  // and the dirty bit set at the same time.
  // 

  //
  // Note that we don't increment the weapon count here as we have to make sure we added to the list box
  // since they are kept in sync by the index in the list box.
  //
  return TRUE;
}



//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::DisableAllControls()
{

  for (UINT i = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (2) )
          ; i < m_nLastControlID ; i++)
  {
    CWnd * pWnd = GetDescendantWindow(i); 
    if (pWnd)
      pWnd->EnableWindow(FALSE);
  }
  return TRUE;
}


//****************************************************************************************
//
//****************************************************************************************

BOOL CDlgObjectBalance::GetBOOLFromButton(UINT uiID, BOOL * pfDest)
{
  CButton * pWnd = (CButton *)GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  *pfDest = pWnd->GetCheck();
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::GetBOOLFromControl(UINT uiID, BOOL * pfDest)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];

  pWnd->GetWindowText(szTemp, 255);
  
  *pfDest = FALSE;
  if (!stricmp(szTemp, "true") || 
      !stricmp(szTemp, "yes") || 
      !stricmp(szTemp, "1")) 
  {
    *pfDest = TRUE;
  }
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::GetfloatFromControl(UINT uiID, float * pfltDest)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  pWnd->GetWindowText(szTemp, 255);
  *pfltDest = (float)atof(szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::GetLPSTRFromControl(UINT uiID, LPSTR pszDest)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  pWnd->GetWindowText(szTemp, 255);
  szTemp[255] = 0;
  strcpy(pszDest, szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::GetintFromControl(UINT uiID, int * pnDest)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  pWnd->GetWindowText(szTemp, 255);
  *pnDest = atoi(szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::GetVector3DFromControl(UINT uiID, float * pfltx, float * pflty, float * pfltz)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  pWnd->GetWindowText(szTemp, 255);
  *pfltx = (float)atof(szTemp);
  pWnd = GetDescendantWindow(uiID +1);
  if (!pWnd)
    return FALSE;
  pWnd->GetWindowText(szTemp, 255);
  *pflty = (float)atof(szTemp);
  pWnd = GetDescendantWindow(uiID +2);
  if (!pWnd)
    return FALSE;
  pWnd->GetWindowText(szTemp, 255);
  *pfltz = (float)atof(szTemp);

  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetBOOLToButton(UINT uiID, BOOL fDest)
{
  CButton * pWnd = (CButton *)GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  pWnd->SetCheck(fDest);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetBOOLToControl(UINT uiID, BOOL fSrc)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, fSrc ? "true" : "false");
  pWnd->SetWindowText(szTemp);
  return TRUE;

}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetfloatToControl(UINT uiID, float fltSrc)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, "%f", (double) fltSrc);
  pWnd->SetWindowText(szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetLPSTRToControl(UINT uiID, LPSTR pszSrc)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  pWnd->SetWindowText(pszSrc);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetintToControl(UINT uiID, int nSrc)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, "%d", nSrc);
  pWnd->SetWindowText(szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::SetVector3DToControl(UINT uiID, float fltx, float flty, float fltz)
{
  CWnd * pWnd = GetDescendantWindow(uiID);
  if (!pWnd)
    return FALSE;
  char szTemp[256];
  sprintf(szTemp, "%f", (double) fltx);
  pWnd->SetWindowText(szTemp);

  sprintf(szTemp, "%f", (double) flty);
  pWnd->SetWindowText(szTemp);

  sprintf(szTemp, "%f", (double) fltz);
  pWnd->SetWindowText(szTemp);
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::UpdateControlData(BOOL fToControls)
{
  CDOPPROPERTIES_OBJECT_PROPERTIES * pObjectProperties = NULL;
  if (!fToControls)
  {
    
    pObjectProperties = 
            Allocate_CDOPPROPERTIES_OBJECT_PROPERTIES(m_paPropertiesToEdit->nCount);
  }

  for (int nCurrentObject = 0; nCurrentObject < m_pObjectList->nObjectCount; nCurrentObject++)
  {
    // 
    // 
    UINT uiFirst = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (nCurrentObject+3) );
    for (int nCurrentProperty = 0; nCurrentProperty < m_paPropertiesToEdit->nCount; nCurrentProperty++)
    {
      // First is the first ID of the controls for this property. End is the first control of the next
      // property.
      UINT uCurrID = uiFirst + (nCurrentProperty*4);
      if (!fToControls)
      {
        Copy_CDOPPROPERTIES_OBJECT_PROPERTIES(pObjectProperties, m_pObjectList->ppPropertiesArray[nCurrentObject]);
        GetBOOLFromButton(uCurrID,&(pObjectProperties->ppObjectProperties[nCurrentProperty]->fPresent) );
        switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType)
        {
          case efloat:
            GetfloatFromControl(uCurrID + 1,&(pObjectProperties->ppObjectProperties[nCurrentProperty]->uPropValue.fltValue));
            break;
          case eint:    
            GetintFromControl(uCurrID + 1,&(pObjectProperties->ppObjectProperties[nCurrentProperty]->uPropValue.nValue));
            break;
          case eLPSTR:  
            GetLPSTRFromControl(uCurrID + 1,(pObjectProperties->ppObjectProperties[nCurrentProperty]->uPropValue.szValue));
            break;
          case eBOOL:   
            GetBOOLFromControl(uCurrID + 1,&(pObjectProperties->ppObjectProperties[nCurrentProperty]->uPropValue.fValue));
            break;
          case eVector3D:
            GetVector3DFromControl(uCurrID + 1,&(pObjectProperties->ppObjectProperties[nCurrentProperty]->uPropValue.v3dValues.x),
                                             &(pObjectProperties->ppObjectProperties[nCurrentProperty]->uPropValue.v3dValues.y),
                                             &(pObjectProperties->ppObjectProperties[nCurrentProperty]->uPropValue.v3dValues.z));
            break;
        } // switch on property type
        BOOL fDirty = !IsEqual_CDOPPROPERTIES_OBJECT_PROPERTIES(m_pObjectList->ppPropertiesArray[nCurrentObject], pObjectProperties);
      //
    
        if(  fDirty && !m_pObjectList->ppPropertiesArray[nCurrentObject]->fReadOnly)
        {
          m_fDirty = TRUE;
          Copy_CDOPPROPERTIES_OBJECT_PROPERTIES(m_pObjectList->ppPropertiesArray[nCurrentObject], pObjectProperties);
          m_pObjectList->ppPropertiesArray[nCurrentObject]->fDirty = TRUE;
        }
      } // if !fFromControls
      else
      {
        SetBOOLToButton(uCurrID,(m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->fPresent) );
        switch (m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType)
        {
          case efloat:
            SetfloatToControl(uCurrID + 1,(m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->uPropValue.fltValue));
            break;
          case eint:    
            SetintToControl(uCurrID + 1,(m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->uPropValue.nValue));
            break;
          case eLPSTR:  
            SetLPSTRToControl(uCurrID + 1,(m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->uPropValue.szValue));
            break;
          case eBOOL:   
            SetBOOLToControl(uCurrID + 1,(m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->uPropValue.fValue));
            break;
          case eVector3D:
            SetVector3DToControl(uCurrID + 1,(m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->uPropValue.v3dValues.x),
                                             (m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->uPropValue.v3dValues.y),
                                             (m_pObjectList->ppPropertiesArray[nCurrentObject]->ppObjectProperties[nCurrentProperty]->uPropValue.v3dValues.z));
            break;
        } // switch on property type
      }
    } // for loop for property list
  } // nCurrentObject loop
  free(pObjectProperties);
  return TRUE;
}


#define TooSmall(M_fltTemp) ((M_fltTemp >= -0.00001f) && (M_fltTemp <= 0.00001f))

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::AlterAllProperties(CDOPMATHTYPE eMathType)
{
  //
  // Run through the list of properties, and read in the value from the controls at the top
  // if the control value is non-zero, call the function for altering all the object values
  // for that property.
  //
  float fltTemp;        
  int nTemp;            
  float fltTempArray[3];
  
  UINT uiFirst = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (1));
  for (int nCurrentProperty = 0; nCurrentProperty < m_paPropertiesToEdit->nCount; nCurrentProperty++)
  {
    CDOPVARTYPE eVarType = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType;
    switch (eVarType)
    {
    case efloat:
      fltTemp = 0.0f;        
      GetfloatFromControl(uiFirst + (nCurrentProperty*4 + 1), &fltTemp);
      if (TooSmall(fltTemp))
      {
        continue;
      }
      AlterProperty(nCurrentProperty, (void *)&fltTemp, eMathType);
      break;
    case eint:
      GetintFromControl(uiFirst + (nCurrentProperty*4 + 1), &nTemp);
      nTemp = 0;;            
      if (nTemp == 0)
      {
        continue;
      }
      AlterProperty(nCurrentProperty, (void *)&nTemp, eMathType);
      break;
    case eVector3D:
      fltTempArray[0] = 0.0f; fltTempArray[1] = 0.0f; fltTempArray[2] = 0.0f;
      if (TooSmall(fltTempArray[0]) && TooSmall(fltTempArray[1]) && TooSmall(fltTempArray[2]))
      {
        continue;
      }
      GetVector3DFromControl(uiFirst + (nCurrentProperty*4 + 1), &(fltTempArray[0]), &(fltTempArray[1]), &(fltTempArray[2]));
      AlterProperty(nCurrentProperty, (void *)&(fltTempArray[0]), eMathType);
      break;
    }
  }

}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::DoTheMath(float * pValue, float * pModifier, CDOPMATHTYPE eMath)
{
  if (TooSmall(*pModifier))
  {
    return;
  }
  switch (eMath)
  {
  case ePlus:
    *pValue = *pValue + *pModifier;
    break;
  case eMinus:
    *pValue = *pValue - *pModifier;
    break;
  case eMultiply:
    *pValue = *pValue * *pModifier;
    break;
  case eDivide:
    *pValue = *pValue / *pModifier;
    break;
  }

}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::DoTheMath(int * pValue, int * pModifier, CDOPMATHTYPE eMath)
{
  if (!*pModifier)
  {
    return;
  }
  switch (eMath)
  {
  case ePlus:
    *pValue = *pValue + *pModifier;
    break;
  case eMinus:
    *pValue = *pValue - *pModifier;
    break;
  case eMultiply:
    *pValue = *pValue * *pModifier;
    break;
  case eDivide:
    *pValue = *pValue / *pModifier;
    break;
  }

}
//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::AlterProperty(int nProperty, void * pValueToAlterWith, CDOPMATHTYPE eMath)
{
  //
  //
  //
  for (int nCurrentObject = 0; nCurrentObject < m_pObjectList->nObjectCount; nCurrentObject++)
  {
    // 
    // 
    UINT uiOfControl = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (nCurrentObject+3) ) ;
    CButton * pWnd = (CButton *)GetDescendantWindow(uiOfControl - 1);
    uiOfControl += 1 + (nProperty*4);
    //
    // If the apply button isn't checked this object should be skipped
    //
    if (pWnd && !pWnd->GetCheck())
      continue;

    CDOPVARTYPE eVarType = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nProperty]->ePropertyType;
    switch (eVarType)
    {
    case efloat:
      {
        float fltValue;
        GetfloatFromControl(uiOfControl, &fltValue);
        DoTheMath(&fltValue, (float *)pValueToAlterWith, eMath);
        SetfloatToControl(uiOfControl, fltValue);
      }
      break;
    case eint:
      {
        int nValue;
        GetintFromControl(uiOfControl, &nValue);
        DoTheMath(&nValue, (int *)pValueToAlterWith, eMath);
        SetintToControl(uiOfControl, nValue);
      }
      break;
    case eVector3D:
      {
        for (int i = 0; i<3; i++)
        {
          float fltValue;
          GetfloatFromControl(uiOfControl + i, &fltValue);
          DoTheMath(&fltValue, (float *)&(((float *)pValueToAlterWith)[i]), eMath);
          SetfloatToControl(uiOfControl + i, fltValue);
        }
      }
      break;

    }
  }
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnValuePlus() 
{
	AlterAllProperties(ePlus);
  ClearModifierCells();
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnValueMultiply() 
{
	AlterAllProperties(eMultiply);
  ClearModifierCells();
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnValueMinus() 
{
	AlterAllProperties(eMinus);
  ClearModifierCells();
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnValueDivide() 
{
	AlterAllProperties(eDivide);
  ClearModifierCells();
}


//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::ClearModifierCells()
{
  //
  // Since the cells have been used to do math already we want to clear the values to make 
  // sure we don't do an operation twice when not wanting it.
  //
  
  UINT uiFirst = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (1));
  for (int nCurrentProperty = 0; nCurrentProperty < m_paPropertiesToEdit->nCount; nCurrentProperty++)
  {
    CDOPVARTYPE eVarType = m_paPropertiesToEdit->ppProperties_To_Edit_Entry[nCurrentProperty]->ePropertyType;
    switch (eVarType)
    {
    case efloat:
      SetfloatToControl(uiFirst + (nCurrentProperty*4 + 1), 0.0f);
      break;
    case eint:
      SetintToControl(uiFirst + (nCurrentProperty*4 + 1), 0);
      break;
    case eVector3D:
      SetVector3DToControl(uiFirst + (nCurrentProperty*4 + 1), 0.0f, 0.0f, 0.0f);
      break;
    }  // switch eVarType
  } // for properties loop

} // end of ClearModifierCells

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
  int nCurPos = m_nCurScrollPosVert;
	switch(nSBCode)
  {
  case SB_BOTTOM:
    m_nCurScrollPosVert = m_pObjectList->nObjectCount - m_nMaxVisibleVert + 2;
    break;
  case SB_ENDSCROLL:
    m_nCurScrollPosVert = pScrollBar->GetScrollPos();
    break;
  case SB_LINEDOWN:
    m_nCurScrollPosVert ++;
    break;
  case SB_LINEUP:
    m_nCurScrollPosVert --;
    break;
  case SB_PAGEDOWN:
    m_nCurScrollPosVert += m_nMaxVisibleVert;
    break;
  case SB_PAGEUP:
    m_nCurScrollPosVert -= m_nMaxVisibleVert;
    break;
  case SB_THUMBPOSITION:
    m_nCurScrollPosVert = nPos;
    break;
  case SB_THUMBTRACK:
    m_nCurScrollPosVert = nPos;
    break;
  case SB_TOP:
    m_nCurScrollPosVert = 0;
    break;
  }
  m_nCurScrollPosVert = max(0, m_nCurScrollPosVert);
  m_nCurScrollPosVert = min(m_nCurScrollPosVert, m_pObjectList->nObjectCount - m_nMaxVisibleVert + 2);

  pScrollBar->SetScrollPos(m_nCurScrollPosVert, TRUE);
  CWnd * pControlGroupboxArea            = GetDescendantWindow(IDC_DOP_PROPERTIES);
  int nMoveAmount = m_nScrollIncrementVert * (nCurPos - m_nCurScrollPosVert);

  CWnd * pChildWindow = pControlGroupboxArea->GetWindow(GW_CHILD);
  //CRect crControlGroupboxArea;
  //pControlGroupboxArea->GetWindowRect(crControlGroupboxArea);
  CRect crWindowRect;
  
  while (pChildWindow)
  {
    pChildWindow->GetWindowRect(crWindowRect);
    ::MapWindowPoints(NULL, pControlGroupboxArea->GetSafeHwnd(), (LPPOINT) (LPRECT)crWindowRect, 2);
    
//    pControlGroupboxArea->InvalidateRect(crWindowRect, TRUE);
//    pChildWindow->InvalidateRect(NULL, TRUE);
    crWindowRect.OffsetRect(0, nMoveAmount);
//    pControlGroupboxArea->InvalidateRect(crWindowRect, TRUE);

    pChildWindow->SetWindowPos(NULL, crWindowRect.left, crWindowRect.top, 0, 0, 
                 SWP_NOACTIVATE  | SWP_NOOWNERZORDER  | SWP_NOREDRAW  | SWP_NOSIZE  | SWP_NOZORDER );
//    pChildWindow->InvalidateRect(NULL, TRUE);
    pChildWindow = pChildWindow->GetWindow(GW_HWNDNEXT);
  }
  
/*  
    pControlGroupboxArea->ScrollWindow(0, m_nScrollIncrementVert * (m_nCurScrollPosVert - nCurPos), NULL, NULL);
*/
  CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
  CRect crGroupboxRect;
  pControlGroupboxArea->GetWindowRect(crGroupboxRect);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crGroupboxRect, 2);
//  InvalidateRect(crGroupboxRect, TRUE);
  crGroupboxRect.OffsetRect(-crGroupboxRect.left, -crGroupboxRect.top);
//  pControlGroupboxArea->InvalidateRect(crGroupboxRect, TRUE);

  pControlGroupboxArea->InvalidateRect(NULL, TRUE);
	pControlGroupboxArea->UpdateWindow();
//  UpdateWindow();

}


//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
  int nCurPos = m_nCurScrollPosHorz;
	switch(nSBCode)
  {
  case SB_BOTTOM:
    m_nCurScrollPosHorz = m_paPropertiesToEdit->nCount - m_nMaxVisibleVert + 2;
    break;
  case SB_ENDSCROLL:
    m_nCurScrollPosHorz = pScrollBar->GetScrollPos();
    break;
  case SB_LINEDOWN:
    m_nCurScrollPosHorz ++;
    break;
  case SB_LINEUP:
    m_nCurScrollPosHorz --;
    break;
  case SB_PAGEDOWN:
    m_nCurScrollPosHorz += m_nMaxVisibleHorz;
    break;
  case SB_PAGEUP:
    m_nCurScrollPosHorz -= m_nMaxVisibleHorz;
    break;
  case SB_THUMBPOSITION:
    m_nCurScrollPosHorz = nPos;
    break;
  case SB_THUMBTRACK:
    m_nCurScrollPosHorz = nPos;
    break;
  case SB_TOP:
    m_nCurScrollPosHorz = 0;
    break;
  }
  m_nCurScrollPosHorz = max(0, m_nCurScrollPosHorz);
  m_nCurScrollPosHorz = min(m_nCurScrollPosHorz, m_paPropertiesToEdit->nCount - m_nMaxVisibleHorz + 2);

  pScrollBar->SetScrollPos(m_nCurScrollPosHorz, TRUE);
  CWnd * pControlGroupboxArea            = GetDescendantWindow(IDC_DOP_PROPERTIES);
  int nMoveAmount = m_nScrollIncrementHorz * (nCurPos - m_nCurScrollPosHorz);
  CRect crDisplayableArea;
  pControlGroupboxArea->GetClientRect(crDisplayableArea);


  CRect crWindowRect;
  CWnd * pControlValuePlaceHolder = GetDescendantWindow(IDC_VARIABLE_PRESENT_PLACEHOLDER, FALSE);
  pControlValuePlaceHolder->GetWindowRect(crWindowRect);

  //
  // Set the displayable area to the left of the first "present" check box
  //
  ::MapWindowPoints(NULL, pControlGroupboxArea->GetSafeHwnd(), (LPPOINT) (LPRECT)crWindowRect, 2);
  crDisplayableArea.left = crWindowRect.left - 1;


  CWnd * pChildWindow = pControlGroupboxArea->GetWindow(GW_CHILD);


  while (pChildWindow)
  {
    if (IsHorizontallyScrollable(pChildWindow))
    {
      pChildWindow->GetWindowRect(crWindowRect);
      ::MapWindowPoints(NULL, pControlGroupboxArea->GetSafeHwnd(), (LPPOINT) (LPRECT)crWindowRect, 2);
      crWindowRect.OffsetRect(nMoveAmount, 0);
      pChildWindow->SetWindowPos(NULL, crWindowRect.left, crWindowRect.top, 0, 0, 
                   SWP_NOACTIVATE  | SWP_NOOWNERZORDER  | SWP_NOREDRAW  | SWP_NOSIZE  | SWP_NOZORDER );
      pChildWindow->ShowWindow(IsRectTotallyWithinHorizontally(crWindowRect, crDisplayableArea)  ? SW_SHOW : SW_HIDE);
    }
    pChildWindow = pChildWindow->GetWindow(GW_HWNDNEXT);
  }
  
  //
  // Scroll the text headers and property edit windows at the top.
  //
  int nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * 1;
  for (int nCurrProperty = 0; nCurrProperty < m_paPropertiesToEdit->nCount; nCurrProperty++)
  {
    for (int nCurControl = 1; nCurControl < 4; nCurControl++)
    {
      for (int nRowMultiplier = 0; nRowMultiplier < 2; nRowMultiplier ++)
      {
        pChildWindow = GetDescendantWindow(nCurrID + nCurrProperty * 4 + nCurControl + 
                                          (((m_paPropertiesToEdit->nCount * 4) + 1) * nRowMultiplier), FALSE);
        if (pChildWindow)
        {
          if (IsHorizontallyScrollable(pChildWindow))
          {
            CRect crTestRect;
            pChildWindow->GetWindowRect(crWindowRect);
            crTestRect = crWindowRect;// can't actually move this window relative to a non-parent window
            ::MapWindowPoints(NULL, pControlGroupboxArea->GetSafeHwnd(), (LPPOINT) (LPRECT)crTestRect, 2);
            ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crWindowRect, 2);
            InvalidateRect(crWindowRect, TRUE);
            crWindowRect.OffsetRect(nMoveAmount, 0);
            InvalidateRect(crWindowRect, TRUE);
            crTestRect.OffsetRect(nMoveAmount, 0);
            pChildWindow->SetWindowPos(NULL, crWindowRect.left, crWindowRect.top, 0, 0, 
                         SWP_NOACTIVATE  | SWP_NOOWNERZORDER  | SWP_NOREDRAW  | SWP_NOSIZE  | SWP_NOZORDER );
            pChildWindow->ShowWindow(IsRectTotallyWithinHorizontally(crWindowRect, crDisplayableArea)  ? SW_SHOW : SW_HIDE);

          }
        }
      }
    }
  }




  CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
  CRect crGroupboxRect;
  pControlGroupboxArea->GetWindowRect(crGroupboxRect);
  ::MapWindowPoints(NULL, GetSafeHwnd(), (LPPOINT) (LPRECT)crGroupboxRect, 2);
  crGroupboxRect.OffsetRect(-crGroupboxRect.left, -crGroupboxRect.top);
  pControlGroupboxArea->InvalidateRect(NULL, TRUE);
	pControlGroupboxArea->UpdateWindow();

}


//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsRectTotallyWithinHorizontally(LPRECT pRectToTest, LPRECT pOuterRect) // used for scrolling
{
  // we are going to clip the rectangles to a rectangle bordering the first present box on the left, and the
  // client area of the static control for the other three sides
  // they intersect so lets see if the one rect is totally contained
  if ((min(pRectToTest->left, pOuterRect->left) == pOuterRect->left) &&
      (max(pRectToTest->right, pOuterRect->right) == pOuterRect->right))
  {
      return TRUE;
  }
  return FALSE;
}
//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsHorizontallyScrollable(CWnd * pChildWindow)
{
  int nID = pChildWindow->GetDlgCtrlID();

  //
  // Don't move the name of the object
  //
  if (nID == IDC_STATIC)
    return FALSE;

  //
  // If the id is an even multiplier of 4 off of the base then it is a present control
  //
  int nFirstButton = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * 1;
  int nRowThisButtonWouldBeOn = (nID - nFirstButton) / ((m_paPropertiesToEdit->nCount * 4) + 1);

  if (nID < nFirstButton)
    return FALSE;

  if (nID > IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * (m_pObjectList->nObjectCount+3))
    return FALSE;

  //
  // Don't move the apply button either
  //      The apply button cheats and would actually be told it's one row below the current one by the math above

  nRowThisButtonWouldBeOn--;
  int nFirstButtonOnThisRow = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * (nRowThisButtonWouldBeOn+3);
  
  if ( nID  == (nFirstButtonOnThisRow - 1)) 
        return FALSE;


  return TRUE;

}
//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::OnEraseBkgnd( CDC* pDC )
{
  return CDialog::OnEraseBkgnd(pDC);
}



//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{

  if (IsCreatedControl(wParam))
  {
    //
    //
    //
    OnNotifyCreatedControl(wParam, lParam, pResult);
  }
	return CDialog::OnNotify(wParam, lParam, pResult);
}


//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsCreatedControl(int uID)
{
  int nMinControl = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (2) );
  
  return  (uID >= nMinControl && uID <= m_nLastControlID);
    
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::OnNotifyCreatedControl(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 
{
  if (IsCreatedPresentButton(wParam))
  {
    //
    // Figure out the correct items to enable/disable
    //
    for (int i = wParam + 1; i < wParam + 4; i++)
    {
      CButton * pButton = (CButton *)GetDescendantWindow(i, FALSE);
      BOOL fChecked = pButton->GetCheck() == 1;
      CWnd * pWnd = GetDescendantWindow(i, FALSE);
      if (pButton && pWnd)
      {
        pWnd->EnableWindow(fChecked);
      }
    }
  }
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsCreatedPresentButton(int uID)
{
  //
  // If the id is an even multiplier of 4 off of the base then it is a present control
  //
  int nFirstButton = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * 2;
  int nRowThisButtonWouldBeOn = (uID - nFirstButton) / ((m_paPropertiesToEdit->nCount * 4) + 1);
  int nFirstButtonOnThisRow = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * (nRowThisButtonWouldBeOn+3);

  if ( ((uID - nFirstButtonOnThisRow) % 4 ) == 0) 
        return TRUE;

  return FALSE;


}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::OnCommand(WPARAM wParam, LPARAM lParam) 
{

  if (IsCreatedControl(LOWORD(wParam)))
  {
    //
    //
    //
    OnCommandCreatedControl(wParam, lParam);
  }
  
	return CDialog::OnCommand(wParam, lParam);
}

//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::OnCommandCreatedControl(WPARAM wParam, LPARAM lParam) 
{
  if (IsCreatedPresentButton(LOWORD(wParam)) && HIWORD(wParam) == BN_CLICKED)
  {
    //
    // Figure out the correct items to enable/disable
    //
    CButton * pButton = (CButton *)GetDescendantWindow(LOWORD(wParam), FALSE);
    if (pButton)
    {
      BOOL fChecked = pButton->GetCheck() == 1;
      for (int i = LOWORD(wParam) + 1; i < LOWORD(wParam) + 4; i++)
      {
        CWnd * pWnd = GetDescendantWindow(i, FALSE);
        if (pWnd)
        {
          pWnd->EnableWindow(fChecked);
        }
      }
    }
  }
  else if (IsCreatedApplyButton(LOWORD(wParam)) && HIWORD(wParam) == BN_CLICKED)
  {
    //
    // Get the state we should set the apply all button to
    //
    int nStateForAllButton = GetStateForApplyAllButton();
    CButton * pButton = (CButton *) GetDescendantWindow(IDC_CHECKALL_APPLY_BUTTONS, FALSE);
    if (pButton)
    {
      pButton->SetCheck(nStateForAllButton);
    }
  }
  return TRUE;
}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnFillAllValues() 
{
	// 
  // Run through the row of values and copy the first element into all the rest
  //
  // All of our current uses for this dialog use floats or vector3d's so I currently
  // don't worry about having ints/bools/strings in the row
  //
  float flt[3] = {0.0f, 0.0f, 0.0f};

  //
  // Scroll the text headers and property edit windows at the top.
  //
  int nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * 1;
  int nCurrProperty = 0; // has to start at 0 to get our fill values

  int nCurControl;

  for (nCurControl = 1; nCurControl < 4; nCurControl++) 
  {
    if (!GetfloatFromControl(nCurrID + nCurrProperty * 4 + nCurControl, &(flt[nCurControl -1])))
    {
      flt[nCurControl - 1] = 0.0f;
    }
  }


  //
  // Start loop at one since we got the data from the first one.
  //
  for (nCurrProperty = 1; nCurrProperty < m_paPropertiesToEdit->nCount; nCurrProperty++)
  {
    for (nCurControl = 1; nCurControl < 4; nCurControl++) 
    {
      if (!SetfloatToControl(nCurrID + nCurrProperty * 4 + nCurControl, flt[nCurControl -1]))
      {
        flt[nCurControl - 1] = 0.0f;
      }
    }
  }
  

}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::OnCheckallApplyButtons() 
{
  //
  // If our current state is off, turn all buttons on including this one
  // If our current state is on,  turn all buttons off including this one
  // If our current state is other, turn all buttons off including this one
  //
  // This button state must be updated whenever any of the apply buttons are checked.
  CButton * pButton = (CButton *) GetDescendantWindow(IDC_CHECKALL_APPLY_BUTTONS, FALSE);
  if (pButton)
  {
    int nStateOfCheck = pButton->GetCheck();
    if (nStateOfCheck == 2) // don't act it's not set to anything in particular
    {
      return;
    }
    SetAllApplyButtons(nStateOfCheck);
  }

}


//****************************************************************************************
//
//****************************************************************************************
int CDlgObjectBalance::GetStateForApplyAllButton()
{
  // Iterate through all the apply buttons
  // Then if we have two that don't match we can return indeterminate. Otherwise we return the state
  // of the buttons
  int nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (3) );
  CButton * pButton = NULL;

  pButton = (CButton * ) GetDescendantWindow(nCurrID - 1);
  if (!pButton)
  {
    return 2;
  }
  int nButtonStates = pButton->GetCheck();
  // start at 1 since we already got one
  for (int nCurrentObject = 1; nCurrentObject < m_pObjectList->nObjectCount; nCurrentObject++)
  {
    nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (nCurrentObject + 3) );
    pButton = (CButton * ) GetDescendantWindow(nCurrID - 1);
    if (!pButton)
    {
      return 2;
    }
    if (pButton->IsWindowEnabled())
    {
      if (pButton->GetCheck() != nButtonStates) // something isn't the same
      {
        return 2;
      }
    }
  }
  return nButtonStates;

}

//****************************************************************************************
//
//****************************************************************************************
void CDlgObjectBalance::SetAllApplyButtons(int nState)
{
  // Iterate through all the apply buttons
  // Then if we have two that don't match we can return indeterminate. Otherwise we return the state
  // of the buttons
  CButton * pButton = NULL;
  int nCurrID = IDC_STATIC;
  for (int nCurrentObject = 0; nCurrentObject < m_pObjectList->nObjectCount; nCurrentObject++)
  {
    nCurrID = IDC_VARIABLE_PRESENT_PLACEHOLDER + (((m_paPropertiesToEdit->nCount * 4) + 1) * (nCurrentObject + 3) );
    pButton = (CButton * ) GetDescendantWindow(nCurrID - 1);
    if (!pButton)
    {
      continue; // shouldn't really hit this though
    }
    if (pButton->IsWindowEnabled())
      pButton->SetCheck(nState);
  }
  return;

}



//****************************************************************************************
//
//****************************************************************************************
BOOL CDlgObjectBalance::IsCreatedApplyButton(int uID)
{
  //
  // If the id is an even multiplier of 4 off of the base then it is a present control
  //
  int nFirstButton = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * 2;
  int nRowThisButtonWouldBeOn = (uID - nFirstButton) / ((m_paPropertiesToEdit->nCount * 4) + 1);
  //
  // The apply button actually resides on the row below this one, so these calculations are one off
  //
  nRowThisButtonWouldBeOn ++;
  int nFirstButtonOnThisRow = IDC_VARIABLE_PRESENT_PLACEHOLDER +  ((m_paPropertiesToEdit->nCount * 4) + 1) * (nRowThisButtonWouldBeOn+3);

  if ( uID == (nFirstButtonOnThisRow - 1)) 
        return TRUE;

  return FALSE;


}

