Click here to Skip to main content
15,895,084 members
Articles / Programming Languages / C++

HexEdit - Window Binary File Editor

Rate me:
Please Sign up or sign in to vote.
4.96/5 (137 votes)
17 Oct 2012MIT45 min read 496.9K   22.4K   321  
Open-source hex editor with powerful binary templates
// DFFDFor.cpp : implementation file
//
// Copyright (c) 2011 by Andrew W. Phillips.
//
// This file is distributed under the MIT license, which basically says
// you can do what you want with it but I take no responsibility for bugs.
// See http://www.opensource.org/licenses/mit-license.php for full details.
//

#include "stdafx.h"
#include "hexedit.h"
#include "HexEditDoc.h"
#include "DataFormatView.h"
#include "DFFDStruct.h"
#include "DFFDUseStruct.h"
#include "DFFDFor.h"
#include "DFFDIf.h"
#include "DFFDSwitch.h"
#include "DFFDJump.h"
#include "DFFDData.h"
#include "DFFDMisc.h"
#include "resource.hm"      // Help IDs
#include "HelpID.hm"            // User defined help IDs

#include <HtmlHelp.h>

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

/////////////////////////////////////////////////////////////////////////////
// CDFFDFor dialog


CDFFDFor::CDFFDFor(CXmlTree::CElt *pp, signed char parent_type, 
				   CWnd* pParent /*=NULL*/)
	: CDialog(CDFFDFor::IDD, pParent), saved_(pp->GetOwner())
{
	modified_ = false;
	first_ = false;                     // Default to first (see SetPosition())

	pelt_ = pp;
	parent_type_ = parent_type;

	saved_.SaveKids(pelt_);
	saved_mod_flag_ = pelt_->GetOwner()->IsModified();
	show_prev_next_ = !pParent->IsKindOf(RUNTIME_CLASS(CDataFormatView)) &&
					  (parent_type == CHexEditDoc::DF_FILE ||
					   parent_type == CHexEditDoc::DF_STRUCT ||
					   parent_type == CHexEditDoc::DF_DEFINE_STRUCT ||
					   parent_type == CHexEditDoc::DF_SWITCH ||
					   parent_type == CHexEditDoc::DF_UNION);

	pos_.x = pos_.y = -30000;
	pcount_menu_ = NULL;
	pstop_menu_ = NULL;

	//{{AFX_DATA_INIT(CDFFDFor)
	comment_ = _T("");
	count_ = _T("");
	elt_name_ = _T("");
	name_ = _T("");
	stop_ = _T("");
	type_name_ = _T("");
	//}}AFX_DATA_INIT
}

CDFFDFor::~CDFFDFor()
{
	if (pcount_menu_ != NULL)
	{
		// Destroy the menu and free the memory
		pcount_menu_->DestroyMenu();
		delete pcount_menu_;
	}
	if (pstop_menu_ != NULL)
	{
		// Destroy the menu and free the memory
		pstop_menu_->DestroyMenu();
		delete pstop_menu_;
	}
}

void CDFFDFor::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDFFDFor)
	DDX_Control(pDX, IDC_DFFD_STOP, ctl_stop_);
	DDX_Control(pDX, IDC_DFFD_COUNT, ctl_count_);
	DDX_Control(pDX, IDC_DFFD_NAME, ctl_name_);
	DDX_Control(pDX, IDC_DFFD_REPLACE, ctl_replace_);
	DDX_Control(pDX, IDC_DFFD_EDIT, ctl_edit_);
	DDX_Control(pDX, ID_DFFD_PREV, ctl_prev_);
	DDX_Control(pDX, ID_DFFD_NEXT, ctl_next_);
	DDX_Text(pDX, IDC_DFFD_COMMENTS, comment_);
	DDX_Text(pDX, IDC_DFFD_COUNT, count_);
	DDX_Text(pDX, IDC_DFFD_ELT, elt_name_);
	DDX_Text(pDX, IDC_DFFD_NAME, name_);
	DDX_Text(pDX, IDC_DFFD_STOP, stop_);
	DDX_Text(pDX, IDC_DFFD_TYPE_NAME, type_name_);
	//}}AFX_DATA_MAP
	DDX_Control(pDX, IDC_DFFD_COUNT_VAR, ctl_count_var_);
	DDX_Control(pDX, IDC_DFFD_STOP_VAR, ctl_stop_var_);
}


