/*********************************************************************************************
 * Copyright 2002 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: RODefFrm.cs $     $Revision: 18 $
 * $Author: Kathy $   $Date: 9/06/05 11:26a $
 *
 * $History: RODefFrm.cs $
 * 
 * *****************  Version 18  *****************
 * User: Kathy        Date: 9/06/05    Time: 11:26a
 * Updated in $/EXE/RefObj/ROEditor
 * B2005-035
 * 
 * *****************  Version 17  *****************
 * User: Jsj          Date: 7/02/03    Time: 9:25a
 * Updated in $/EXE/RefObj/ROEditor
 * made messagebox calls modal by removing the parent window reference
 * 
 * *****************  Version 16  *****************
 * User: Kathy        Date: 5/21/03    Time: 12:52p
 * Updated in $/EXE/RefObj/ROEditor
 * B2003-041: process if only one field for RO & not always saving ro
 * field name edit
 * 
 * *****************  Version 15  *****************
 * User: Kathy        Date: 5/07/03    Time: 1:58p
 * Updated in $/EXE/RefObj/ROEditor
 * B2003-033 fixed
 * 
 * *****************  Version 14  *****************
 * User: Jsj          Date: 4/14/03    Time: 3:03p
 * Updated in $/EXE/RefObj/ROEditor
 * changes due to RO FST file creation speed up
 * 
 * *****************  Version 13  *****************
 * User: Kathy        Date: 12/10/02   Time: 2:26p
 * Updated in $/EXE/RefObj/ROEditor
 * fieldname special chars & bug fixes
 * 
 * *****************  Version 12  *****************
 * User: Kathy        Date: 12/02/02   Time: 9:25a
 * Updated in $/EXE/RefObj/ROEditor
 * after edit fieldname, update menu & retval correctly
 * 
 * *****************  Version 11  *****************
 * User: Kathy        Date: 12/02/02   Time: 8:30a
 * Updated in $/EXE/RefObj/ROEditor
 * fieldname replace chars
 * 
 * *****************  Version 10  *****************
 * User: Kathy        Date: 12/02/02   Time: 6:19a
 * Updated in $/EXE/RefObj/ROEditor
 * status on long ops & update text box for fieldname change
 * 
 * *****************  Version 9  *****************
 * User: Kathy        Date: 11/19/02   Time: 11:30a
 * Updated in $/EXE/RefObj/ROEditor
 * validate retval (handle {})
 * 
 * *****************  Version 8  *****************
 * User: Kathy        Date: 10/15/02   Time: 2:18p
 * Updated in $/EXE/RefObj/ROEditor
 * new group (table)
 * 
 * *****************  Version 7  *****************
 * User: Kathy        Date: 10/10/02   Time: 10:02a
 * Updated in $/EXE/RefObj/ROEditor
 * try to m ake xml tree & tree control in sync 
 * 
 * *****************  Version 6  *****************
 * User: Kathy        Date: 9/25/02    Time: 2:31p
 * Updated in $/EXE/RefObj/ROEditor
 * multitxt->variable
 * 
 * *****************  Version 5  *****************
 * User: Kathy        Date: 9/25/02    Time: 9:57a
 * Updated in $/EXE/RefObj/ROEditor
 * dev
 * 
 * *****************  Version 4  *****************
 * User: Kathy        Date: 9/19/02    Time: 10:04a
 * Updated in $/EXE/RefObj/ROEditor
 * mods for subgroup apply below node. if mod menutitle template, update
 * menu titles from node down.
 * 
 * *****************  Version 3  *****************
 * User: Kathy        Date: 9/11/02    Time: 1:15p
 * Updated in $/EXE/RefObj/ROEditor
 * vlnxml
 * 
 * *****************  Version 2  *****************
 * User: Kathy        Date: 8/28/02    Time: 10:57a
 * Updated in $/EXE/RefObj/ROEditor
 *********************************************************************************************/

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;
using System.Text;
using ROFields;
using RODBInterface;
using VlnStatus;


namespace ROEditor
{

