/*********************************************************************************************
 * Copyright 2021 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: ctlXMLEdit.cs $     $Revision: 36 $
 * $Author: Jsj $   $Date: 7/06/06 9:32a $
 *
 * $History: ctlXMLEdit.cs $
 * 
 * *****************  Version 36  *****************
 * User: Jsj          Date: 7/06/06    Time: 9:32a
 * Updated in $/LibSource/ctlXMLEditLib
 * needed full path to read ROAPP.INI
 * 
 * *****************  Version 35  *****************
 * User: Jsj          Date: 5/03/05    Time: 11:44a
 * Updated in $/LibSource/ctlXMLEditLib
 * 
 * *****************  Version 34  *****************
 * User: Jsj          Date: 7/02/03    Time: 9:26a
 * Updated in $/LibSource/ctlXMLEditLib
 * made messagebox calls modal by removing the parent window reference
 * 
 * *****************  Version 33  *****************
 * User: Kathy        Date: 6/11/03    Time: 2:01p
 * Updated in $/LibSource/ctlXMLEditLib
 * Bug fix B2003-045 (save date as part of graphic)
 * 
 * *****************  Version 32  *****************
 * User: Kathy        Date: 5/21/03    Time: 12:41p
 * Updated in $/LibSource/ctlXMLEditLib
 * B2003-036: save of group data to ro if both have a common field
 * 
 * *****************  Version 31  *****************
 * User: Kathy        Date: 2/07/03    Time: 11:19a
 * Updated in $/LibSource/ctlXMLEditLib
 * Error on pathname compares if long filename
 * 
 * *****************  Version 30  *****************
 * User: Kathy        Date: 2/03/03    Time: 10:36a
 * Updated in $/LibSource/ctlXMLEditLib
 * UI Improve
 * 
 * *****************  Version 29  *****************
 * User: Kathy        Date: 1/22/03    Time: 12:17p
 * Updated in $/LibSource/ctlXMLEditLib
 * duplicate/save as bug
 * 
 * *****************  Version 28  *****************
 * User: Kathy        Date: 1/15/03    Time: 1:56p
 * Updated in $/LibSource/ctlXMLEditLib
 * sizing of labels & form
 * 
 * *****************  Version 27  *****************
 * User: Kathy        Date: 1/08/03    Time: 10:05a
 * Updated in $/LibSource/ctlXMLEditLib
 * Add view image
 * 
 * *****************  Version 26  *****************
 * User: Jsj          Date: 1/06/03    Time: 11:29a
 * Updated in $/LibSource/ctlXMLEditLib
 * INI file logic
 * 
 * *****************  Version 25  *****************
 * User: Jsj          Date: 1/02/03    Time: 3:47p
 * Updated in $/LibSource/ctlXMLEditLib
 * Added logic to check INI files for default graphics file extension
 * 
 * *****************  Version 24  *****************
 * User: Jsj          Date: 12/17/02   Time: 4:51p
 * Updated in $/LibSource/ctlXMLEditLib
 * fixed date/time for graphic files
 * 
 * *****************  Version 23  *****************
 * User: Kathy        Date: 12/16/02   Time: 12:11p
 * Updated in $/LibSource/ctlXMLEditLib
 * once data is saved, don't flag it as changed
 * 
 * *****************  Version 22  *****************
 * User: Kathy        Date: 12/13/02   Time: 10:05a
 * Updated in $/LibSource/ctlXMLEditLib
 * bugs with combo (& more than one field with data)
 * 
 * *****************  Version 21  *****************
 * User: Jsj          Date: 12/10/02   Time: 3:35p
 * Updated in $/LibSource/ctlXMLEditLib
 * Default Graphic file extension fix
 * 
 * *****************  Version 20  *****************
 * User: Kathy        Date: 12/10/02   Time: 2:24p
 * Updated in $/LibSource/ctlXMLEditLib
 * fieldname special chars
 * 
 * *****************  Version 19  *****************
 * User: Kathy        Date: 12/02/02   Time: 8:28a
 * Updated in $/LibSource/ctlXMLEditLib
 * fieldname replace chars
 * 
 * *****************  Version 18  *****************
 * User: Kathy        Date: 11/26/02   Time: 11:08a
 * Updated in $/LibSource/ctlXMLEditLib
 * fix required bug on combo type & fix improve sizing
 * 
 * *****************  Version 17  *****************
 * User: Kathy        Date: 11/20/02   Time: 11:36a
 * Updated in $/LibSource/ctlXMLEditLib
 * zoom button moved on main form
 * 
 * *****************  Version 16  *****************
 * User: Kathy        Date: 11/19/02   Time: 11:33a
 * Updated in $/LibSource/ctlXMLEditLib
 * zoom tool tip & tab order
 * 
 * *****************  Version 15  *****************
 * User: Kathy        Date: 10/24/02   Time: 11:15a
 * Updated in $/LibSource/ctlXMLEditLib
 * some image file date (still needs fixed)
 * 
 * *****************  Version 14  *****************
 * User: Kathy        Date: 10/15/02   Time: 2:14p
 * Updated in $/LibSource/ctlXMLEditLib
 * required fields bug
 * 
 * *****************  Version 13  *****************
 * User: Kathy        Date: 10/11/02   Time: 11:35a
 * Updated in $/LibSource/ctlXMLEditLib
 * Required fields & restore data
 * 
 * *****************  Version 12  *****************
 * User: Kathy        Date: 10/10/02   Time: 10:43a
 * Updated in $/LibSource/ctlXMLEditLib
 * console.writeline->messagebox
 * 
 * *****************  Version 11  *****************
 * User: Kathy        Date: 10/07/02   Time: 11:26a
 * Updated in $/LibSource/ctlXMLEditLib
 * graphics image file support
 * 
 * *****************  Version 10  *****************
 * User: Kathy        Date: 9/27/02    Time: 1:12p
 * Updated in $/LibSource/ctlXMLEditLib
 * fix digit as first char in fieldname
 * 
 * *****************  Version 9  *****************
 * User: Jsj          Date: 9/26/02    Time: 12:12p
 * Updated in $/LibSource/ctlXMLEditLib
 * removed reference
 * 
 * *****************  Version 8  *****************
 * User: Jsj          Date: 9/26/02    Time: 11:09a
 * Updated in $/LibSource/ctlXMLEditLib
 * added reference to VLNXML
 * 
 * *****************  Version 7  *****************
 * User: Kathy        Date: 9/25/02    Time: 9:54a
 * Updated in $/LibSource/ctlXMLEditLib
 * remove xmlvln
 * 
 * *****************  Version 6  *****************
 * User: Kathy        Date: 9/19/02    Time: 9:59a
 * Updated in $/LibSource/ctlXMLEditLib
 * fix minor bugs
 * 
 * *****************  Version 5  *****************
 * User: Kathy        Date: 9/11/02    Time: 1:13p
 * Updated in $/LibSource/ctlXMLEditLib
 * vlnxml
 * 
 * *****************  Version 4  *****************
 * User: Kathy        Date: 9/10/02    Time: 12:17p
 * Updated in $/LibSource/ctlXMLEditLib
 * modify xpath expression to find field contents
 * 
 * *****************  Version 3  *****************
 * User: Kathy        Date: 9/05/02    Time: 12:41p
 * Updated in $/LibSource/ctlXMLEditLib
 * dev
 * 
 * *****************  Version 2  *****************
 * User: Kathy        Date: 8/30/02    Time: 9:42a
 * Updated in $/LibSource/ctlXMLEditLib
 * dev
 * 
 * *****************  Version 1  *****************
 * User: Jsj          Date: 8/23/02    Time: 3:02p
 * Created in $/LibSource/ctlXMLEditLib
 *********************************************************************************************/