BEGIN_MESSAGE_MAP(CDFFDFor, CDialog)
	//{{AFX_MSG_MAP(CDFFDFor)
	ON_BN_CLICKED(ID_DFFD_NEXT, OnNext)
	ON_BN_CLICKED(ID_DFFD_PREV, OnPrev)
	ON_EN_CHANGE(IDC_DFFD_NAME, OnChange)
	ON_BN_CLICKED(IDC_DFFD_EDIT, OnEdit)
	ON_BN_CLICKED(IDC_DFFD_REPLACE, OnReplace)
	ON_EN_CHANGE(IDC_DFFD_COUNT, OnChange)
	ON_EN_CHANGE(IDC_DFFD_STOP, OnChange)
	ON_EN_CHANGE(IDC_DFFD_TYPE_NAME, OnChange)
	ON_EN_CHANGE(IDC_DFFD_COMMENTS, OnChange)
	ON_WM_HELPINFO()
	//}}AFX_MSG_MAP
	ON_WM_CONTEXTMENU()
	ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
	ON_BN_CLICKED(ID_DFFD_HELP, OnHelp)
	ON_BN_CLICKED(IDC_DFFD_COUNT_VAR, OnGetCountVar)
	ON_BN_CLICKED(IDC_DFFD_STOP_VAR, OnGetStopVar)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDFFDFor message handlers

BOOL CDFFDFor::OnInitDialog() 
{
	CDialog::OnInitDialog();
	SetIcon(AfxGetApp()->LoadIcon(IDI_FOR), TRUE);

	// If window position specified
	if (pos_.y != -30000)
	{
		CRect rr;               // Rectangle where we will put the dialog
		GetWindowRect(&rr);

		// Move to where it was when it was last closed
		rr.OffsetRect(pos_.x - rr.left, pos_.y - rr.top);
		(void)::NeedsFix(rr);  // Make sure its visible

		MoveWindow(&rr);
	}

	if (!show_prev_next_)
	{
		// Hide << and >> (previous/next) buttons
		ctl_prev_.ShowWindow(SW_HIDE);
		ctl_next_.ShowWindow(SW_HIDE);
	}
	else
	{
		CXmlTree::CElt ee;
		if (parent_type_ == CHexEditDoc::DF_SWITCH)
			ee = pelt_->GetParent();
		else
			ee = *pelt_;
		ee--;
		ctl_prev_.EnableWindow(!ee.IsEmpty());

		if (parent_type_ == CHexEditDoc::DF_SWITCH)
			ee = pelt_->GetParent();
		else
			ee = *pelt_;
		++ee;
		ctl_next_.EnableWindow(!ee.IsEmpty());
	}

	load_data();

	// Setup menu button that allows insert/replace of struct/for/if/jump/data
	VERIFY(button_menu_.LoadMenu(IDR_DFFD));
	CString strTemp;
	ASSERT((button_menu_.GetMenuString(4, strTemp, MF_BYPOSITION), strTemp == "ButtonReplace"));
	ctl_replace_.m_hMenu = button_menu_.GetSubMenu(4)->GetSafeHmenu();
//    ctl_replace_.SizeToContent();
	ctl_replace_.m_bOSMenu = TRUE;
	ctl_replace_.m_bStayPressed = TRUE;
	ctl_replace_.m_bRightArrow = TRUE;

	// Set up menu that allows the user to choose any valid variable name
	pcount_menu_ = make_var_menu_tree(*pelt_);
	ctl_count_var_.m_hMenu = pcount_menu_->GetSafeHmenu();
	ctl_count_var_.m_bOSMenu = TRUE;
	ctl_count_var_.m_bStayPressed = TRUE;
	ctl_count_var_.m_bRightArrow = TRUE;
	ctl_count_.SetSel(0, -1);           // Select all so a var selection (see OnGetVar) replaces current contents

	pstop_menu_ = make_var_menu_tree(*pelt_, true);
	ctl_stop_var_.m_hMenu = pstop_menu_->GetSafeHmenu();
	ctl_stop_var_.m_bOSMenu = TRUE;
	ctl_stop_var_.m_bStayPressed = TRUE;
	ctl_stop_var_.m_bRightArrow = TRUE;
	ctl_stop_.SetSel(0, -1);            // Select all so a var selection (see OnGetVar) replaces current contents

	return TRUE;
}