	/// <summary>
	/// Summary description for RODefinition.
	/// </summary>
	public class RODefFrm : System.Windows.Forms.Form
	{
		private System.Windows.Forms.Label lblGroup;
		private System.Windows.Forms.Label lblRetVal;
		private System.Windows.Forms.Label lblMenuVal;
		private System.Windows.Forms.TextBox tbRetVal;
		private System.Windows.Forms.TextBox tbMenuVal;
		private System.Windows.Forms.Label lblGroupText;
		private System.Windows.Forms.GroupBox gbFields;
		private System.Windows.Forms.Label lblInUse;
		private System.Windows.Forms.Label lblAvail;
		private System.Windows.Forms.ListBox lboxInUse;
		private System.Windows.Forms.ListBox lboxAvail;
		private System.Windows.Forms.Button btnAdd;
		private System.Windows.Forms.Button btnEdit;
		private System.Windows.Forms.Button btnRemove;
		private System.Windows.Forms.Button btnNew;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		private RODB myrodb;
		ArrayList AvailList;
		private System.Windows.Forms.Button btnOK;
		private System.Windows.Forms.Button btnCancel;
		ArrayList InUseList;
		private VlnXmlElement elem;
		private uint editlevel;			// flag for group definition or ro definition
		private string strFieldsInUse;
		private string origFieldsInUse;
		private string origRetVal;
		private string origMenuItem;
		StringBuilder parseerror; 
		private int hi, lo;
		private int dbtype;
		// pass in Group Element
		public RODefFrm(VlnXmlElement pelem, RODB rodb, string fields, string grptxt, int idbtype)
		{
			myrodb = rodb;
			strFieldsInUse = fields;
			dbtype = idbtype;
			if (fields == "FieldsInUse")    // not a group level edit.
				editlevel = (uint) RecordType.Schema;
			else
				editlevel = (uint) RecordType.GroupSchema;
			elem = pelem;
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			if(editlevel==(uint)RecordType.GroupSchema)this.Text = "Subgroup Definition";
			FillInData(grptxt);
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}
		
		private void FillInAvailable()
		{
			ROField rof;
			
			// put the items in the AvailList box.
			for (int i=0; i< AvailList.Count; i++)
			{
				rof = (ROField) AvailList[i];
				if (rof.GetFieldname != null)   // DO YET: Why null?
					this.lboxAvail.Items.Add(rof.GetFieldname);
			}
		}

		
		private void SetUpListBoxes()
		{
			ROField rof;
			// Get list of available fields (all fields in this type, i.e. rectype = 'Schema'
			// or 'GroupSchema') and get list of inuse from the schema definition.
			AvailList = myrodb.RODB_GetFields(elem, editlevel);
			InUseList = myrodb.RODB_GetFieldsInUse(elem, AvailList, strFieldsInUse, ref origFieldsInUse, true);
			FillInAvailable();

			// set up the listbox for inuse items.
			if (InUseList != null) 
			{
				// add items to the InUseList box.
				for (int i=0; i< InUseList.Count; i++)
				{
					rof = (ROField) InUseList[i];
					if (rof.GetFieldname != null)   //DO YET: why null?
						this.lboxInUse.Items.Add(rof.GetFieldname);
				}
			}
		}

		
		private void DoValueTextBoxes()
		{
			string menuitm, retval;
			if (editlevel == (uint) RecordType.GroupSchema)
			{
				menuitm = elem.GetAttribute("GroupMenuItem");
				this.tbRetVal.Visible = false;
				this.lblRetVal.Visible = false;
				VlnXmlElement parent;
				parent = (VlnXmlElement) elem.ParentNode;
				while (parent != null && (menuitm == ""))
				{
					// walk up tree to get parent and check for data there
					if (menuitm == "") menuitm = parent.GetAttribute("GroupMenuItem");
					if (parent.Name != "RO_Root")
						parent = (VlnXmlElement) parent.ParentNode;
					else
						parent = null;
				}
				if (menuitm !="")
					this.tbMenuVal.Text = CvtFldToUserFld(menuitm);
				else
					this.tbMenuVal.Text = "";
			}
			else
			{
				menuitm = elem.GetAttribute("MenuItem");
				retval = elem.GetAttribute("RetVal");
				this.tbRetVal.Visible = true;
				this.lblRetVal.Visible = true;
				VlnXmlElement parent;
				parent = (VlnXmlElement) elem.ParentNode;
				while (parent != null && (menuitm == "" || retval==""))
				{
					// walk up tree to get parent and check for data there
					if (menuitm == "") menuitm = parent.GetAttribute("MenuItem");
					if (retval == "") retval = parent.GetAttribute("RetVal");
					if (parent.Name != "RO_Root")
						parent = (VlnXmlElement) parent.ParentNode;
					else
						parent = null;
				}
				if (menuitm!="")
					this.tbMenuVal.Text = CvtFldToUserFld(menuitm);
				else
					this.tbMenuVal.Text = "";
				if (retval!="")
					this.tbRetVal.Text = CvtFldToUserFld(retval);
				else
					this.tbRetVal.Text = "";
			}
		}

