/// <summary>
		/// obtain value of Textbox
		/// </summary>
		public FunctionTextbox(Textbox tb, string uniquename) 
		{
			t=tb;
			if (uniquename == null)	
				return;
			// We need to register this expression with the Textbox
			tb.AddExpressionReference(uniquename);
		}
		Textbox _ToggleTextbox;	//  resolved TextBox for toggling visibility
	
		public TableGroup(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			_Grouping=null;
			_Sorting=null;
			_Header=null;
			_Footer=null;
			_Visibility=null;
			_ToggleTextbox=null;

			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "Grouping":
						_Grouping = new Grouping(r, this, xNodeLoop);
						break;
					case "Sorting":
						_Sorting = new Sorting(r, this, xNodeLoop);
						break;
					case "Header":
						_Header = new Header(r, this, xNodeLoop);
						break;
					case "Footer":
						_Footer = new Footer(r, this, xNodeLoop);
						break;
					case "Visibility":
						_Visibility = new Visibility(r, this, xNodeLoop);
						break;
					default:	
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown TableGroup element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
			}
			if (_Grouping == null)
				OwnerReport.rl.LogError(8, "TableGroup requires the Grouping element.");
		}
		Textbox _ToggleTextbox;	//  resolved TextBox for toggling visibility
	
		public Details(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			_TableRows=null;
			_Grouping=null;
			_Sorting=null;
			_Visibility=null;
			_ToggleTextbox = null;

			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "TableRows":
						_TableRows = new TableRows(r, this, xNodeLoop);
						break;
					case "Grouping":
						_Grouping = new Grouping(r, this, xNodeLoop);
						break;
					case "Sorting":
						_Sorting = new Sorting(r, this, xNodeLoop);
						break;
					case "Visibility":
						_Visibility = new Visibility(r, this, xNodeLoop);
						break;
					default:
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown Details element " + xNodeLoop.Name + " ignored.");
						break;
				}
			}
			if (_TableRows == null)
				OwnerReport.rl.LogError(8, "Details requires the TableRows element.");
		}
		public void Textbox(Textbox tb, string t, Row row)
		{
			if (!tb.IsHtml(this.r, row))		// we leave the text as is when request is to treat as html
			{									//   this can screw up the generated HTML if not properly formed HTML
				// make all the characters browser readable
				t = XmlUtil.XmlAnsi(t);

				// handle any specified bookmark
				t = Bookmark(tb.BookmarkValue(this.r, row), t);

				// handle any specified actions
				t = Action(tb.Action, row, t, tb.ToolTipValue(this.r, row));
			}
			// determine if we're in a tablecell
			Type tp = tb.Parent.Parent.GetType();
			bool bCell;
			if (tp == typeof(TableCell) ||
				tp == typeof(Corner) ||
				tp == typeof(DynamicColumns) ||
				tp == typeof(DynamicRows) ||
				tp == typeof(StaticRow) ||
				tp == typeof(StaticColumn) ||
				tp == typeof(Subtotal) ||
				tp == typeof(MatrixCell))
				bCell = true;
			else
				bCell = false;

			if (tp == typeof(Rectangle))
				tw.Write("<td>");

			if (bCell)
			{	// The cell has the formatting for this text
				if (t == "")
					tw.Write("<br />");		// must have something in cell for formating
				else
					tw.Write(t);
			}
			else
			{	// Formatting must be specified
				string cssName = CssAdd(tb.Style, tb, row);	// get the style name for this item

				tw.Write("<div class='{0}'>{1}</div>", cssName, t);
			}

			if (tp == typeof(Rectangle))
				tw.Write("</td>");
		}
		private string SortType(TableCell tc, Textbox tb)
		{
			// return of null means don't sort
			if (tb == null || !IsTableSortable(tc.OwnerTable))
				return null;

			// default is true if table is sortable;
			//   but user may place override on Textbox custom tag
			if (tb.Custom != null)
			{
				// Loop thru all the child nodes
				foreach(XmlNode xNodeLoop in tb.Custom.CustomXmlNode.ChildNodes)
				{
					if (xNodeLoop.Name == "HTML")
					{
						if (xNodeLoop.LastChild.InnerText.ToLower() == "false")
						{
							return null;
						}
						break;
					}
				}
			}

			// Must find out the type of the detail column
			Details d = tc.OwnerTable.Details;
			if (d == null)
				return null;
			TableRow tr = d.TableRows.Items[0] as TableRow;
			if (tr == null)
				return null;
			TableCell dtc = tr.TableCells.Items[tc.ColIndex] as TableCell;
			if (dtc == null)
				return null;
			Textbox dtb = dtc.ReportItems.Items[0] as Textbox;
			if (dtb == null)
				return null;

			string sortcmp;
			switch (dtb.Value.Type)
			{
				case TypeCode.DateTime:
					sortcmp = "sort_cmp_date";
					break;
				case TypeCode.Int16:
				case TypeCode.UInt16:
				case TypeCode.Int32:
				case TypeCode.UInt32:
				case TypeCode.Int64:
				case TypeCode.UInt64:
				case TypeCode.Decimal:
				case TypeCode.Single:
				case TypeCode.Double:
					sortcmp = "sort_cmp_number";
					break;
				case TypeCode.String:
					sortcmp = "sort_cmp_string";
					break;
				case TypeCode.Empty:	// Not a type we know how to sort
				default:		
					sortcmp = null;
					break;
			}

			return sortcmp;
		}
 public void Textbox(Textbox tb, string t, Row r)
 {
     object value = tb.Evaluate(report, r);
     tw.Write(value);
 }	
		override public void FinalPass()
		{
			_TableRows.FinalPass();
			if (_Grouping != null)
				_Grouping.FinalPass();
			if (_Sorting != null)
				_Sorting.FinalPass();
			if (_Visibility != null)
			{
				_Visibility.FinalPass();
				if (_Visibility.ToggleItem != null)
				{
					_ToggleTextbox = (Textbox) (OwnerReport.LUReportItems[_Visibility.ToggleItem]);
					if (_ToggleTextbox != null)
						_ToggleTextbox.IsToggle = true;
				}
			}
			return;
		}
 public void Textbox(Textbox tb, string t, Row row)
 {
 }
		public void AddHideDuplicates(Textbox tb)
		{
			if (_HideDuplicates == null)
				_HideDuplicates = new List<Textbox>();
			_HideDuplicates.Add(tb);
		}
		public void Textbox(Textbox tb, string t, Row row)
		{
			if (tb.DataElementOutput != DataElementOutputEnum.Output ||
				tb.DataElementName == null)
				return;
			
			if (rowstart != null)		// In case no items in row are visible
			{							//   we delay until we get one.
//				WriteElement(rowstart);
				rowstart = null;
			}
			t = XmlUtil.XmlAnsi(t);
			if (tb.DataElementStyle == DataElementStyleEnum.AttributeNormal)
			{	// write out as attribute
				WriteAttribute(" {0}='{1}'",
								tb.DataElementName, XmlUtil.EscapeXmlAttribute(t));
			}
			else
			{	// write out as element
				WriteElement("<{0}>{1}</{0}>", tb.DataElementName, t);
			}
		}
        public void Textbox(Textbox tb, string t, Row r)
        {
            object value = tb.Evaluate(report, r);

            tw.Write(value);
        }
		public void Textbox(Textbox tb, string t, Row row)
		{
            if (InTable(tb))
                _Excel.SetCell(_ExcelRow, _ExcelCol, t, GetStyle(tb, row));
            else if (InList(tb))
            {
                _ExcelCol++;
                _Excel.SetCell(_ExcelRow, _ExcelCol, t, GetStyle(tb, row));
            }
        }
		public void Textbox(Textbox tb, string t, Row row)
		{
		}
		public object LastObject=null;	// last object calculated

		static public TextboxRuntime GetTextboxRuntime(Report rpt, Textbox tb)
		{
			TextboxRuntime tbr = rpt.Cache.Get(tb, "txtbox") as TextboxRuntime;
			if (tbr != null)
				return tbr;
			tbr = new TextboxRuntime();
			rpt.Cache.Add(tb, "txtbox", tbr);
			return tbr;
		}