static DWORD id_pairs[] = { 
	IDC_DFFD_NAME, HIDC_DFFD_NAME,
	IDC_DFFD_COUNT, HIDC_DFFD_COUNT,
	IDC_DFFD_COUNT_VAR, HIDC_DFFD_COUNT_VAR,
	IDC_DFFD_STOP, HIDC_DFFD_STOP,
	IDC_DFFD_STOP_VAR, HIDC_DFFD_STOP_VAR,
	IDC_DFFD_ELT, HIDC_DFFD_ELT,
	IDC_DFFD_EDIT, HIDC_DFFD_EDIT,
	IDC_DFFD_REPLACE, HIDC_DFFD_REPLACE,
	IDC_DFFD_COMMENTS, HIDC_DFFD_COMMENTS,
	IDC_DFFD_TYPE_NAME, HIDC_DFFD_TYPE_NAME,
	ID_DFFD_PREV, HID_DFFD_PREV,
	ID_DFFD_NEXT, HID_DFFD_NEXT,
	0,0 
};

BOOL CDFFDFor::OnHelpInfo(HELPINFO* pHelpInfo) 
{
	theApp.HtmlHelpWmHelp((HWND)pHelpInfo->hItemHandle, id_pairs);
	return TRUE;
}

void CDFFDFor::OnContextMenu(CWnd* pWnd, CPoint point) 
{
	theApp.HtmlHelpContextMenu(pWnd, id_pairs);
}

void CDFFDFor::OnHelp() 
{
	if (!::HtmlHelp(AfxGetMainWnd()->m_hWnd, theApp.htmlhelp_file_, HH_HELP_CONTEXT, HIDD_DFFD_FOR))
		AfxMessageBox(AFX_IDP_FAILED_TO_LAUNCH_HELP);
}

void CDFFDFor::OnCancel() 
{
	pelt_->DeleteAllChildren();
	saved_.InsertKids(pelt_);
	pelt_->GetOwner()->SetModified(saved_mod_flag_);

	CDialog::OnCancel();
}

void CDFFDFor::OnOK() 
{
	if (modified_)
	{
		if (!check_data())
			return;

		save_data();
	}

	CDialog::OnOK();
}

void CDFFDFor::OnNext() 
{
	// Does the same as OnOK but returns ID_DFFD_NEXT to indicate
	// that the next sibling should now be edited
	if (modified_)
	{
		if (!check_data())
			return;

		save_data();
	}

	// Save position so sibling node's dialog can be put in the same place
	pos_ = update_posn(this);

	CDialog::EndDialog(ID_DFFD_NEXT);
}

void CDFFDFor::OnPrev() 
{
	// Does the same as OnOK but returns ID_DFFD_PREV to indicate
	// that the previous sibling should now be edited
	if (modified_)
	{
		if (!check_data())
			return;

		save_data();
	}

	// Save position so sibling node's dialog can be put in the same place
	pos_ = update_posn(this);

	CDialog::EndDialog(ID_DFFD_PREV);
}