		private void FillInData(string grptxt)
		{	
			this.lblGroupText.Text = grptxt;
			DoValueTextBoxes();
			SetUpListBoxes();
	
			lboxInUse.GotFocus += new EventHandler(this.lboxInUse_GotFocus);
			lboxAvail.GotFocus += new EventHandler(this.lboxAvail_GotFocus);

			// save copies of local data, so if there is a change, we know we must save them.
			origRetVal=this.tbRetVal.Text;
			origMenuItem=this.tbMenuVal.Text;
			
			// if this is a database level definition & it's a user defined database,
			// the okay button is disabled until a field is used.
			if (elem.ParentNode.Name == "RO_Root" && dbtype==3)
			{
				string attr;
				if (editlevel==(uint)RecordType.Schema)
					attr = elem.GetAttribute("FieldsInUse");
				else
					attr = elem.GetAttribute("GroupFieldsInUse");
				if (attr==null || attr=="") btnOK.Enabled=false;
			}
		}	

		protected void lboxInUse_GotFocus (object sender, EventArgs e)
		{
			lboxAvail.ClearSelected();
			this.btnRemove.Enabled = true;
			this.btnAdd.Enabled = false;
			this.btnEdit.Enabled = true;
		}
		protected void lboxAvail_GotFocus (object sender, EventArgs e)
		{
			lboxInUse.ClearSelected();
			this.btnAdd.Enabled = true;
			this.btnRemove.Enabled = false;
			this.btnEdit.Enabled = true;
		}
		private void btnRemove_Click(object sender, System.EventArgs e)
		{
			//get item in lboxInUse (in use list) and remove it from there
			// and add it to the lboxAvail list.
			if (lboxInUse.SelectedIndex >=0 ) 
			{
				// get the selected item and its string. Remove it from the listbox,
				// add it to the available array list & listbox & then remove it from
				// the inuse array list.
				int indx = lboxInUse.SelectedIndex;
				lboxInUse.Items.RemoveAt(indx);

				// copy if over to availlist
				ROField rof = (ROField) InUseList[indx];
				ROField copyrof = new ROField(rof.GetFieldname,rof.GetRecID,rof.GetMasterRecID, rof.GetFieldType);
				AvailList.Add(copyrof);

				lboxAvail.Items.Add(copyrof.GetFieldname);
				InUseList.RemoveAt(indx);
				lboxAvail.Refresh();
				lboxInUse.Refresh();
			}
		}
		private void btnAdd_Click(object sender, System.EventArgs e)
		{
			//get item in lboxInUse (in use list) and remove it from there
			// and add it to the lboxAvail list.
			if (lboxAvail.SelectedIndex >=0 ) 
			{
				// get the selected item and its string. Remove it from the listbox,
				// add it to the inuse array list & listbox & then remove it from
				// the avail(able) array list.
				int indx = lboxAvail.SelectedIndex;
				lboxAvail.Items.RemoveAt(indx);
				
				// copy if over to availlist
				ROField rof = (ROField) AvailList[indx];
				ROField copyrof = new ROField(rof.GetFieldname,rof.GetRecID,rof.GetMasterRecID,rof.GetFieldType);
				InUseList.Add(copyrof);

				lboxInUse.Items.Add(copyrof.GetFieldname);
				AvailList.RemoveAt(indx);
				lboxAvail.Refresh();
				lboxInUse.Refresh();
				btnOK.Enabled=true;

			}
		}

		private void syntax(string prob,int start, int end)
		{
			lo=start;
			hi=end;
			parseerror.Append("Invalid Syntax - ");
			parseerror.Append(prob);
		}

		private void missingDelimiter(char chr,int start,int end)
		{
			lo=start;
			hi=end;
			string delim = "{}{<><";
			int delindx;
			delindx = delim.IndexOf(chr,0);
			parseerror.Append("Couldn't find matching ");
			if (delindx >-1)
				parseerror.Append(delim.Substring(delindx,1));
			else
				parseerror.Append("?");
		}
		private bool isWhiteSpace(char chr)
		{
			return(chr!=0 && " \t\n\r".IndexOf(chr)!=-1);
		}
		private int endsIn(char chr, string str, int start, int end)
		{
			while(start <= end && isWhiteSpace(str[end]))end--;
			if(start <= end && str[end]==chr)return 1;
			return 0;
		}

		private void processBrace(string str, int start, int end)
		{
			int where=str.IndexOfAny("<>{}=".ToCharArray(),start,end-start);
			if(where!=0) 
			{
				char chr=str[where];
				switch(chr)
				{
					case '{':
						// verify that this ends in a }
						if(endsIn('}',str,where,end)==0) 
						{
							syntax("should end with }",where,end);
						} 
						break;
					case '<':
						syntax("< not permitted here",where,where);
						break;
					case '}':
						syntax("} not permitted here",start,where);
						break;
					case '>':
						syntax("> not permitted here",start,where);
						break;
				}
			}
		}