using System;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Xml;
using System.Xml.Schema;
using System.Text;
using RODBInterface;
using Org.Mentalis.Files;
//using IniFileIO;
namespace ctlXMLEditLib
{
	/// 
	/// This windows control takes an input XML Schema and displays windows controls for the
	/// schema elements.
	/// If XML is also passed in, it uses that for the data.
	/// Methods supported are:
	///    ctlXMLEdit(VlnXmlElement myelem, XmlSchema myschema)
	///			Constructor - VlnXmlElement contains data for edit and contains top
	///						- document node if creating new xml data.
	///						- XmlSchema contains field definitions
	///	   bool SaveData 
	///						- saves data in windows into passed in VlnXmlElement. 
	///						-    returns true if successful, false if failed.
	///	  
	///    
	/// 
	public class ctlXMLEdit : System.Windows.Forms.UserControl
	{
		/// 
		/// Required designer variable.
		/// 
		private System.ComponentModel.Container components = null;
		private Random random = new Random();
		private XmlSchema myXmlSchema;
		private int screenx;
		private int screeny;
		private int tabindx;
		public bool mysavexml;
		private Hashtable myHT;
		private bool dosaveflag;
		private VlnXmlElement editelem;
		private XmlDocument editdoc;
		private TextBox zoomtextbox;
		// the following is used for handling images. Note that if we want to have more
		// than one image defined in an ro, this should be become a list.
		private TextBox GraphicsFiletextbox;
		private TextBox GraphicsWdtextbox;
		private TextBox GraphicsHttextbox;
		private System.Windows.Forms.Button btnFindFile;
		private string GraphicsText;
		private ToolTip zmtooltip;
		private string DefaultGraphicFileExtension;
		int MaxWidth; // largest textbox/groupbox width to size control.
		int himage, wimage;
		int hmin, hmax;
		int wmin, wmax;
		int MAXLT = 60;  // make these defines
		int MAXWT = 84;
		int STDCPI = 12;
		int STDLPI = 6;
		int DPPL = (720/6);
		int DPPC = (720/12);
		int height, width;
		/** C2021-026 the following are use for Parent Child RO fields **/
		public ArrayList FieldsWithApplic = null;
		public string[] PCChildren;
		private GroupBox pcGrpBox = null;
		/** end C2021-026 **/
		// use this struct to define attributes for the text box fields, storing
		// the pattern, radio button association, required field flag, etc.
		struct TextBoxAttrTag
		{
			bool required;		//whether the field is required
			string req_msg;		//message to print if required, but not set
			string pattern;		//pattern for validation
			RadioButton radio;	// if this text box has associated radio button
			bool imagechild;	//whether this field is a subchild of an image
			string imagename;	//if subchild of image, name of image parent (for save)
			string imagedate;   //if this was filename, save the date/time stamp
			string name;        //name of element
			string parenthtid;  //name of parent element in hashtable for PC items
			public TextBoxAttrTag(bool reqd, string ptn, RadioButton rd, bool img, 
				string imgname, string imgdate, string elemname) 
			{
				this.req_msg = null;
				this.required = reqd;
				this.pattern = ptn;
				this.radio = rd;
				this.imagechild = img;
				this.imagename = imgname;
				this.imagedate = imgdate;
				this.name = elemname;
				if (name.Contains("_PCCHILD"))
					this.parenthtid = name.Substring(0, name.IndexOf("_PCCHILD"));
				else
					this.parenthtid = null;
			}
			public void SetPattern(string pattern) {this.pattern = pattern;}
			public void SetRequired(bool req) {this.required = req;}
			public void SetRequiredMessage(string ireq_msg) {this.req_msg = ireq_msg;}
			public string GetPattern { get {return pattern;}}
			public string GetRequiredMessage { get {return req_msg;}}
			public bool GetRequired { get {return required;}}
			public RadioButton GetRadio { get {return radio;}}
			public bool GetImageChild { get {return imagechild;}}
			public string GetImageName { get {return imagename;}}
			public string GetImageDate { get {return imagedate;}}
			public void SetImageDate(string imgdate) {this.imagedate = imgdate;}
			public string GetName { get { return name; } }
			public void SetName(string elemname) { this.name = elemname; }
			public string GetParentHTId { get { return parenthtid; } }
			public void SetParentHTId(string id) { this.parenthtid = id; }
		}
		public ctlXMLEdit(VlnXmlElement myelem, XmlSchema myschema, ArrayList reqfields, ArrayList fldsWithApplic, string [] pckids)
		{
			// This call is required by the Windows.Forms Form Designer.
			InitializeComponent();
			FieldsWithApplic = fldsWithApplic;
			PCChildren = pckids; //C2021-026 list of Parent/Child children
			zmtooltip = new ToolTip();
			myHT = new Hashtable();
			tabindx=0;
			screenx=8;
			screeny=10;
			MaxWidth = 0;
			mysavexml = false;
			dosaveflag = false;
			editelem = myelem;
			editdoc = myelem.OwnerDocument;
			myXmlSchema = myschema;
			GraphicsText = null;
			GraphicsFiletextbox = null;
//			DefaultGraphicFileExtension = ".TIF"; // default graphics file extension
			string ApplicationPath = Application.StartupPath;
			string ROINI = Directory.GetCurrentDirectory()+ "\\ROAPP.INI";
			// Get the default file extension for graphic files
			//			DefaultGraphicFileExtension = TheIniFile.GetINIKeyValueStr("ROApp","Extention","",5,"ROAPP.INI");
			// Bug fix: B2006-025
			//  needed to pass the full path of ROAPP.INI
			// B2025-007 only use the default file location (remove 16 bit PROMS location)
			IniReader in1 = new IniReader(ROINI);
			DefaultGraphicFileExtension = in1.ReadString("ROApp", "Extention");
			if (string.IsNullOrEmpty(DefaultGraphicFileExtension)) DefaultGraphicFileExtension = in1.ReadString("ROApp", "Extension", ".TIF");
			//			if (File.Exists(PromsINI))
			//				DefaultGraphicFileExtension = TheIniFile.GetINIKeyValueStr("Graphics","defaultext",".TIF",5,PromsINI);
			//			if (File.Exists("ROAPP.INI"))
			//				DefaultGraphicFileExtension = TheIniFile.GetINIKeyValueStr("ROApp","Extention",".TIF",5,"ROAPP.INI");
			if (!DefaultGraphicFileExtension.StartsWith("."))
				DefaultGraphicFileExtension = "." + DefaultGraphicFileExtension;
			// Put out window fields based on schema fields.
			DisplaySchemaControls(myXmlSchema);
			// Add field data to fields.
			DisplayFieldContents((XmlNode) myelem);
			// Set required fields based on list. (can add message later).
			SetRequiredFields(reqfields);
			dosaveflag=true;
		}
		// set the required fields tag on those fields which are included in the passed
		// in required fields list.
		private void DoSet(TextBox hwnd, string msg)
		{
			if (hwnd == null) return;
			if (hwnd.Tag != null)
			{
				TextBoxAttrTag tag = (TextBoxAttrTag) hwnd.Tag;
				tag.SetRequired(true);
				tag.SetRequiredMessage(msg);
				hwnd.Tag = tag;
			}
		}
		private void SetRequiredFields(ArrayList reqfields)
		{
			Object o;
			TextBox hwnd;
			string field, msg;
			int indx;
			foreach (string strfld in reqfields)
			{
				indx = strfld.IndexOf("\t");   // message is stored after field, tab delim
				field = strfld.Substring(0,indx);
				msg = strfld.Substring(indx,strfld.Length-indx);
				o = null;
				o = myHT[field];
				if (o == null)
				{
					// there may be an a,b,c,d tacked on to represent combo types.
					// try them. if we have a combo type, then set them all as required
					// because the checker will only check the one that is visible.
					o = myHT[field+"a"];
					if (o != null)   // set all combo types required, the checker
					{
						DoSet((TextBox)o,msg);
						DoSet((TextBox)myHT[field+"b"],msg);
						DoSet((TextBox)myHT[field+"c"],msg);
						DoSet((TextBox)myHT[field+"d"],msg);
					}
				}
				else if (o != null)
				{
					hwnd = (TextBox) o;
					if (hwnd.Tag != null)
					{
						TextBoxAttrTag tag = (TextBoxAttrTag) hwnd.Tag;
						tag.SetRequired(true);
						tag.SetRequiredMessage(msg);
						hwnd.Tag = tag;
					}
				}
			}
		}
		