LRESULT CDFFDFor::OnKickIdle(WPARAM, LPARAM lCount)
{
	if (valid_element_)
	{
		ctl_edit_.EnableWindow(TRUE);
		CString ss;
		ctl_replace_.GetWindowText(ss);
		if (ss != "Replace with")
			ctl_replace_.SetWindowText("Replace with");
	}
	else
	{
		ctl_edit_.EnableWindow(FALSE);
		CString ss;
		ctl_replace_.GetWindowText(ss);
		if (ss != "Insert")
			ctl_replace_.SetWindowText("Insert");
	}

	return FALSE;
}

void CDFFDFor::OnChange() 
{
	modified_ = true;
}

// Edit current child element (Edit button)
void CDFFDFor::OnEdit() 
{
	bool new_change = false;            // Were changes made during the edit?
	CXmlTree::CElt ee(pelt_->GetFirstChild());
	ASSERT(valid_element_ && !ee.IsEmpty());

	// Work out where to put the dialog
	pos_ = update_posn(this);
	if (first_) orig_ = pos_;
	CPoint pt = get_posn(this, orig_);

	// Edit the subelement according to its type
	int dlg_ret;                        // Save value returned from dialog in case we need it

	CString elt_type = ee.GetName();
	if (elt_type == "struct")
	{
		// Edit the struct element node
		CDFFDStruct dlg(&ee, CHexEditDoc::DF_FORV, this);
		dlg.SetPosition(pt, orig_);
		dlg_ret = dlg.DoModal();
		if (dlg_ret != IDCANCEL && dlg.IsModified())
			new_change = true;
	}
	else if (elt_type == "use_struct")
	{
		// Edit the struct element node
		CDFFDUseStruct dlg(&ee, CHexEditDoc::DF_FORV, this);
		dlg.SetPosition(pt, orig_);
		dlg_ret = dlg.DoModal();
		if (dlg_ret != IDCANCEL && dlg.IsModified())
			new_change = true;
	}
	else if (elt_type == "for")
	{
		// Edit the for element node
		CDFFDFor dlg(&ee, CHexEditDoc::DF_FORV, this);
		dlg.SetPosition(pt, orig_);
		dlg_ret = dlg.DoModal();
		if (dlg_ret != IDCANCEL && dlg.IsModified())
			new_change = true;
	}
	else if (elt_type == "if")
	{
		// Edit the if element node
		CDFFDIf dlg(&ee, CHexEditDoc::DF_FORV, this);
		dlg.SetPosition(pt, orig_);
		dlg_ret = dlg.DoModal();
		if (dlg_ret != IDCANCEL && dlg.IsModified())
			new_change = true;
	}
	else if (elt_type == "switch")
	{
		// Edit the switch element node
		CDFFDSwitch dlg(&ee, CHexEditDoc::DF_FORV, this);
		dlg.SetPosition(pt, orig_);
		dlg_ret = dlg.DoModal();
		if (dlg_ret != IDCANCEL && dlg.IsModified())
			new_change = true;
	}
	else if (elt_type == "jump")
	{
		// Edit the jump element node
		CDFFDJump dlg(&ee, CHexEditDoc::DF_FORV, this);
		dlg.SetPosition(pt, orig_);
		dlg_ret = dlg.DoModal();
		if (dlg_ret != IDCANCEL && dlg.IsModified())
			new_change = true;
	}
	else if (elt_type == "data")
	{
		// Edit the data element node
		CDFFDData dlg(&ee, CHexEditDoc::DF_FORV, this);
		dlg.SetPosition(pt, orig_);
		dlg_ret = dlg.DoModal();
		if (dlg_ret != IDCANCEL && dlg.IsModified())
			new_change = true;
	}
	else
		ASSERT(0);

	if (new_change)
	{
		modified_ = true;

		// Update the displayed name of the sub-element
		UpdateData(TRUE);
		elt_name_ = get_name(ee);
		UpdateData(FALSE);

		// Fix the "stop" variable menu since the "this" subelements may have changed
		pstop_menu_->DestroyMenu();
		delete pstop_menu_;
		pstop_menu_ = make_var_menu_tree(*pelt_, true);
		ctl_stop_var_.m_hMenu = pstop_menu_->GetSafeHmenu();
	}
}