		private int findMatchingBrace(string str, int start, int end)
		{
			int where=start+1;
			int last=where;
			int termlevel=1;
			while(hi==0 && where <= end && termlevel!=0)
			{
				char chr=str[where];
				switch (chr) 
				{
					case '{':
						where=findMatchingBrace(str,where,end);
						where++;
						break;
					case '}':
						termlevel=0;
						break;
					case '>':
						missingDelimiter(chr,last,where);
						break;
					case '<':
						int next;
						next=str.IndexOfAny("{}<>".ToCharArray(),where+1,end-where);
						if(next==0)
						{
							missingDelimiter(chr,last,end);
						} 
						else if(str[next]!='>')
						{
							missingDelimiter(chr,last,next);
						} 
						else 
						{
							last=where=next+1;
						}
						break;
					default:
						where++;
						break;
				}
			}
			if(hi==0 && termlevel!=0)
			{
				missingDelimiter('{',start,end);
			}
			return where;
		}

		private bool ValidReturnFieldSpec(string str, int start, int end)
		{
			if (parseerror != null) 
				parseerror.Remove(0,parseerror.Length);
			else
				parseerror = new StringBuilder();
			int where=start;
			int last=start;
			hi=lo=0;
			while(hi==0 && where <= end)
			{
				char chr=str[where];
				switch (chr) 
				{
					case '{':
						int next;
						next=findMatchingBrace(str,where,end);
						if(hi==0)
						{
							processBrace(str,where+1,next-1);
							where=next+1;
							last=where;
						}
						break;
					case '}':
						missingDelimiter(chr,last,where);
						break;
					case '>':
						missingDelimiter(chr,last,where);
						break;
					case '<':
						int strt=where+1;
						where = str.IndexOfAny("<>{}".ToCharArray(),where+1,end-where);
						if (where==0)
							missingDelimiter('<',start,end);
						else if (str[where]=='>')
						{
							string inusename = str.Substring(strt,where-strt);
							// now check field name.
							bool found = false;
							string rofname;
							for (int i=0; i<InUseList.Count; i++)
							{
								ROField rof = (ROField) InUseList[i];
								rofname = rof.GetFieldname;
								if (inusename == rofname)
								{
									found = true;
									break;
								}
							}
							if (found == false)syntax(inusename+" invalid field name",strt,where);
							// used to have procini def stuff here.
							start=where+1;
							where++;
						}
						else
							missingDelimiter('<',start,where);
						break;
					default:
						where++;
						break;
				}
			}
			if (parseerror.Length>0)
			{
				MessageBox.Show(parseerror.ToString(),"Error in validating return value");
				return false;
			}
			else
				return true;
		}

		private bool ValidFieldSpec(string field)
		{
			int left = field.IndexOf("<");
			int right;
			int stindx = 0;
			while (left != -1)
			{
				// get right bracket and then check for valid field & if commas with
				// this, check for integer values.
				right = field.IndexOf(">",stindx);
				if (right == -1)
				{
					MessageBox.Show("Missing > in Menu value.");
					return false;
				}
				string substring = field.Substring(left+1,right-left-1);
				// do comma parts now.
				string inusename;
				int comma1 = substring.IndexOf(",");
				if (comma1 != -1)
				{
					// get any numbers for formatting & check for ints.
					int comma2 = substring.IndexOf(",",comma1+1);
					if (comma2 != -1) // two ints.
					{
						if (comma2-comma1 > 1) // first format field - not empty 
						{
							string int1 = substring.Substring(comma1+1,comma2-comma1-1);
							try
							{
								int l = System.Convert.ToInt32(int1);
							}
							catch (Exception e)
							{
								MessageBox.Show(e.Message,"invalid format number");
								return false;
							}
						}
						if (comma2<substring.Length)  // second part - not empty
						{
							string int2 = substring.Substring(comma2+1,substring.Length-comma2-1);
							try
							{
								int l = System.Convert.ToInt32(int2);
							}
							catch (Exception e)
							{
								MessageBox.Show(e.Message,"invalid format number");
								return false;
							}
						}
					}
					else  // just one format 
					{
						string int1 = substring.Substring(comma1,comma2-comma1);
						try
						{
							int l = System.Convert.ToInt32(int1);
						}
						catch (Exception e)
						{
							MessageBox.Show(e.Message,"invalid format number");
							return false;
						}
					}
					inusename = substring.Substring(0,comma1);
				}
				else  // no commas, it's just a field name.
					inusename = substring;
				
				// now check field name.
				bool found = false;
				string rofname;
				for (int i=0; i<InUseList.Count; i++)
				{
					ROField rof = (ROField) InUseList[i];
					rofname = rof.GetFieldname;
					if (inusename == rofname)
					{
						found = true;
						break;
					}
				}
				if (found == false)
				{
					MessageBox.Show("Menu Value not in In Use field list");
					return false;
				}
				if (left>field.Length)
					left = -1;
				else
				{
					stindx=right+1;
					left = field.IndexOf("<",left+1);
				}
			}
			return true;
		}
		private bool ProcessRROMenuChange(VlnXmlElement mnuele, string newtmpl)
		{
			bool success;
			XmlNode chldnode = (XmlNode) mnuele.FirstChild;
			VlnXmlElement echild;
			while (chldnode != null)
			{
				if (chldnode is VlnXmlElement)
				{
					echild = (VlnXmlElement) chldnode;
					if (echild.Name != "vlnGroup")
					{
						// If this is a group defintion subtree it will only have one
						// child, which is the text definition for the subgroup. Don't
						// include these in the tree.
						int levelCnt = chldnode.ChildNodes.Count;
						string TheMenuTitle = echild.GetAttribute("MenuTitle");
						if ((levelCnt >= 1) && !TheMenuTitle.Equals(""))
						{
							string mnustring = echild.GetMenuString(newtmpl,false);
							echild.SetAttribute("MenuTitle",mnustring);
							success = myrodb.RODB_WriteRO(echild);
							if (success==false) return false;
						}
					}
				}
				chldnode = chldnode.NextSibling;
			}
			return true;
		}