		// C2021-026 If this is a Parent/Child field that has no value saved in the database
		//           then use the parent's value and display it as grey colored text.
		private void GetDefaultParentValue(TextBox tb, XmlNode node, string chldName)
		{
			XmlNode parentNode = null;
			// PCChildren contains a list strings reprenting the Children setup in the current Working Draft Parent/Child property
			if (PCChildren == null) return; // C2021-026 no children to process
			int pcChildIdx = 0; // C2021-026 corresponds with DocVersionConfig.SelectedSlave value: 0 = no child selected 1 = first child ...
			foreach (string c in PCChildren)
			{
				pcChildIdx++;
				string csufx = string.Format("_PCCHILD{0}", pcChildIdx); // C2021-026 create a child field name
				if (chldName.EndsWith(csufx))
				{
					int idx = chldName.LastIndexOf(csufx);
					if (idx > 0)
					{
						string parent = chldName.Remove(idx);
						parentNode = node.SelectSingleNode(parent);
						// if not found with just the string, search the tree.
						if (parentNode == null) parentNode = node.SelectSingleNode("*/" + parent);
						if (parentNode != null)
						{
							bool orgMySaveXML = mysavexml; 
							tb.Text = parentNode.InnerText; // set the P/C Child text box to the parent's value
							tb.ForeColor = SystemColors.GrayText;
							// B2021-077 Setting tb.Text triggers the need to save.
							//           In this case it's for visual purposes only, so no need to trigger the save
							//           but if mysavexml is already turn, then don't set it to false
							if (!orgMySaveXML)
							{
								mysavexml = false;
								dosaveflag = false;
							}
							break;
						}
					}
				}
			}
		}
		// Given a parent XmlNode, walk through the child nodes & add the text for the
		// node to the associated text box.
		private void DisplayFieldContents(XmlNode node)
		{
			Object o;
			TextBox hwnd;
			XmlNode nd;
			// if we have an image, check it for validity, i.e. file exists, etc.??
			foreach (string str in myHT.Keys)
			{
				o = myHT[str];
				hwnd = (TextBox) o;
				nd = node.SelectSingleNode(str);
				// if not found with just the string, search the tree.
				if (nd==null)nd = node.SelectSingleNode("*/"+str);
				if (nd == null || nd.InnerText.Length==0) // B2024-004 use Parent value if Child text length is zero
					GetDefaultParentValue(hwnd, node, str); // C2021-026 Parent/Child Field has no value so use parent's value
				else
				{	
					hwnd.Text = nd.InnerText;   // set the field's text from XML
					// check if this window has a button name as part of its tag. If
					// so it's a combo type & the radio button/visibility may need to
					// be reset. If this button is not checked (during setup), then
					// check it. By setting it's Checked property to true, an event
					// is thrown which causes the code to be run which turns off/on
					// visibility of the text boxes.
					if (hwnd.Tag != null)
					{
						TextBoxAttrTag tag = (TextBoxAttrTag) hwnd.Tag;
						RadioButton radio;
						radio = tag.GetRadio;
						
						if (radio != null )
						{
							// Use a tag on the groupbox to know that the radio button
							// was set - the first xml element in the group is the
							// active one.
							GroupBox gp = (GroupBox) radio.Parent;
							if (gp.Tag == null && radio.Checked == false)
							{
								// see if this is the first member of the group in the 
								// xml node.
								string nmgr = str.Substring(0,str.Length-1);
								XmlNode grpnd = (XmlNode) editelem.FirstChild;
								bool first = true;
								while (grpnd != null)
								{
									if (grpnd.Name.Substring(0,grpnd.Name.Length-1) == nmgr)
									{
										if (grpnd.Name == str)
										{
											if (first)
											{
												radio.Checked = true;
												radio.Parent.Tag = true;
											}
											break;
										}
										first = false;
									}
									grpnd = grpnd.NextSibling;
								}
							}
						}
						bool img;
						img = tag.GetImageChild;
						if (img==true && str == "Image_Filename")
						{
							int indx = hwnd.Text.IndexOf(" ");
							if (indx>=0)
							{
								tag.SetImageDate(hwnd.Text.Substring(0,indx));
								hwnd.Tag = tag;
								hwnd.Text = hwnd.Text.Substring(indx+1,hwnd.Text.Length-indx-1);
							}
						}
						if (img==true && str == "Image_Width")
							width = System.Convert.ToInt32(hwnd.Text);
						if (img==true && str == "Image_Height")
							height = System.Convert.ToInt32(hwnd.Text);
					}
				}
			}
			// if we're doing an image file & all data has been set, update the
			// height/width data of the image.
			if (GraphicsFiletextbox != null && GraphicsFiletextbox.Text != "") UpdateHtWd();
		}
		public TextBox GetGraphicsFiletextbox()
		{
			return GraphicsFiletextbox;
		}
		public int GetMaxWidth()
		{
			// if MaxWidth is small, a scroll bar appears below the fields
			// so make this a bit larger, so a scroll bar doesn't appear
			if (MaxWidth < 30) return MaxWidth + 100;
			return MaxWidth + 70;
		}
		public int GetMaxLength()
		{
			return screeny+50;
		}
		public void SetNotSaved()
		{
			mysavexml = true;
		}
		/// 
		/// Clean up any resources being used.
		/// 
		protected override void Dispose( bool disposing )
		{
			zmtooltip.RemoveAll();
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );			
		}
		// RestoreData restores the data in the text boxes to be that which was originally 
		// sent into the control (ctlXMLEdit)
		public void RestoreData()
		{
			dosaveflag=false;
			mysavexml=false;
			TextBox hwnd;
			// first, clear out all of the text boxes.
			foreach (string str in myHT.Keys)
			{
				object o = myHT[str];
				hwnd = (TextBox) o;
				hwnd.Text = "";
				TextBoxAttrTag tag = (TextBoxAttrTag) hwnd.Tag;
				RadioButton radio;
				radio = tag.GetRadio;		
				if (radio != null )
				{
					GroupBox gp = (GroupBox) radio.Parent;
					gp.Tag = null;
				}
			}
			// Add field data to fields.
			DisplayFieldContents((XmlNode) editelem);
			dosaveflag=true;
		}
		public bool DataChanged()
		{
			if (mysavexml) return true;
			return false;
		}
		//B2015-089 - restore the inner xml data when user duplicated an RO and tried to save with out giving it a new setpoint ID (Accessory Page ID)
		public void RestoreInnerXml(string savedInnerXml)
		{
			editelem.InnerXml = savedInnerXml;
		}
		//C2021-026 when saving the RO record, if a PC Child's textbox contains a value identical
		//          to the parent's value, then clear the child's textbox so that nothing is saved
		//          to the database.  This allow us to know that a specific value was not set
		//          for this Parent/Child child
		private void RemovePCChildTextIfSameAsParent(XmlNode node, TextBox tb, string chldName)
		{
			XmlNode parentNode = null;
			// if this is a child node get the parent's value
			// PCChildren contains a list strings reprenting the Children setup in the current Working Draft Parent/Child property
			if (PCChildren == null) return;
			int pcChildIdx = 0;
			foreach (string c in PCChildren)
			{
				//string csufx = CvtUserFldToFld(c);
				pcChildIdx++;
				string csufx = string.Format("_PCCHILD{0}", pcChildIdx);
				if (chldName.EndsWith(csufx))
				{
					int idx = chldName.LastIndexOf(csufx);
					if (idx > 0)
					{
						string parent = chldName.Remove(idx);
						parentNode = node.SelectSingleNode(parent);
						// if not found with just the string, search the tree.
						if (parentNode == null) parentNode = node.SelectSingleNode("*/" + parent);
						// C2021-026 if the text is grey it's a child field using the parent's value
						//           we need to clear the child's text box so that it will reset using the new parent's value
						if (parentNode != null && (parentNode.InnerText == tb.Text || tb.ForeColor == SystemColors.GrayText))
						{
							tb.Text = "";
							break;
						}
					}
				}
			}
		}
		// SaveData saves the data in the element which had been sent to the control. Return
		// true if success, false if fail.
		// Note that the Parent and Child XML node variables below are not coding for Parent/Child Applicabily Fields
		public bool SaveData()
		{
			if (mysavexml) 
			{				
				TextBox hwnd;
				TextBoxAttrTag tag;
				string imgdate;
				//go thru the hash table to get textboxes. Find the ones that have text.
				foreach (string str in myHT.Keys)
				{
					object o = myHT[str];
					hwnd = (TextBox) o;
					imgdate = null;
					// if this is a required field and there is no text, put out an error
					// message and get out of here.
					if (hwnd.Tag != null)
					{
						tag = (TextBoxAttrTag) hwnd.Tag;
						if (tag.GetRequired == true && hwnd.Visible == true && (hwnd.Text == null || hwnd.Text == ""))
						{
							string message = CvtFldToUserFld(str) + " must be entered because it is\nused to define the " + tag.GetRequiredMessage;
							string caption = "Error on Save RO Data";
							MessageBoxButtons buttons = MessageBoxButtons.OK;
							DialogResult result;
							result = MessageBox.Show(message, caption, buttons);
							return (false);
						}
						imgdate = tag.GetImageDate;
					}
					
					// find the element in the current element node. 
					// if text exists and the element exists, just reset the innertext. 
					// if text exists and it's a new element, add it.
					// if the element is there and there's no text. delete it.
					XmlNode elmnode = (XmlNode) editelem;
					XmlNode nd = elmnode.SelectSingleNode(str);
					if (nd == null) nd = elmnode.SelectSingleNode("*/"+str);
					RemovePCChildTextIfSameAsParent(elmnode, hwnd, str); // C2021-026 will clear hwnd.Text if child value is same as parent value
					// handle, element exists - but text removed first (delete element)
					if ((hwnd.Text == null || hwnd.Text == "") && nd != null)
					{
						XmlNode parent = nd.ParentNode;
						parent.RemoveChild(nd);
					}
					else
					{
						// found the node, just set text and text exists, just modify text.
						if (nd != null)
						{
							if (str == "Image_Filename")
								nd.InnerText = imgdate + " " + hwnd.Text;
							else
								nd.InnerText = hwnd.Text;
						}
							// node not here. Make a new element
						else if (hwnd.Text != null && hwnd.Text != "")
						{	
							// if this is part of an image, then be sure that the
							// parent image node was created and if not, create it.
							bool ischldimag = false;
							if (hwnd.Tag != null)
							{
								tag = (TextBoxAttrTag) hwnd.Tag;
								if (tag.GetImageChild == true) ischldimag = true;
							}
							if (ischldimag)
							{
								tag = (TextBoxAttrTag) hwnd.Tag;   // yikes, compile errors
								string imagnm = tag.GetImageName;
								VlnXmlElement ielm;
								VlnXmlElement newelem;
								nd = elmnode.SelectSingleNode(imagnm);
								if (nd == null) // make this image node.
								{
									newelem = (VlnXmlElement) editdoc.CreateElement(imagnm);
									editelem.AppendChild((XmlNode) newelem);
									ielm = newelem;
								}
								else
									ielm = (VlnXmlElement) nd;
								newelem = (VlnXmlElement) editdoc.CreateElement(str);
								if (str == "Image_Filename")
									newelem.InnerText = imgdate + " " + hwnd.Text;
								else
									newelem.InnerText = hwnd.Text;
								ielm.AppendChild((XmlNode) newelem);
							}
							else
							{
								// just added at the current level
								VlnXmlElement newelem = (VlnXmlElement) editdoc.CreateElement(str);
								newelem.InnerText = hwnd.Text;
								editelem.AppendChild((XmlNode) newelem);
								nd = (XmlNode) newelem;
							}
						}
						// check on order of combo types if this was one. Note that the 'active'
						// type within the combo, i.e. text, variable, table or xyplot, must
						// be the first one in xml tree (before the others) in order to be the 
						// active one as presented to user. This was done since more than one can have data.
						if (hwnd.Tag != null)
						{
							tag = (TextBoxAttrTag) hwnd.Tag;
							RadioButton rb = tag.GetRadio;
							if (rb != null)
							{
								if (rb.Checked == true)
								{
									// this one needs to be before any others (of the same
									// group type) in the xml childlist - just make it 1st.
									XmlNode parent = nd.ParentNode;
									parent.RemoveChild(nd);
									parent.PrependChild(nd);										
								}
							}
						}
					}
				}
				DisplayFieldContents((XmlNode) editelem); // to refresh display after saving Parent/Child fields to show default values
				// B2021-077 we saved the information. set the need to save flags to false
				mysavexml = false;   // data has been saved, don't allow it again.
				dosaveflag = false;
				return (true);
			}
			MessageBox.Show("Save Failed");
			return (false);
		}
		#region Component Designer generated code
		/// 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// 
		private void InitializeComponent()
		{
			this.btnFindFile = new System.Windows.Forms.Button();
			this.SuspendLayout();
			// 
			// btnFindFile
			// 
			this.btnFindFile.Location = new System.Drawing.Point(608, 8);
			this.btnFindFile.Name = "btnFindFile";
			this.btnFindFile.Size = new System.Drawing.Size(96, 24);
			this.btnFindFile.TabIndex = 1;
			this.btnFindFile.Text = "Find File";
			this.btnFindFile.Visible = false;
			this.btnFindFile.Click += new System.EventHandler(this.btnFindFile_click);
			this.ResumeLayout(false);
			this.Controls.Add(btnFindFile);
		}
		#endregion
	