void CDFFDFor::OnReplace() 
{
	if (ctl_replace_.m_nMenuResult == 0)
		return;

	// See if we have a subelement to replace (otherwise we just insert)
	CXmlTree::CElt curr_elt(pelt_->GetFirstChild());
	if (!curr_elt.IsEmpty())
	{
		ASSERT(valid_element_);
		CString sub_name = get_name(curr_elt);

		CString mess;
		if (curr_elt.GetFirstChild().IsEmpty())
		{
			mess.Format("This will replace the contents of %s.\n\n"
						"Are you sure you want to do this?", sub_name);
		}
		else
		{
			mess.Format("This will replace the contents of %s\n"
						"and all elements below it.\n\n"
						"Are you sure you want to do this?", sub_name);
		}
		if (AfxMessageBox(mess, MB_YESNO) != IDYES)
			return;

		pelt_->DeleteChild(curr_elt);         // Remove the replaced node from the tree
	}

	// Work out where to put the dialog
	pos_ = update_posn(this);
	if (first_) orig_ = pos_;
	CPoint pt = get_posn(this, orig_);
	int dlg_ret;
	CXmlTree::CElt ee;

	switch (ctl_replace_.m_nMenuResult)
	{
	case ID_DFFD_INSERT_STRUCT:
		ee = pelt_->InsertNewChild("struct", NULL);

		{
			CDFFDStruct dlg(&ee, CHexEditDoc::DF_FORV, this);
			dlg.SetModified();          // Force validation as it is incomplete
			dlg.SetPosition(pt, orig_);
			dlg_ret = dlg.DoModal();
		}
		break;
	case ID_DFFD_INSERT_USE_STRUCT:
		ee = pelt_->InsertNewChild("use_struct", NULL);

		{
			CDFFDUseStruct dlg(&ee, CHexEditDoc::DF_FORV, this);
			dlg.SetModified();          // Force validation as it is incomplete
			dlg.SetPosition(pt, orig_);
			dlg_ret = dlg.DoModal();
		}
		break;
	case ID_DFFD_INSERT_FOR:
		ee = pelt_->InsertNewChild("for", NULL);

		{
			CDFFDFor dlg(&ee, CHexEditDoc::DF_FORV, this);
			dlg.SetModified();          // Force validation as it is incomplete
			dlg.SetPosition(pt, orig_);
			dlg_ret = dlg.DoModal();
		}
		break;
	case ID_DFFD_INSERT_IF:
		ee = pelt_->InsertNewChild("if", NULL);
		ee.SetAttr("test", "true");

		{
			CDFFDIf dlg(&ee, CHexEditDoc::DF_FORV, this);
			dlg.SetModified();          // Force validation as it is incomplete
			dlg.SetPosition(pt, orig_);
			dlg_ret = dlg.DoModal();
		}
		break;
	case ID_DFFD_INSERT_SWITCH:
		ee = pelt_->InsertNewChild("switch", NULL);

		{
			CDFFDSwitch dlg(&ee, CHexEditDoc::DF_FORV, this);
			dlg.SetModified();          // Force validation as it is incomplete
			dlg.SetPosition(pt, orig_);
			dlg_ret = dlg.DoModal();
		}
		break;
	case ID_DFFD_INSERT_JUMP:
		ee = pelt_->InsertNewChild("jump", NULL);
		ee.SetAttr("offset", "0");

		{
			CDFFDJump dlg(&ee, CHexEditDoc::DF_FORV, this);
			dlg.SetModified();          // Force validation as it is incomplete
			dlg.SetPosition(pt, orig_);
			dlg_ret = dlg.DoModal();
		}
		break;
	case ID_DFFD_INSERT_DATA:
		ee = pelt_->InsertNewChild("data", NULL);
		ee.SetAttr("type", "none");

		{
			CDFFDData dlg(&ee, CHexEditDoc::DF_FORV, this);
			dlg.SetModified();          // Force validation as it is incomplete
			dlg.SetPosition(pt, orig_);
			dlg_ret = dlg.DoModal();
		}
		break;
	default:
		ASSERT(0);
	}

	if (dlg_ret == IDCANCEL)
	{
		// Restore elts again (remove new element, add back old elt)
		(void)pelt_->DeleteChild(ee);
		if (!curr_elt.IsEmpty())
		{
			ASSERT(valid_element_);
			pelt_->InsertChild(curr_elt, NULL);
		}
		return;
	}

	modified_ = true;
	valid_element_ = true;              // Keep not that we now have a sub-element

	// Update the displayed name of the sub-element
	UpdateData(TRUE);
	elt_name_ = get_name(ee);
	UpdateData(FALSE);

	// Fix the "stop" variable menu since the "this" subelements may have changed
	pstop_menu_->DestroyMenu();
	delete pstop_menu_;
	pstop_menu_ = make_var_menu_tree(*pelt_, true);
	ctl_stop_var_.m_hMenu = pstop_menu_->GetSafeHmenu();
}