		private bool UpdateMenuTitles(VlnXmlElement mnuele, string newtmpl, string stredittype, VlnStatusMessage StatMsgWindow)
		{
			// For this group and below in the xml tree, update menu titles using the new
			// template.
			bool success;
			string haskids;
			string kidsloaded;
			XmlNode node = (XmlNode) mnuele.FirstChild;
			VlnXmlElement child;
			
			while (node != null) 
			{
				if (node is VlnXmlElement)
				{
					child = (VlnXmlElement) node;
					
					// If this is a group menu edit, process group nodes, otherwise
					// process rros. For both, when hitting a group node, check that
					// it doesn't have a menu item string redefining the menu template.

					if (child.Name == "vlnGroup" && child.HasAttribute(stredittype) == false)
					{
						haskids = child.GetAttribute("HasChild");
						kidsloaded = child.GetAttribute("ChildLoaded");
						if (haskids == "True" && kidsloaded == "False" )
						{
							myrodb.RODB_GetChildData(child,true);
							child.SetAttribute("ChildLoaded","True");
						}
						StatMsgWindow.StatusMessage = child.GetAttribute("MenuTitle");
						success = UpdateMenuTitles(child,newtmpl, stredittype, StatMsgWindow);
						if (success == false) return false;
 
						if (editlevel == (uint) RecordType.Schema)
						{
							success = ProcessRROMenuChange(mnuele, newtmpl);
							if (success==false)return false;
						}
						else if (editlevel == (uint) RecordType.GroupSchema)
						{
							string mnustring = child.GetMenuString(newtmpl,true);
							child.SetAttribute("MenuTitle",mnustring);
							success = myrodb.RODB_WriteGroup(child,child);
							if (success==false) return false;
						}
					}
				}
				node = node.NextSibling;
			}
			// if doing RO menu updates, do all at the current level, otherwise, skip it 
			// because it is a group menu item.
			if (stredittype == "MenuItem")
			{
				success = ProcessRROMenuChange(mnuele, newtmpl);
				if (success==false) return false;
			}
			return true;
		}

		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 (cur<len) outstr += tmpstr.Substring(cur,len-cur);	

			return outstr;
		}