		// Given a schema, display labels or radio buttons and their associated textboxes
		// Radio button names are derived from removing the type from the element name.
		private void DisplaySchemaControls(XmlSchema myXmlSchema)
		{
			// the first element is the group name, get past this to the data. This group
			// name is a complex type, which is processed below.
			bool first=true;						
			try
			{
				foreach(object item in myXmlSchema.Items)
				{
					if (item is XmlSchemaElement && first) 
					{
						first=false;
					}
					// in the RO schema (and any types derived from it), the fields are
					// defined within the ComplexType. 
					if (item is XmlSchemaComplexType)
						DisplayXmlSchemaComplexType((XmlSchemaComplexType)item);  //complexType
				}
			}
			catch(Exception e)
			{
				MessageBox.Show(e.ToString(),"Error displaying fields from schema");
			}
		}
		//XmlSchemaComplexType
		private void DisplayXmlSchemaComplexType(XmlSchemaComplexType complexType)
		{
			if (complexType.ContentModel == null)
			{
				if (complexType.Particle != null)
					DisplayXmlSchemaParticle(complexType.Particle);
			}
		}
		//XmlSchemaSimpleType processes a simple type schema element which contains a fields
		//  attributes such as patterns, maxLength and whether it is single (type is 
		//  'normalizedString') or a multiline text box(type is 'string'). Also, a radio 
		//  button is passed in if this was called to handle an element within a combo type.
		//  This button is saved for a given text box so that initialization of these combo types can occur.
		private string DisplayXmlSchemaSimpleType(XmlSchemaSimpleType simpleType, TextBox mytextbox, RadioButton radio, 
			bool img, string imgname)
		{
			// set up for text box tag, which stores whether field is required and
			// if a pattern exists for validation.
			string pattern = null;
			string getannot=null;
			if (simpleType.Annotation != null) 
			{
				object item = simpleType.Annotation.Items[0];
				if (item is XmlSchemaDocumentation)
				{
					XmlSchemaDocumentation doc = (XmlSchemaDocumentation) item;
					XmlNode[] node = (XmlNode[]) doc.Markup;
					// Note: this has nothing to do with PROMS Annotations.  This is more of a field type description
					//       used for Graphics (figures) RO fields to setup event handlers and buttons to display a Find File Dialog
					getannot = node[0].InnerText;
					if (getannot == "VLN_FINDFILE") 
					{
						this.btnFindFile.Location = new System.Drawing.Point(mytextbox.Location.X+mytextbox.Width+50,mytextbox.Location.Y-(2*(int)mytextbox.Font.GetHeight()));
						this.btnFindFile.Visible = true;
						GraphicsFiletextbox = mytextbox;
						GraphicsFiletextbox.LostFocus += new System.EventHandler(this.GraphicText_lostfocus);
						getannot = null;
					}
					if (getannot!=null && getannot!="")
					{
						if (getannot.IndexOf("Lines")>=0)
						{
							GraphicsHttextbox = mytextbox;
							mytextbox.LostFocus += new System.EventHandler(this.GraphicsHt_lostfocus);
						}
						if (getannot.IndexOf("Character")>=0)
						{
							GraphicsWdtextbox = mytextbox;
							mytextbox.LostFocus += new System.EventHandler(this.GraphicsWd_lostfocus);
						}
					}
				}
			}
					
			if (simpleType.Content is XmlSchemaSimpleTypeRestriction) 
			{
				// A Schema Type Restriction is used to define single or multi-line boxes.
				XmlSchemaSimpleTypeRestriction myRestrictions = (XmlSchemaSimpleTypeRestriction) simpleType.Content;
				if (myRestrictions.BaseTypeName.Name == "normalizedString") 
					mytextbox.Multiline = false;
				else
				{
					mytextbox.Multiline = true;
					mytextbox.AcceptsReturn = true;
					mytextbox.Height = (int) mytextbox.Font.GetHeight() * 4;
					mytextbox.ScrollBars = ScrollBars.Vertical;
					zmtooltip.SetToolTip(mytextbox, "Press Shift F2 To Zoom");
				}
				// Schema facets were used for maxlength & patterns, process these if found.
				foreach(XmlSchemaFacet facet in myRestrictions.Facets)
				{
					if (facet is XmlSchemaMaxLengthFacet)
					{
						if (mytextbox.Multiline == false)
							mytextbox.MaxLength = System.Convert.ToInt32(facet.Value);
						// if the box is small, make the width a little bigger so maxlength text can fit. 
						int wd = System.Convert.ToInt32(facet.Value);
						if (wd<5) wd++;
						mytextbox.Width = (int) mytextbox.Font.SizeInPoints * wd;
						MaxWidth = (mytextbox.Width>MaxWidth)?mytextbox.Width:MaxWidth;
					}
					else if (facet is XmlSchemaPatternFacet)
					{
						pattern = facet.Value;
					}
				}
			}
			// Now setup a tag for the text box which will store attributes associated with it
			// for seeing if it is a required field, checking a pattern or processing a radio box
			// initialization.
			if (mytextbox.Tag == null)
			{
				TextBoxAttrTag tag = new TextBoxAttrTag(false, pattern, radio, img, (img?imgname:null), null, mytextbox.Name);
				mytextbox.Tag = (object) tag;
			}
			return getannot;
		}
		//XmlSchemaParticle's contain the individual schema elements which represent
		//  fields defined for this given RO group. It may also contain a schema 'choice'
		//  which was used to handle the combo type data type fields.
		private void DisplayXmlSchemaParticle(XmlSchemaParticle particle)
		{
			if (particle is XmlSchemaElement)
				DisplayXmlSchemaElement((XmlSchemaElement)particle);
			else if (particle is XmlSchemaSequence)
			{
				foreach(XmlSchemaParticle particle1 in ((XmlSchemaSequence)particle).Items)
					DisplayXmlSchemaParticle(particle1);
			}
			else if (particle is XmlSchemaChoice)
				DisplayXmlSchemaChoice((XmlSchemaChoice) particle);
			else
				MessageBox.Show(particle.ToString(),"Not Implemented for this type");
		}
		