void CDFFDFor::OnGetCountVar() 
{
	if (ctl_count_var_.m_nMenuResult != 0)
	{
		CString strVar = get_menu_text(pcount_menu_, ctl_count_var_.m_nMenuResult);
		ASSERT(!strVar.IsEmpty());      // The selected menu item should be found in the menu tree

		// Put the var name into the "count" control
//        CString ss;
//        int start, end;
//        ctl_count_.GetWindowText(ss);
//        ctl_count_.GetSel(start, end);
//        ASSERT(start <= end);
//        ss.Delete(start, end-start);
//        ss.Insert(start, strVar);
//        end = start + strVar.GetLength();
//        ctl_count_.SetWindowText(ss);
//        ctl_count_.SetSel(start, end);
		for (int ii = 0; ii < strVar.GetLength (); ii++)
			ctl_count_.SendMessage(WM_CHAR, (TCHAR)strVar[ii]);
		ctl_count_.SetFocus();
		modified_ = true;
	}
}

void CDFFDFor::OnGetStopVar() 
{
	if (ctl_stop_var_.m_nMenuResult != 0)
	{
		CString strVar = get_menu_text(pstop_menu_, ctl_stop_var_.m_nMenuResult);
		ASSERT(!strVar.IsEmpty());      // The selected menu item should be found in the menu tree

		// Put the var name into the "stop" control
		for (int ii = 0; ii < strVar.GetLength (); ii++)
			ctl_stop_.SendMessage(WM_CHAR, (TCHAR)strVar[ii]);
		ctl_stop_.SetFocus();
		modified_ = true;
	}
}

void CDFFDFor::load_data()
{
	// Get values from XML data node
	ASSERT(pelt_ != NULL && pelt_->GetName() == "for");
	name_ = pelt_->GetAttr("name");

	count_ = pelt_->GetAttr("count");
	stop_ = pelt_->GetAttr("stop_test");

	CXmlTree::CElt ee(pelt_->GetFirstChild());
	elt_name_.Empty();                   // Clear the name of the contained element

	// Check if we have a sub-element (without which a FOR is not valid)
	if (valid_element_ = !ee.IsEmpty())
		elt_name_ = get_name(ee);       // Show sub-element name

	type_name_ = pelt_->GetAttr("type_name");
	comment_ = pelt_->GetAttr("comment");
	UpdateData(FALSE);
}