		private void btnOK_Click(object sender, System.EventArgs e)
		{
			// Check if data has changed and if so, write it out.
			// Set all of the attributes/data in the passed in VlnXmlElement & then use
			//   the database methods to send it to the database.
			
			// First check for validity of Return value and Menu value.
			//   return value: be sure it's a valid string representing a field
			bool valid;
			if (origRetVal != this.tbRetVal.Text)
			{
				valid =  ValidReturnFieldSpec(this.tbRetVal.Text,0,this.tbRetVal.Text.Length-1);
				if (valid == false) return;
			}
			if (origMenuItem != this.tbMenuVal.Text)
			{
				valid = ValidFieldSpec(this.tbMenuVal.Text);
				if (valid == false) return;
			}

			// save the inuse recs in the inuse attribute.
			string origfields = origFieldsInUse;
			string inuserecs=null;
			ROField rof;
			for (int i=0; i< InUseList.Count; i++)
			{
				rof = (ROField) InUseList[i];
				if (rof.GetFieldname != null)   //DO YET: why null?
				{					
					inuserecs = inuserecs + rof.GetRecID;
					if (i+1 < InUseList.Count) inuserecs = inuserecs + " ";
				}
			}

			// save the database record, saving modified data as appropriate -- if a mod
			// occurred at this level, add it to the attribute list too. 
			
			// also, if the menu item template changed, we need to spin thru all of the menu title strings 
			// from here down (either RO's if editlevel is Schema, or Group's if editlevel is
			// GroupSchema).
			string dotemplatechange = null;
			bool mod = false;
			if (editlevel == (uint) RecordType.Schema)
			{
				if (this.tbRetVal.Text != origRetVal)
				{
					mod = true;
					elem.SetAttribute("RetVal",CvtUserFldToFld(this.tbRetVal.Text));
				}
				if (this.tbMenuVal.Text != origMenuItem)
				{
					mod = true;
					// there may be 'spaces' in the template - don't do replace.(" ","_");
					elem.SetAttribute("MenuItem",this.tbMenuVal.Text);
					dotemplatechange = "MenuItem";
				}
				if (inuserecs != origFieldsInUse)
				{
					elem.SetAttribute("FieldsInUse", inuserecs);
					mod = true;
				}
			}
				
			if (editlevel == (uint) RecordType.GroupSchema)
			{
				if (this.tbMenuVal.Text != origMenuItem)
				{
					mod = true;
					// there may be 'spaces' in the template - don't do replace.(" ","_");
					elem.SetAttribute("GroupMenuItem",this.tbMenuVal.Text);
					dotemplatechange = "GroupMenuItem";
				}
				if (inuserecs != origFieldsInUse)
				{
					elem.SetAttribute("GroupFieldsInUse", inuserecs);
					mod = true;
				}
			}

			if (mod == true)
			{
				bool success = myrodb.RODB_WriteRO(elem);
				if (dotemplatechange!=null)
				{
					Cursor.Current = Cursors.WaitCursor;
					VlnStatusMessage StatMsgWindow = new VlnStatusMessage("Status of Menu Title Change");
					success = UpdateMenuTitles(elem, this.tbMenuVal.Text, dotemplatechange, StatMsgWindow);
					StatMsgWindow.Dispose();
					elem.SetAttribute("TreeNotData","True");
					Cursor.Current = Cursors.Default;
				}


				// if an RO defintion, reload schema. Don't bother to reload Group schema
				// since it's rarely used.
				if (editlevel == (uint) RecordType.Schema)
				{
					XmlSchema myschema;
					VlnXmlElement parent;
					parent = (VlnXmlElement) elem;
					while (parent != null)
					{
						if (parent.HasAttribute("Schema") == true) break;
						if (parent.HasAttribute("FieldsInUse") == true) break;
						parent = (VlnXmlElement) parent.ParentNode;
					}
					// if a schema has been read in, delete it and reread it.
					if (parent.HasAttribute("Schema"))
					{
						// delete this attribute because we'll reread it in.
						parent.RemoveAttribute("Schema");
						myschema = myrodb.RODB_GetSchema(elem);
					}
				}
			}

		}
		
		private void btnEdit_Click(object sender, System.EventArgs e)
		{
			ROField rof;
			bool isInSelList = true;
			// Get the field which is active & it's type. Pass the field through to the field 
			// editor.
			int indx;
			indx = lboxInUse.SelectedIndex;
			if (indx >= 0) 
				rof = (ROField) InUseList[indx];
			else 
			{
				isInSelList = false;
				indx = lboxAvail.SelectedIndex;
				if (indx >= 0)
					rof = (ROField) AvailList[indx];
				else
				{
					// message - bad one.
					return;
				}
			}
			
			uint ftype = rof.GetFieldType;
			if (ftype == (uint)FieldTypes.FrmtSingleTxt || ftype == (uint)FieldTypes.VariableTxt ||
				ftype == (uint)FieldTypes.SingleTxt || ftype == (uint)FieldTypes.Table || 
				ftype == (uint)FieldTypes.XYPlot || ftype == (uint)FieldTypes.Image)
			
			{
				FieldTypeDefFrm nwtyp = new FieldTypeDefFrm(rof, myrodb, elem, editlevel, AvailList, InUseList);
				nwtyp.ShowDialog();	
			}
			else
			{
				FieldCombFrm nwcomb = new FieldCombFrm(rof, myrodb, elem, editlevel, rof.GetFieldname, AvailList, InUseList);
				nwcomb.ShowDialog();
			}

			// Update Lists & Text boxes to represent any modified text.
			if (isInSelList)
			{
				lboxInUse.Items.Clear();
				// add items to the InUseList box.
				for (int i=0; i< InUseList.Count; i++)
				{
					rof = (ROField) InUseList[i];
					if (rof.GetFieldname != null)
						this.lboxInUse.Items.Add(rof.GetFieldname);
				}
			}
			else 
			{
				lboxAvail.Items.Clear();
				// add items to the AvailList box.
				for (int i=0; i< AvailList.Count; i++)
				{
					rof = (ROField) AvailList[i];
					if (rof.GetFieldname != null)
						this.lboxAvail.Items.Add(rof.GetFieldname);
				}
			}
		}