		// For a combo field, display the radio button & the text field. Select the first 
		// button. If data is also sent in during initialization of this control, i.e. 
		// edit versus new, the selected button will be changed during the XML file
		// data load process.
		// Return value (set as reference params): 'x' & 'y' size of the text field so that 
		//    the caller can determine the max to set the group box x, y size value and know 
		//    how far to move down on the screen.
		private void DisplayXmlSchemaRadioAndTextBox(XmlSchemaElement element, GroupBox gbox, bool first, int x, int y,
			int boxx, ref int retx, ref int rety)
		{
			int ysize;
			RadioButton radio = new RadioButton();
			radio.Location = new System.Drawing.Point(x,20);
			radio.Name = element.Name + element.SchemaTypeName.Name;
			string tmp;
			if (element.SchemaTypeName.Name.Length>1 && char.IsDigit(element.SchemaTypeName.Name,1))
				tmp = element.SchemaTypeName.Name.Substring(1,element.SchemaTypeName.Name.Length-1);
			else
				tmp = element.SchemaTypeName.Name;
			radio.Text = CvtFldToUserFld(tmp);
			
			radio.TabIndex = 0;
			ysize = (int) radio.Height;
			if (first) radio.Checked = true;
			radio.CheckedChanged += new System.EventHandler(this.radiocheckchg);
			gbox.Controls.Add(radio);
			TextBox tb = new TextBox();
			tb.Location = new Point(boxx+20, y+25);
			if (!first) tb.Visible = false;
			myHT.Add(element.Name,tb);
			// add the textbox to the ctlxmledit control rather than groupbox - the
			// groupbox is just to control the choices, the text box won't display if in
			// the groupbox.
			Controls.Add(tb);
			tb.TextChanged += new System.EventHandler(this.textbox_change);
			tb.GotFocus += new System.EventHandler(this.textbox_zoombtn);
			tb.KeyDown += new KeyEventHandler(MyOnKeyDown);
			gbox.Contains(tb);
			// the following will set attributes on the text boxes such as maxlength, multiline, etc.
			DisplayXmlSchemaSimpleType((XmlSchemaSimpleType)element.SchemaType, tb, radio,false,null);
			int chldGrpHeight = 0;
			if (FieldsWithApplic != null && FieldsWithApplic.Contains(gbox.Text) && radio.Text == "Fixed") // C2021-026 user turned appicability on for this field
			{
				int saveScreeny = screeny;
				int saveScreenx = screenx;
				screeny += tb.Height + 50;
				screenx += 20;
				pcGrpBox = null; // C2021-026 used to hide/show Parent/Child group when radio buttons are selected
				DisplayAppicChildrenGroup(element, PCChildren, ref chldGrpHeight, gbox.Text); // C2021-026 make sub-group for Parent/Child  child fields (if needed)
				screeny = saveScreeny;
				screenx = saveScreenx;
			}
			rety = (ysize + 25 + chldGrpHeight + tb.Height);
			retx = tb.Width;
		}
		//XmlSchemaChoice: processes the 'combo data type' which is represented by a
		// a schema choice element. This is handled by a group box and using radio 
		// button/textbox pairs to represent each element within the choice. 
		private void DisplayXmlSchemaChoice(XmlSchemaChoice choice)
		{
			bool first = true;
			int screenposx, screenposy;
			int maxyfortext=0,maxxfortext=0;
			GroupBox groupbox = new GroupBox();
			groupbox.SuspendLayout();
			groupbox.Location = new System.Drawing.Point(screenx,screeny);
			XmlSchemaElement elem = (XmlSchemaElement) choice.Items[0];
			groupbox.Name = elem.Name;
			groupbox.TabStop = false;
			// remove the last character of the name for the title of the group box.
			string tstr = CvtFldToUserFld(elem.Name);
			groupbox.Text = tstr.Remove((tstr.Length)-1,1);
			screenposx = screenx+20;
			screenposy = screeny+20;
			foreach (object o in choice.Items)
			{
				// put out a radio button/text box for each item in the choice.
				int xmax=0, ymax=0;
				DisplayXmlSchemaRadioAndTextBox((XmlSchemaElement) o,groupbox,first,screenposx,screenposy,screenx,ref xmax,ref ymax);
				first = false;
				maxyfortext = ymax>maxyfortext?ymax:maxyfortext;
				maxxfortext = xmax>maxxfortext?xmax:maxxfortext;
				screenposx = screenposx + 160;				
			}
			// get the widths of all children of this group box & use this to set the size 
			// of the groupbox.
			int gritms = groupbox.Controls.Count;
			screeny = screeny + maxyfortext + 20;
			MaxWidth = (maxxfortext+40>MaxWidth)?maxxfortext+40:MaxWidth;
			groupbox.Size = new System.Drawing.Size(maxxfortext+40, maxyfortext+10);
			Controls.Add(groupbox);
		}
		private void DisplayXmlSchemaElement(XmlSchemaElement element)
		{
			string userfldName = CvtFldToUserFld(element.Name);
			string childLabel = "";
			bool pcApplic = FieldsWithApplic != null && FieldsWithApplic.Contains(userfldName); // C2021-026 returns True if field should show Parent/Child Applicability fields
			if (pcApplic) childLabel = CvtFldToUserFld(element.Name);
			int dmy = 0;
			DoDisplayXmlSchemaElement(element, childLabel, 0, ref dmy, Color.Transparent, 0);
			if (pcApplic)
			{
				// C2021-026 this will create a grouping containing Parent/Child Applicability fields
				DisplayAppicChildrenGroup(element, PCChildren, ref dmy, null);
				screeny += 10;
			}
		}
		// C2021-026 Creates a group box containing Parent/Child Applicability fields
		//           the group box is indented and shaded a blue color
		private void DisplayAppicChildrenGroup(XmlSchemaElement element, string [] PCChildren, ref int chldgrphgt, string altGrpName)
		{
			int screenposx, screenposy;
			int savescreenX = screenx;
			screenx += 20;
			GroupBox groupbox = new GroupBox();
			groupbox.BackColor = Color.AliceBlue;
			groupbox.SuspendLayout();
			groupbox.Location = new System.Drawing.Point(screenx, screeny);
			groupbox.Name = "PCgb_" + element.Name;
			groupbox.TabStop = false;
			// remove the last character of the name for the title of the group box.
			string tstr = (altGrpName == null)?CvtFldToUserFld(element.Name):altGrpName;//(elem.Name);
			groupbox.Text = "Child Values";
			screenposx = screenx + 20;
			screenposy = screeny + 20;
			int screenySave = screeny;
			screeny += 20;
			int maxChldWidth = 0;
			int pcChildIdx = 0;
			foreach (string s in PCChildren)
			{
				string childLabel = s;
				pcChildIdx++;
				DoDisplayXmlSchemaElement(element, childLabel, 20, ref maxChldWidth,Color.AliceBlue, pcChildIdx);
			}
			// get the widths and number of all children of this group box & use this to set the size 
			// of the groupbox.
			groupbox.Size = new System.Drawing.Size(Math.Max(105,maxChldWidth + 40), screeny - screenySave + 5); // B2021-079 increased the minimum width
			chldgrphgt = screeny - screenySave + 5;
			Controls.Add(groupbox);
			if (pcGrpBox == null)
				pcGrpBox = groupbox; // only save if is in a Choice (radio button) group - used to hide/unhide the P/C Group box when radio buttons are selected
			screenx = savescreenX;
		}
		private void DoDisplayXmlSchemaElement(XmlSchemaElement element, string applicLabel, int indent, ref int maxwidth, Color chlbckcolor, int pcChildIdx)
		{
			// always add a label.
			int sscreeny;
			string userfldName = CvtFldToUserFld(element.Name);
			string pcChildFldName = element.Name; // C2021-026 PC Child field name
			// C2021-026 the first PC Child has pcChildIdx of one
			if (pcChildIdx > 0)
				pcChildFldName += string.Format("_PCCHILD{0}", pcChildIdx);
			Label mylabel;
			mylabel = new System.Windows.Forms.Label();
			mylabel.BackColor = chlbckcolor; // PC Applic fields are shaded blue otherwise backcolor is transparent
			mylabel.Location = new Point(screenx+indent, screeny);
			mylabel.Name = (pcChildIdx == 0) ? CvtUserFldToFld(element.Name) : CvtUserFldToFld(pcChildFldName); 
			mylabel.Text = (applicLabel.Length > 0) ? applicLabel : CvtFldToUserFld(element.Name).Replace("Image_",""); // C2021-026  applicLabel is the P/C Child name
			mylabel.AutoSize = true;
			Controls.Add(mylabel);
			// add 3 onto screeny so that textbox is slightly below label.
			screeny = screeny + mylabel.Height + 3;
			sscreeny = screeny;   // save 'y' location in case of annotation.
			if (element.SchemaType != null && element.SchemaType is XmlSchemaComplexType)
			{
				GraphicsText = element.Name;
				DisplayXmlSchemaComplexType((XmlSchemaComplexType)element.SchemaType);
				screeny+=30;
			}
			else
			{
				TextBox mytextbox;
				mytextbox = new TextBox();
				mytextbox.Location = new Point(screenx+indent, screeny);
				string tFieldName = (pcChildIdx == 0) ? CvtUserFldToFld(element.Name) : CvtUserFldToFld(pcChildFldName);
				mytextbox.Name = tFieldName;
				myHT.Add(tFieldName, mytextbox);
				tabindx++;
				Controls.Add(mytextbox);
				screeny = screeny + 10;
				mytextbox.TextChanged += new System.EventHandler(this.textbox_change);
				mytextbox.Validating += new System.ComponentModel.CancelEventHandler(this.textbox_Validating);
				mytextbox.GotFocus += new System.EventHandler(this.textbox_zoombtn);
				mytextbox.KeyDown += new KeyEventHandler(MyOnKeyDown);
				if (pcChildIdx > 0)
				{
					mytextbox.Enter += new System.EventHandler(this.txtBox_Enter);
					mytextbox.Leave += new System.EventHandler(this.txtBox_Leave);
				}
				string labelAnnot;
				bool imgchld = false;
				if (element.Name == "Image_Filename" || element.Name == "Image_Height" || element.Name == "Image_Width") imgchld = true;
				// Pass the textbox in to set attributes such as maxlength, multiline and pattern
				// DisplayXmlSchemaSimpleType will set up
				// - the textbox length which is needed to determin the Parent/Child group box width
				// - add special buttons and events for Graphic (figure) RO type fields
				labelAnnot = DisplayXmlSchemaSimpleType((XmlSchemaSimpleType)element.SchemaType,mytextbox, null,imgchld,GraphicsText);
				if (labelAnnot != null)
				{
					// this is part of a Graphics (figure) RO - either the height or width adjustment text box
					// so put a lable next to the respective height/width text box
					Label annot;
					annot =  new System.Windows.Forms.Label();
					annot.Location = new Point(screenx+mytextbox.Width+10, sscreeny);
					annot.Name = "annot" + screeny.ToString();
					annot.Text = labelAnnot;
					annot.AutoSize = true;
					Controls.Add(annot);
				}
				screeny += mytextbox.Height;
				maxwidth = Math.Max(maxwidth, mylabel.Width); // is the label wider?
				maxwidth = Math.Max(mytextbox.Width, maxwidth); // is the text box wider?
			}
		}
		// the following is needed to load the control.
		private void ctlXMLEdit_Load(object sender, System.EventArgs e)
		{
		
		}
		