bool CDFFDFor::check_data()
{
	// Get values from the controls and validate them
	UpdateData();

	if (!valid_element_)
	{
		AfxMessageBox("Please add the contained element of the FOR.\n");
		ctl_replace_.SetFocus();
		return false;
	}

	// Make sure name is given unless the container is a FOR (whence sub-elements are accessed via array index)
	if (name_.IsEmpty() && parent_type_ != CHexEditDoc::DF_FORV && parent_type_ != CHexEditDoc::DF_FORF)
	{
		AfxMessageBox("Please enter a name for this element");
		ctl_name_.SetFocus();
		return false;
	}

	if (!name_.IsEmpty() && !valid_id(name_))
	{
		AfxMessageBox("Invalid name.  Please use alphanumeric characters (or\r\n"
					  "underscore) and start with a alphabetic character.");
		ctl_name_.SetFocus();
		return false;
	}

	if (name_.CompareNoCase("true") == 0 ||
		name_.CompareNoCase("false") == 0 ||
		name_.CompareNoCase("end") == 0 ||
		expr_eval::func_token(name_) != expr_eval::TOK_NONE)
	{
		AfxMessageBox("This name is reserved");
		ctl_name_.SetFocus();
		return false;
	}

	// We can only have an empty name if parent is FOR in which case there are no siblings
	ASSERT(!name_.IsEmpty() || pelt_->GetParent().GetNumChildren() == 1);

	// Check that the name is not the same as any siblings
	CXmlTree::CElt ee;

	// First find the actual element whose siblings we want to check (an IF takes the name of its child)
	for (ee = *pelt_; ee.GetParent().GetName() == "if"; ee = ee.GetParent())
		; // empty loop body

	// Check that older siblings don't have the same name
	for (ee--; !ee.IsEmpty(); ee--)
	{
		CXmlTree::CElt ee2;
		for (ee2 = ee; !ee2.IsEmpty() && ee2.GetName() == "if"; ee2 = ee2.GetFirstChild())
			; // nothing here
		if (!ee2.IsEmpty() && ee2.GetAttr("name") == name_)
		{
			AfxMessageBox("This element has a sibling with the same name");
			ctl_name_.SetFocus();
			return false;
		}
	}

	// First find the actual element whose siblings we want to check (an IF takes the name of its child)
	for (ee = *pelt_; ee.GetParent().GetName() == "if"; ee = ee.GetParent())
		; // empty loop body

	// Check that younger siblings don't have the same name
	for (++ee; !ee.IsEmpty(); ++ee)
	{
		CXmlTree::CElt ee2;
		for (ee2 = ee; !ee2.IsEmpty() && ee2.GetName() == "if"; ee2 = ee2.GetFirstChild())
			; // nothing here
		if (!ee2.IsEmpty() && ee2.GetAttr("name") == name_)
		{
			AfxMessageBox("This element has a sibling with the same name");
			ctl_name_.SetFocus();
			return false;
		}
	}

	// xxx TBD check that stop_ and count_ are empty or valid expressions

	return true;
}

void CDFFDFor::save_data()
{
	// Save data to XML element node
	pelt_->SetAttr("name", name_);

	pelt_->SetAttr("count", count_);
	pelt_->SetAttr("stop_test", stop_);

	pelt_->SetAttr("type_name", type_name_);
	pelt_->SetAttr("comment", comment_);
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Australia Australia
Andrew has a BSc (1983) from Sydney University in Computer Science and Mathematics. Andrew began programming professionally in C in 1984 and has since used many languages but mainly C, C++, and C#.

Andrew has a particular interest in STL, .Net, and Agile Development. He has written articles on STL for technical journals such as the C/C++ User's Journal.

In 1997 Andrew began using MFC and released the source code for a Windows binary file editor called HexEdit, which was downloaded more than 1 million times. From 2001 there was a shareware version of HexEdit (later called HexEdit Pro). HexEdit has been updated to uses the new MFC (based on BCG) and is once more open source.

Comments and Discussions