		private void btnNew_Click(object sender, System.EventArgs e)
		{
			ROField rof = new ROField(null,null,null,0);
			FieldTypeDefFrm nwtyp = new FieldTypeDefFrm(rof, myrodb, elem, editlevel, AvailList, InUseList);
			nwtyp.ShowDialog();
			
			if (rof.GetFieldname != null)
			{
				AvailList.Add(rof);
				lboxAvail.Items.Add(rof.GetFieldname);
			}
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.lblGroup = new System.Windows.Forms.Label();
			this.lblRetVal = new System.Windows.Forms.Label();
			this.lblMenuVal = new System.Windows.Forms.Label();
			this.tbRetVal = new System.Windows.Forms.TextBox();
			this.tbMenuVal = new System.Windows.Forms.TextBox();
			this.lblGroupText = new System.Windows.Forms.Label();
			this.gbFields = new System.Windows.Forms.GroupBox();
			this.btnNew = new System.Windows.Forms.Button();
			this.btnRemove = new System.Windows.Forms.Button();
			this.btnEdit = new System.Windows.Forms.Button();
			this.btnAdd = new System.Windows.Forms.Button();
			this.lboxAvail = new System.Windows.Forms.ListBox();
			this.lboxInUse = new System.Windows.Forms.ListBox();
			this.lblAvail = new System.Windows.Forms.Label();
			this.lblInUse = new System.Windows.Forms.Label();
			this.btnOK = new System.Windows.Forms.Button();
			this.btnCancel = new System.Windows.Forms.Button();
			this.gbFields.SuspendLayout();
			this.SuspendLayout();
			// 
			// lblGroup
			// 
			this.lblGroup.Location = new System.Drawing.Point(24, 0);
			this.lblGroup.Name = "lblGroup";
			this.lblGroup.Size = new System.Drawing.Size(88, 16);
			this.lblGroup.TabIndex = 0;
			this.lblGroup.Text = "Group:";
			this.lblGroup.TextAlign = System.Drawing.ContentAlignment.TopRight;
			// 
			// lblRetVal
			// 
			this.lblRetVal.Location = new System.Drawing.Point(16, 32);
			this.lblRetVal.Name = "lblRetVal";
			this.lblRetVal.Size = new System.Drawing.Size(96, 16);
			this.lblRetVal.TabIndex = 1;
			this.lblRetVal.Text = "Return Value:";
			this.lblRetVal.TextAlign = System.Drawing.ContentAlignment.TopRight;
			// 
			// lblMenuVal
			// 
			this.lblMenuVal.Location = new System.Drawing.Point(16, 64);
			this.lblMenuVal.Name = "lblMenuVal";
			this.lblMenuVal.Size = new System.Drawing.Size(96, 16);
			this.lblMenuVal.TabIndex = 2;
			this.lblMenuVal.Text = "Menu Value:";
			this.lblMenuVal.TextAlign = System.Drawing.ContentAlignment.TopRight;
			// 
			// tbRetVal
			// 
			this.tbRetVal.Location = new System.Drawing.Point(120, 32);
			this.tbRetVal.Name = "tbRetVal";
			this.tbRetVal.Size = new System.Drawing.Size(464, 22);
			this.tbRetVal.TabIndex = 4;
			this.tbRetVal.Text = "textBox1";
			// 
			// tbMenuVal
			// 
			this.tbMenuVal.Location = new System.Drawing.Point(120, 64);
			this.tbMenuVal.Name = "tbMenuVal";
			this.tbMenuVal.Size = new System.Drawing.Size(464, 22);
			this.tbMenuVal.TabIndex = 5;
			this.tbMenuVal.Text = "textBox1";
			// 
			// lblGroupText
			// 
			this.lblGroupText.Location = new System.Drawing.Point(120, 0);
			this.lblGroupText.Name = "lblGroupText";
			this.lblGroupText.Size = new System.Drawing.Size(464, 16);
			this.lblGroupText.TabIndex = 7;
			this.lblGroupText.Text = "label1";
			// 
			// gbFields
			// 
			this.gbFields.Controls.AddRange(new System.Windows.Forms.Control[] {
																				   this.btnNew,
																				   this.btnRemove,
																				   this.btnEdit,
																				   this.btnAdd,
																				   this.lboxAvail,
																				   this.lboxInUse,
																				   this.lblAvail,
																				   this.lblInUse});
			this.gbFields.Location = new System.Drawing.Point(8, 112);
			this.gbFields.Name = "gbFields";
			this.gbFields.Size = new System.Drawing.Size(704, 224);
			this.gbFields.TabIndex = 8;
			this.gbFields.TabStop = false;
			this.gbFields.Text = "Fields";
			// 
			// btnNew
			// 
			this.btnNew.Location = new System.Drawing.Point(608, 48);
			this.btnNew.Name = "btnNew";
			this.btnNew.Size = new System.Drawing.Size(80, 24);
			this.btnNew.TabIndex = 7;
			this.btnNew.Text = "New...";
			this.btnNew.Click += new System.EventHandler(this.btnNew_Click);
			// 
			// btnRemove
			// 
			this.btnRemove.Enabled = false;
			this.btnRemove.Location = new System.Drawing.Point(272, 144);
			this.btnRemove.Name = "btnRemove";
			this.btnRemove.Size = new System.Drawing.Size(80, 24);
			this.btnRemove.TabIndex = 6;
			this.btnRemove.Text = "Remove>>";
			this.btnRemove.Click += new System.EventHandler(this.btnRemove_Click);
			// 
			// btnEdit
			// 
			this.btnEdit.Enabled = false;
			this.btnEdit.Location = new System.Drawing.Point(272, 96);
			this.btnEdit.Name = "btnEdit";
			this.btnEdit.Size = new System.Drawing.Size(80, 24);
			this.btnEdit.TabIndex = 5;
			this.btnEdit.Text = "Edit...";
			this.btnEdit.Click += new System.EventHandler(this.btnEdit_Click);
			// 
			// btnAdd
			// 
			this.btnAdd.Enabled = false;
			this.btnAdd.Location = new System.Drawing.Point(272, 48);
			this.btnAdd.Name = "btnAdd";
			this.btnAdd.Size = new System.Drawing.Size(80, 24);
			this.btnAdd.TabIndex = 4;
			this.btnAdd.Text = "<< Add";
			this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);
			// 
			// lboxAvail
			// 
			this.lboxAvail.ItemHeight = 14;
			this.lboxAvail.Location = new System.Drawing.Point(376, 48);
			this.lboxAvail.Name = "lboxAvail";
			this.lboxAvail.Size = new System.Drawing.Size(216, 144);
			this.lboxAvail.TabIndex = 3;
			// 
			// lboxInUse
			// 
			this.lboxInUse.ItemHeight = 14;
			this.lboxInUse.Location = new System.Drawing.Point(24, 48);
			this.lboxInUse.Name = "lboxInUse";
			this.lboxInUse.Size = new System.Drawing.Size(216, 144);
			this.lboxInUse.TabIndex = 2;
			// 
			// lblAvail
			// 
			this.lblAvail.Location = new System.Drawing.Point(384, 24);
			this.lblAvail.Name = "lblAvail";
			this.lblAvail.Size = new System.Drawing.Size(80, 16);
			this.lblAvail.TabIndex = 1;
			this.lblAvail.Text = "Available:";
			// 
			// lblInUse
			// 
			this.lblInUse.Location = new System.Drawing.Point(24, 24);
			this.lblInUse.Name = "lblInUse";
			this.lblInUse.Size = new System.Drawing.Size(64, 16);
			this.lblInUse.TabIndex = 0;
			this.lblInUse.Text = "In Use:";
			// 
			// btnOK
			// 
			this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
			this.btnOK.Location = new System.Drawing.Point(512, 376);
			this.btnOK.Name = "btnOK";
			this.btnOK.Size = new System.Drawing.Size(88, 24);
			this.btnOK.TabIndex = 9;
			this.btnOK.Text = "OK";
			this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
			// 
			// btnCancel
			// 
			this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
			this.btnCancel.Location = new System.Drawing.Point(616, 376);
			this.btnCancel.Name = "btnCancel";
			this.btnCancel.Size = new System.Drawing.Size(80, 24);
			this.btnCancel.TabIndex = 10;
			this.btnCancel.Text = "Cancel";
			// 
			// RODefFrm
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
			this.ClientSize = new System.Drawing.Size(792, 437);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.btnCancel,
																		  this.btnOK,
																		  this.gbFields,
																		  this.lblGroupText,
																		  this.tbMenuVal,
																		  this.tbRetVal,
																		  this.lblMenuVal,
																		  this.lblRetVal,
																		  this.lblGroup});
			this.Font = new System.Drawing.Font("Tahoma", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.Name = "RODefFrm";
			this.Text = "Referenced Object Definition";
			this.gbFields.ResumeLayout(false);
			this.ResumeLayout(false);

		}
		#endregion
	}
}