		// when data in a text box has changed, flag it (except for when this
		// happens during loading of original data). This flag will be used
		// later to know whether to save data.
		private void textbox_change(object sender, System.EventArgs e)
		{
			if(dosaveflag)mysavexml = true;
		}
		// C2021-026 Event handler for Parent/Child child textbox
		//           if the textbox text is same as parent, then or nothing is entered in the textbox
		//           then use the parent value and set the text color to gray
		// B2025-028 RO Editor - Parent Child Applicability - Default Values
		//           Were always setting to the Group parent value instead of individual parent values
		//           When leaving the textbox
		private void txtBox_Leave(object sender, EventArgs e)
		{
			TextBox tb = sender as TextBox;
			string dfTxt = "";
			try
			{
				string parentid = ((TextBoxAttrTag)tb.Tag).GetParentHTId;
				if (!string.IsNullOrEmpty(parentid))
				{
					object o = myHT[parentid];
					if (o != null)
						dfTxt = (o as TextBox).Text; // set to use the parent's value (default)				
				}
				else
                {
					string parName = pcGrpBox.Name.Substring(5);
					object o = myHT[parName];
					if (o != null)
						dfTxt = (o as TextBox).Text; // set to use the parent's value (default)
				}
			}
            catch 
			{
				string parName = pcGrpBox.Name.Substring(5);
				object o = myHT[parName];
				if (o != null)
					dfTxt = (o as TextBox).Text; // set to use the parent's value (default)
			}
			if (dosaveflag) mysavexml = true;
			if (tb.Text.Length == 0 || tb.Text == dfTxt)
			{
				tb.Text = dfTxt;
				tb.ForeColor = SystemColors.GrayText;
			}
		}
		// C2021-026 Event handler for Parent/Child child textbox
		//           If the textbox is set to gray, then we are using the parent value
		//           so clear the textbox so that user can enter the value for that child
		private void txtBox_Enter(object sender, EventArgs e)
		{
			TextBox tb = sender as TextBox;
			if (tb.ForeColor == SystemColors.GrayText) // currently no value set - using parent's value
			{
				tb.Text = "";
				tb.ForeColor = SystemColors.WindowText;
			}
		}
		// radiocheckchg - called when a radio button is selected. This will set
		// visibility for associated text box. 
		private void radiocheckchg(object sender, System.EventArgs e)
		{
			RadioButton btnsel = (RadioButton) sender;
			TextBox assocbox;
			string btntext, str;
			if(dosaveflag)mysavexml = true;
			btntext = CvtUserFldToFld(btnsel.Text);
			
			// now get the actual textbox and set visibility depending
			// on whether the box is checked, i.e. turned on(if checked, it should
			// be set to visible)
		
			str = btnsel.Name.Replace(btntext,"");
			object o = myHT[str];
			if (o == null) return;
			assocbox = (TextBox) o;
			// make the text box visible if checked, otherwise, invisible
			assocbox.Visible = btnsel.Checked;
			// C2021-026 show or hide the Parent/Child appicability group box
			if (pcGrpBox != null) // note that we save only one applicability group box and only for the schema choice field type (radio buttons)
			{
				bool vsblState = (btnsel.Text == "Fixed"); // only show parent/child group if the Fixed radio button is selected
				{
					pcGrpBox.Visible = vsblState; // show or hide the P/C Children group box (the box shaded in blue)
					// C2021-026 PCChildren contains a list strings reprenting the Children setup in the current Working Draft Parent/Child property
					int pcChildIdx = 0;
					foreach (string s in PCChildren)
					{
						pcChildIdx++;
						string fldx = string.Format("_PCCHILD{0}", pcChildIdx); // create a field name for P/C Child fields
						string tstr = pcGrpBox.Name.Substring(5) + fldx; // get the base part of the label and text box control name
						foreach (Control c in Controls)
							if (c.Name == tstr)
							{
								c.Visible = vsblState; // show or hide the lable inside the group box
								object oo = myHT[c.Name];
								if (oo != null)
									(oo as TextBox).Visible = vsblState; // show or hide the associated text box
							}
					}
				}
			}
		}
		// these two methods convert user input field names from/to xml tags. (xml tags
		// cannot begin with digit, have spaces or other special chars.
		private string CvtUserFldToFld(string fldname)
		{
			if (fldname.Length < 2)
				return fldname;
			// a digit cannot start an xml fieldname, prepend a "__" to it.
			string tmp0;
			if (char.IsDigit(fldname,0))
				tmp0 = "__" + fldname;
			else
				tmp0 = fldname;
			// an xml fieldname cannot have a space, change it to a "__"
			string tmpstr = tmp0.Replace(" ","__");
			int len = tmpstr.Length;
			int cnt = 0;
			// this is also our sequence that tells us the follow 3 digits is the ascii number (base 10)
			// of the character we replaced.
			string OKpunch = "-._"; 
			string outstr = "";
			int decval;
			while (cnt < len)
			{
				char tmpchr = tmpstr[cnt];
				if(!char.IsLetterOrDigit(tmpchr)&& (OKpunch.IndexOf(tmpchr) == -1) )
				{
					decval = tmpchr;
					outstr += OKpunch + decval.ToString("D3");
				}
				else
				{
					outstr += tmpchr.ToString();
				}
				cnt++;
			}
			return outstr;
		}
		private string CvtFldToUserFld(string fldname)
		{
			string tmpstr0;
			if (fldname.Length < 2) return fldname;
			// an xml element name cannot begin with a digit. we had prepended a "__"
			if (fldname.Substring(0,2) == "__" && char.IsDigit(fldname,2))
				tmpstr0 = fldname.Substring(2,fldname.Length-2);
			else
				tmpstr0 = fldname;
			// an xml element name cannot have a space, we converted to a "__"
			string tmpstr = tmpstr0.Replace("__"," ");
			int len = tmpstr.Length;
			int cur = 0;
			// this is also our sequence that tells us the follow 3 digits is the ascii number (base 10)
			// of the character we replaced.
			string OKpunch = "-._"; 
			string outstr = "";
			int decval, indx;
			if (tmpstr.Length <6)
				indx = -1;
			else
				indx=tmpstr.IndexOf(OKpunch,cur);
			string asc_spchar;
			while (indx>=0)
			{
				outstr += tmpstr.Substring(cur,indx-cur);
				asc_spchar = tmpstr.Substring(indx+3,3);
				decval = System.Convert.ToInt16(asc_spchar,10);
				outstr += System.Convert.ToChar(decval).ToString();
				cur = indx+6;
				if (cur+6 > len)
					indx = -1;
				else
					indx = tmpstr.IndexOf(OKpunch,cur);
			}
			if (cur0)
					lname = iname.Substring(slindx+1,iname.Length-slindx-1);
				else
					lname = iname;
				// now check to see if this is a local file - to do this, check
				// to see if either has a "~" which may signify a short filename.
				// If so, compare the short file names (Note that comparing long
				// filenames didn't work, i.e. the kernel routine did not return
				// a long filename if a short one was sent in).
				// open an existing file, or create a new one
				
				FileInfo lfi = new FileInfo(lname);  // local file & info
				
				if (lfi.Exists == false)    // !exist, copy it & move on
				{
					// Copy the file to the local directory
					ifi.CopyTo(lname);
					// update fields on window....
					GraphicsFiletextbox.Text = lfi.Name;
					TextBoxAttrTag tag = (TextBoxAttrTag) GraphicsFiletextbox.Tag;
					tag.SetImageDate(CalculatePromsDate(ifi.LastWriteTime));
					GraphicsFiletextbox.Tag = tag;
					UpdateHtWd();
					return true;
				}
				else  // A local file exists by the same name
				{ 
					if (ifi.Exists == false)
					{
						MessageBox.Show("Input file doesn't exist.","Error");
						return false;
					}
					else
					{
						// first just check if these are really the same, just the
						// user put a path in.
						int same = 0;
						if (ifi.DirectoryName.IndexOf("~")>=0 || lfi.DirectoryName.IndexOf("~")>=0)
						{
						
							StringBuilder shortlname = new StringBuilder(257);
							StringBuilder shortiname = new StringBuilder(257);
							GetShortPathName(ifi.DirectoryName,shortiname,257);
							GetShortPathName(lfi.DirectoryName,shortlname,257);
							same = String.Compare(shortlname.ToString(),shortlname.ToString());
						}
						if (same == 0)
						{
							TextBoxAttrTag tag = (TextBoxAttrTag) GraphicsFiletextbox.Tag;
							tag.SetImageDate(CalculatePromsDate(lfi.LastWriteTime));
							GraphicsFiletextbox.Tag = tag;
							return true;
						}
						else
						{
							// Is the file the same - size, date, time
							// old code checked for both findfirst failures??
							string msg=null;
							if (lfi.LastWriteTime > ifi.LastWriteTime)
								msg = "A newer copy of t";
							else if (lfi.LastWriteTime < ifi.LastWriteTime)
								msg = "An older copy of t";
							else if (lfi.Length != ifi.Length)
								msg = "A different copy of t";
							msg = msg + "he selected file\nexists in the RO directory\n";
							msg = msg + "Do you want to replace it\n";
							msg = msg + "with the selected file?";
							MessageBoxButtons buttons = MessageBoxButtons.YesNoCancel;
							DialogResult result;
							result = MessageBox.Show( msg, "Graphics File Copy", buttons);
							if (result == DialogResult.Yes)
							{
								lfi.Delete();
								ifi.CopyTo(lname,true);
								// update dialog data & ht/wd
								GraphicsFiletextbox.Text = lfi.Name;
								TextBoxAttrTag tag = (TextBoxAttrTag) GraphicsFiletextbox.Tag;
								tag.SetImageDate(CalculatePromsDate(lfi.LastWriteTime));
								GraphicsFiletextbox.Tag = tag;
								UpdateHtWd();
								return true;
							}
							else if (result != DialogResult.No)   // not yes or no. Cancel out.
								return false;
						}
					}
				}
			}
			return false;
		}
		
		// if changed height, calculate associated width
		private void GraphicsHt_lostfocus(object sender, System.EventArgs e)
		{
			TextBox ht = (TextBox) sender;
			if (ht.Modified == true)
			{
				try
				{
					int newht= System.Convert.ToInt32(ht.Text);
					setHtWdBasedUponHeight(newht);
				}
				catch (Exception ee)
				{
					MessageBox.Show("The Height and Width cannot be empty fields. Enter a number in either field and the other will automatically adjust to a proportional size.", "Invalid Height Input");
				}
			}
		}
		// if changed width, calculate associated height
		private void GraphicsWd_lostfocus(object sender, System.EventArgs e)
		{
			TextBox wd = (TextBox) sender;
			if (wd.Modified == true)
			{
				try
				{
					int newwd= System.Convert.ToInt32(wd.Text);
					setHtWdBasedUponWidth(newwd);
				}
				catch (Exception ee)
				{
					MessageBox.Show("The Height and Width cannot be empty fields. Enter a number in either field and the other will automatically adjust to a proportional size.", "Invalid Width Input");
				}
			}
		}					
		private void setHtWdBasedUponHeight(int nuval)
		{
			int w,h;
			if(himage!=0)
			{
				if(nuval==0)nuval=(himage+DPPL-1)/DPPL;
				if(nuval > hmax)nuval=hmax;
				else if(nuval < hmin)nuval=hmin;
				h=nuval;
				// The <<1, +1, and >>1 efectively round the result
				// by multiplying by 2 performing the division adding
				// 1 and dividing by 2
				w=(((h*STDCPI*wimage)<<1)/(himage*STDLPI)+1)>>1;
			} 
			else 
			{
				w=h=0;
			}
			GraphicsWdtextbox.Text = w.ToString();
			GraphicsHttextbox.Text = h.ToString();
		}
		private void setHtWdBasedUponWidth(int nuval)
		{
			int w,h;
			if(himage!=0)
			{
				if(nuval==0)nuval=(wimage+DPPC-1)/DPPC;
				if(nuval > wmax)nuval=wmax;
				else if(nuval < wmin)nuval=wmin;
				w=nuval;
				//
				// The <<1, +1, and >>1 efectively round the result
				// by multiplying by 2 performing the division adding
				// 1 and dividing by 2
				h=(((w*STDLPI*himage)<<1)/(wimage*STDCPI)+1)>>1;
			} 
			else 
			{
				w=h=0;
			}
			GraphicsWdtextbox.Text = w.ToString();
			GraphicsHttextbox.Text = h.ToString();
		}
		private void UpdateHtWd()
		{
			// copy the file local and also get it's width/height by reading the file.
			Bitmap image;
			try 
			{
				string dir = Directory.GetCurrentDirectory();
				string GraphicsFileName = GraphicsFiletextbox.Text;
//				FileInfo fi = new FileInfo(GraphicsFiletextbox.Text);
				if (GraphicsFileName.IndexOf('.') == -1)
					GraphicsFileName += DefaultGraphicFileExtension; // default graphics file extension
				FileInfo fi = new FileInfo(GraphicsFileName);
				if (fi.Exists == false) 
				{
					MessageBox.Show("Graphics File is missing","Error");
					wimage=himage=height=width=0;
					return;
				}
				
				image = new Bitmap(GraphicsFileName);
				himage = image.Height;
				wimage = image.Width;
				hmax=MAXLT; // 60 Lines
				int hmxw=STDLPI*MAXWT*himage/(wimage*STDCPI);
				if(hmxw < hmax)
				{// Size is limited to the width of the paper
					hmax=hmxw;
					wmax=MAXWT;
				} 
				else 
				{
					wmax=STDCPI*hmax*wimage/(himage*STDLPI);
				}
				hmin=1;
				int hmnw=STDLPI*6*himage/(wimage*STDCPI);
				if(hmnw > hmin)
				{// Size is limited by the width of the graphic
					hmin=hmnw;
					wmin=6;
				} 
				else 
				{
					wmin=STDCPI*1*wimage/(himage*STDLPI);
				}
				setHtWdBasedUponHeight(height);
			}
			catch (Exception exx)
			{
				MessageBox.Show(exx.Message.ToString(),"error on processing of graphics file");
			}
		}
		private void btnFindFile_click(object sender, System.EventArgs e)
		{
			bool done = false;
			while (done == false)
			{
				OpenFileDialog openFileDialog1 = new OpenFileDialog();
				openFileDialog1.InitialDirectory = Directory.GetCurrentDirectory();
				openFileDialog1.Filter = 
					"Bitmap files (*.bmp)|*.bmp|Enhanced Windows metafiles (*.emf)|*.emf|Graphics Interchange Format (*.gif)|*.gif|Icons (*.ico)|*.ico|JPEG (*.jpg)|*.jpg|Portable Network Graphics (*.png)|*.png|TIFF (*.tif)|*.tif|Windows Metafile (*.wmf)|*.wmf" ;
				openFileDialog1.FilterIndex = 7 ;
				openFileDialog1.RestoreDirectory = true ;
				openFileDialog1.CheckFileExists = true;
//				try
//				{
					if(openFileDialog1.ShowDialog() != DialogResult.OK)
						done = true;
					else
					{
						string iname = openFileDialog1.FileName;
						done = ValidGraphicsFile(iname);
						if (done == true)
						{
							FileInfo fi = new FileInfo(iname);
							GraphicsFiletextbox.Text = fi.Name;
							UpdateHtWd();
						}
					}
//				}
//				catch (Exception j)
//				{
//					MessageBox.Show(j.Message,"debug");
//				}
			}
		}
	}			
	/*
	 * TimeZone is an Abstract Class.  You need to inherit the TimeZone Class
	 * and overload a couple of it's functions in order to use it.
	 * - called in CalculatePromsDate() above.
	 */ 
		public class ThisTimeZone : TimeZone
	{
		private TimeZone curTZ;
		public TimeSpan SpanTZ;
		public ThisTimeZone()
		{
			curTZ = CurrentTimeZone;  // local timezone
		}
		// get the UTC (GMT) value for the given date/time
		public override TimeSpan GetUtcOffset(DateTime ForThisDate)
		{
			SpanTZ = curTZ.GetUtcOffset(ForThisDate); 
			return SpanTZ;
		}
		// Name of the local Daylight savings time zone
		public override string DaylightName
		{
			get
			{
				return curTZ.DaylightName;
			}
		}
		// Name of the local standard time zone
		public override string StandardName
		{
			get
			{
				return curTZ.StandardName;
			}
		}
		// Get the start and end dates for daylight savings
		public override System.Globalization.DaylightTime GetDaylightChanges(int year)
		{
			return curTZ.GetDaylightChanges(year);
		}
	}
}