List<StaticColumn> _Items;			// list of StaticColumn

		public StaticColumns(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			StaticColumn sc;
            _Items = new List<StaticColumn>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "StaticColumn":
						sc = new StaticColumn(r, this, xNodeLoop);
						break;
					default:	
						sc=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown StaticColumns element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (sc != null)
					_Items.Add(sc);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For StaticColumns at least one StaticColumn is required.");
			else
                _Items.TrimExcess();
		}
        List<ChartSeries> _Items;			// list of chart series

		public ChartData(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			ChartSeries cs;
            _Items = new List<ChartSeries>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "ChartSeries":
						cs = new ChartSeries(r, this, xNodeLoop);
						break;
					default:	
						cs=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown ChartData element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (cs != null)
					_Items.Add(cs);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For ChartData at least one ChartSeries is required.");
			else
                _Items.TrimExcess();
		}
        List<TableGroup> _Items;			// list of TableGroup entries

		public TableGroups(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			TableGroup tg;
            _Items = new List<TableGroup>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "TableGroup":
						tg = new TableGroup(r, this, xNodeLoop);
						break;
					default:	
						tg=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown TableGroups element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (tg != null)
					_Items.Add(tg);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For TableGroups at least one TableGroup is required.");
			else
                _Items.TrimExcess();
		}
        List<MatrixRow> _Items;			// list of MatrixRow

		public MatrixRows(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			MatrixRow m;
            _Items = new List<MatrixRow>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "MatrixRow":
						m = new MatrixRow(r, this, xNodeLoop);
						break;
					default:	
						m=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown MatrixRows element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (m != null)
					_Items.Add(m);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For MatrixRows at least one MatrixRow is required.");
			else
                _Items.TrimExcess();
		}
        List<SeriesGrouping> _Items;			// list of SeriesGrouping

		public SeriesGroupings(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			SeriesGrouping sg;
            _Items = new List<SeriesGrouping>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "SeriesGrouping":
						sg = new SeriesGrouping(r, this, xNodeLoop);
						break;
					default:
						sg=null;		// don't know what this is
						break;
				}
				if (sg != null)
					_Items.Add(sg);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For SeriesGroupings at least one SeriesGrouping is required.");
			else
                _Items.TrimExcess();
		}
        List<FilterValue> _Items;			// list of FilterValue

		public FilterValues(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			FilterValue f;
            _Items = new List<FilterValue>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "FilterValue":
						f = new FilterValue(r, this, xNodeLoop);
						break;
					default:	
						f=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown FilterValues element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (f != null)
					_Items.Add(f);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For FilterValues at least one FilterValue is required.");
			else
                _Items.TrimExcess();
		}
        List<SortBy> _Items;			// list of SortBy

		public Sorting(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			SortBy s;
            _Items = new List<SortBy>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "SortBy":
						s = new SortBy(r, this, xNodeLoop);
						break;
					default:	
						s=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown Sorting element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (s != null)
					_Items.Add(s);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "Sorting requires at least one SortBy be defined.");
			else
                _Items.TrimExcess();
		}
        List<DrillthroughParameter> _Items;			// list of report items

		public DrillthroughParameters(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			DrillthroughParameter d;
            _Items = new List<DrillthroughParameter>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "Parameter":
						d = new DrillthroughParameter(r, this, xNodeLoop);
						break;
					default:	
						d=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown Parameters element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (d != null)
					_Items.Add(d);
			}
			if (_Items.Count > 0)
                _Items.TrimExcess();
		}
        bool _ContainsArray;                   // true if any of the parameters is an array reference

		public QueryParameters(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
            _ContainsArray = false;
			QueryParameter q;
            _Items = new List<QueryParameter>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "QueryParameter":
						q = new QueryParameter(r, this, xNodeLoop);
						break;
					default:	
						q=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown QueryParameters element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (q != null)
					_Items.Add(q);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For QueryParameters at least one QueryParameter is required.");
			else
                _Items.TrimExcess();
		}
        List<DataPoint> _Items;			// list of datapoint

		public DataPoints(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			DataPoint dp;
            _Items = new List<DataPoint>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "DataPoint":
						dp = new DataPoint(r, this, xNodeLoop);
						break;
					default:	
						dp=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown DataPoints element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (dp != null)
					_Items.Add(dp);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For DataPoints at least one DataPoint is required.");
			else
                _Items.TrimExcess();
		}
		string _key;				// key for cache when scope is dataset we can cache the result
		/// <summary>
		/// Aggregate function: Stdevp = (sqrt(n sum(square(x)) - square((sum(x))) / n*n)
		/// Stdev assumes values are a sample of the population of data.  If the data
		/// is the entire representation then use Stdevp.
		/// 
		///	Return type is decimal for decimal expressions and double for all
		///	other expressions.	
		/// </summary>
        public FunctionAggrStdevp(List<ICacheData> dataCache, IExpr e, object scp)
            : base(e, scp) 
		{
			_key = "aggrstdevp" + Interlocked.Increment(ref Parser.Counter).ToString();

			dataCache.Add(this);
		}
		string _key;			// key used for caching value
		/// <summary>
		/// Aggregate function: CountDistinct
		/// 
		///	Return type is double
		/// </summary>
        public FunctionAggrCountDistinct(List<ICacheData> dataCache, IExpr e, object scp)
            : base(e, scp) 
		{
			_key = "countdistinct" + Interlocked.Increment(ref Parser.Counter).ToString();

			dataCache.Add(this);
		}
		public RowGroupings(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			RowGrouping g;
            _Items = new List<RowGrouping>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "RowGrouping":
						g = new RowGrouping(r, this, xNodeLoop);
						break;
					default:	
						g=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown RowGroupings element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (g != null)
					_Items.Add(g);
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For RowGroupings at least one RowGrouping is required.");
			else
			{
                _Items.TrimExcess();
				_StaticCount = GetStaticCount();
			}
		}
        List<TableCell> _Items;			// list of TableCell

		public TableCells(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
			TableCell tc;
            _Items = new List<TableCell>();
			// Loop thru all the child nodes
			int colIndex=0;			// keep track of the column numbers
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				switch (xNodeLoop.Name)
				{
					case "TableCell":
						tc = new TableCell(r, this, xNodeLoop, colIndex);
						colIndex += tc.ColSpan;
						break;
					default:	
						tc=null;		// don't know what this is
						// don't know this element - log it
						OwnerReport.rl.LogError(4, "Unknown TableCells element '" + xNodeLoop.Name + "' ignored.");
						break;
				}
				if (tc != null)
					_Items.Add(tc);
			}
			if (_Items.Count > 0)
                _Items.TrimExcess();
		}
		string _key;				// key for caching; when scope is dataset we can cache the result
		/// <summary>
		/// Aggregate function: Min returns the lowest value
		///	Return type is same as input expression	
		/// </summary>
        public FunctionAggrMin(List<ICacheData> dataCache, IExpr e, object scp)
            : base(e, scp) 
		{
			_key = "aggrmax" + Interlocked.Increment(ref Parser.Counter).ToString();

			// Determine the result
			_tc = e.GetTypeCode();
			dataCache.Add(this);
		}
		string _key;				// key for retaining prior rows values
		/// <summary>
		/// Aggregate function: RunningValue Sum returns the sum of all values of the
		///		expression within the scope up to that row
		///	Return type is decimal for decimal expressions and double for all
		///	other expressions.	
		/// </summary>
        public FunctionAggrRvAvg(List<ICacheData> dataCache, IExpr e, object scp)
            : base(e, scp) 
		{
			_key = "aggrrvavg" + Interlocked.Increment(ref Parser.Counter).ToString();

			// Determine the result
			_tc = e.GetTypeCode();
			if (_tc != TypeCode.Decimal)	// if not decimal
				_tc = TypeCode.Double;		// force result to double
			dataCache.Add(this);
		}
        List<ReportClass> _Items;			// list of report class

		public Classes(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
            _Items = new List<ReportClass>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				if (xNodeLoop.Name == "Class")
				{
					ReportClass rc = new ReportClass(r, this, xNodeLoop);
					_Items.Add(rc);
				}
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For Classes at least one Class is required.");
			else
                _Items.TrimExcess();
		}
		public MemoryStreamGen(string prefix, string suffix, string extension)
		{
			Prefix = prefix;
			Suffix = suffix;
			Extension = extension;

			_io = new MemoryStream();
            _MemoryList = new List<MemoryStream>();
			_MemoryList.Add(_io);
			_MemoryNames = new List<string>();

			// create the first name
			string unique = Interlocked.Increment(ref MemoryStreamGen.Counter).ToString();
			string name;
			if (Prefix == null)
				name = "a" + Extension + "&unique=" + unique;
			else
				name = Prefix + Extension + "&unique=" + unique;

			_MemoryNames.Add(name);
		}
        List<EmbeddedImage> _Items;			// list of EmbeddedImage

		public EmbeddedImages(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
            _Items = new List<EmbeddedImage>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				if (xNodeLoop.Name == "EmbeddedImage")
				{
					EmbeddedImage ei = new EmbeddedImage(r, this, xNodeLoop);
					_Items.Add(ei);
				}
				else
					this.OwnerReport.rl.LogError(4, "Unknown Report element '" + xNodeLoop.Name + "' ignored.");
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For EmbeddedImages at least one EmbeddedImage is required.");
			else
                _Items.TrimExcess();
		}
        List<CodeModule> _Items;			// list of code module

		public CodeModules(ReportDefn r, ReportLink p, XmlNode xNode) : base(r, p)
		{
            _Items = new List<CodeModule>();
			// Loop thru all the child nodes
			foreach(XmlNode xNodeLoop in xNode.ChildNodes)
			{
				if (xNodeLoop.NodeType != XmlNodeType.Element)
					continue;
				if (xNodeLoop.Name == "CodeModule")
				{
					CodeModule cm = new CodeModule(r, this, xNodeLoop);
					_Items.Add(cm);
				}
				else
				{
					// don't know this element - log it
					OwnerReport.rl.LogError(4, "Unknown CodeModules element '" + xNodeLoop.Name + "' ignored.");
				}
			}
			if (_Items.Count == 0)
				OwnerReport.rl.LogError(8, "For CodeModules at least one CodeModule is required.");
			else
                _Items.TrimExcess();
		}
        private void BuildContextMenusCustom(List<MenuItem> items)
        {
            try
            {
                string[] sa = RdlEngineConfig.GetCustomReportTypes();
                if (sa == null || sa.Length == 0)
                    return;

                items.Add(new MenuItem("-"));       // put a separator
                // Add the custom report items to the insert menu
                foreach (string m in sa)
                {
                    MenuItem mi = new MenuItem(m+"...", new EventHandler(this.menuInsertCustomReportItem_Click));
                    mi.Tag = m;
                    items.Add(mi);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("Error building CustomReportItem menus: {0}", ex.Message), "Insert", MessageBoxButtons.OK);
            }   
        }
		private void BuildContextMenus()
		{
			// EDIT MENU
			menuCopy = new MenuItem("&Copy", new EventHandler(this.menuCopy_Click));
			menuPaste = new MenuItem("Paste", new EventHandler(this.menuPaste_Click));
			menuDelete = new MenuItem("&Delete", new EventHandler(this.menuDelete_Click));
			menuFSep1 = new MenuItem("-");
			menuSelectAll = new MenuItem("Select &All", new EventHandler(this.menuSelectAll_Click));
			menuFSep2 = new MenuItem("-");

            List<MenuItem> insertItems = new List<MenuItem>();
            insertItems.Add(new MenuItem("&Chart...", new EventHandler(this.menuInsertChart_Click)));
            insertItems.Add(new MenuItem("&Grid", new EventHandler(this.menuInsertGrid_Click)));
            insertItems.Add(new MenuItem("&System.Drawing.Image", new EventHandler(this.menuInsertImage_Click)));
            insertItems.Add(new MenuItem("&Line", new EventHandler(this.menuInsertLine_Click)));
			insertItems.Add(new MenuItem("&List", new EventHandler(this.menuInsertList_Click)));
			insertItems.Add(new MenuItem("&Matrix...", new EventHandler(this.menuInsertMatrix_Click)));
            insertItems.Add(new MenuItem("&System.Drawing.Rectangle", new EventHandler(this.menuInsertRectangle_Click)));
            insertItems.Add(new MenuItem("&Subreport", new EventHandler(this.menuInsertSubreport_Click)));
            insertItems.Add(new MenuItem("Ta&ble...", new EventHandler(this.menuInsertTable_Click)));
            insertItems.Add(new MenuItem("&Textbox", new EventHandler(this.menuInsertTextbox_Click)));
            // Now add any CustomReportItems
            BuildContextMenusCustom(insertItems);

			menuInsert = new MenuItem("&Insert");
			menuInsert.MenuItems.AddRange(insertItems.ToArray());

			menuProperties = new MenuItem("&Properties...", new EventHandler(this.menuProperties_Click));

			//
			// Create chart context menu and add array of sub-menu items
			menuPropertiesLegend = new MenuItem("Legend...", new EventHandler(this.menuPropertiesLegend_Click));
			menuPropertiesCategoryAxis = new MenuItem("Category (X) Axis...", new EventHandler(this.menuPropertiesCategoryAxis_Click));
			menuPropertiesValueAxis = new MenuItem("Value (Y) Axis...", new EventHandler(this.menuPropertiesValueAxis_Click));
			
			menuPropertiesCategoryAxisTitle = new MenuItem("Category (X) Axis Title...", new EventHandler(this.menuPropertiesCategoryAxisTitle_Click));
			menuPropertiesValueAxisTitle = new MenuItem("Value (Y) Axis Title...", new EventHandler(this.menuPropertiesValueAxisTitle_Click));
            menuPropertiesValueAxis2Title = new MenuItem("Value (Y) Axis (Right) Title...", new EventHandler(this.menuPropertiesValueAxis2Title_Click));
			menuPropertiesChartTitle = new MenuItem("Title...", new EventHandler(this.menuPropertiesChartTitle_Click));

		}
		private void menuTableProperties_Click(object sender, EventArgs e)
		{
			if (_DrawPanel.SelectedCount != 1)
				return;
			XmlNode riNode = _DrawPanel.SelectedList[0];
			XmlNode table = _DrawPanel.GetTableFromReportItem(riNode);
			if (table == null)
				return;
			XmlNode tc = _DrawPanel.GetTableColumn(riNode);
			XmlNode tr = _DrawPanel.GetTableRow(riNode);

			List<XmlNode> ar = new List<XmlNode>();		// need to put this is a list for dialog to handle
			ar.Add(table);
			_Undo.StartUndoGroup("Table Dialog");
			PropertyDialog pd = new PropertyDialog(_DrawPanel, ar, PropertyTypeEnum.ReportItems, tc, tr);
            try
            {
                DialogResult dr = pd.ShowDialog(this);
                _Undo.EndUndoGroup(pd.Changed || dr == DialogResult.OK);
                if (pd.Changed || dr == DialogResult.OK)
                {
                    ReportChanged(this, new EventArgs());
                    _DrawPanel.Invalidate();
                }
            }
            finally
            {
                pd.Dispose();
            }
		}
		private void menuTableInsertGroup_Click(object sender, EventArgs e)
		{
			if (_DrawPanel.SelectedCount != 1)
				return;
			XmlNode cNode = _DrawPanel.SelectedList[0];
			_Undo.StartUndoGroup("Insert Table Group");
			XmlNode tblGroup = _DrawPanel.InsertTableGroup(cNode);
			if (tblGroup == null)
			{
				_Undo.EndUndoGroup(false);
				return;
			}

			List<XmlNode> ar = new List<XmlNode>();		// need to put this is a list for dialog to handle
			ar.Add(tblGroup);
			PropertyDialog pd = new PropertyDialog(_DrawPanel, ar, PropertyTypeEnum.Grouping);
            try
            {
                DialogResult dr = pd.ShowDialog(this);
                if (pd.Changed || dr == DialogResult.OK)
                {
                    _Undo.EndUndoGroup(true);
                    ReportChanged(this, new EventArgs());
                    _DrawPanel.Invalidate();
                }
                else
                {
                    _DrawPanel.DeleteTableGroup(tblGroup);
                    _Undo.EndUndoGroup(false);
                }
            }
            finally
            {
                pd.Dispose();
            }
		}
		private void menuTableEditGroup_Click(object sender, EventArgs e)
		{
			if (_DrawPanel.SelectedCount != 1)
				return;
			MenuItem menu = sender as MenuItem;
			if (menu == null)
				return;
			string gname = menu.Text;
			XmlNode cNode = _DrawPanel.SelectedList[0];

			_Undo.StartUndoGroup("Dialog Table Group Edit");
			XmlNode tblGroup = _DrawPanel.GetTableGroup(cNode, gname);

			List<XmlNode> ar = new List<XmlNode>();		// need to put this is a list for dialog to handle
			ar.Add(tblGroup);
			PropertyDialog pd = new PropertyDialog(_DrawPanel, ar, PropertyTypeEnum.Grouping);
            try
            {
                DialogResult dr = pd.ShowDialog(this);
                _Undo.EndUndoGroup(pd.Changed || dr == DialogResult.OK);
                if (pd.Changed || dr == DialogResult.OK)
                {
                    ReportChanged(this, new EventArgs());
                    _DrawPanel.Invalidate();
                }
            }
            finally
            {
                pd.Dispose();
            }
		}
		private void GetStartupState()
		{
			string optFileName = AppDomain.CurrentDomain.BaseDirectory + "designerstate.xml";
			_RecentFiles = new SortedList<DateTime, string>();
			_CurrentFiles = new List<string>();
			_HelpUrl = DefaultHelpUrl;				// set as default
			_SupportUrl = DefaultSupportUrl;
			
			try
			{
				XmlDocument xDoc = new XmlDocument();
				xDoc.PreserveWhitespace = false;
				xDoc.Load(optFileName);
				XmlNode xNode;
				xNode = xDoc.SelectSingleNode("//designerstate");

				string[] args = Environment.GetCommandLineArgs();
				for (int i=1; i < args.Length; i++)
				{
					string larg = args[i].ToLower();
					if (larg == "/m" || larg == "-m")
						continue;

					if (File.Exists(args[i]))			// only add it if it exists
						_CurrentFiles.Add(args[i]);
				}

				// Loop thru all the child nodes
				foreach(XmlNode xNodeLoop in xNode.ChildNodes)
				{
					switch (xNodeLoop.Name)
					{
						case "RecentFiles":
							DateTime now = DateTime.Now;
							now = now.Subtract(new TimeSpan(0,1,0,0,0));	// subtract an hour
							foreach (XmlNode xN in xNodeLoop.ChildNodes)
							{
								string file = xN.InnerText.Trim();
								if (File.Exists(file))			// only add it if it exists
								{
									_RecentFiles.Add(now, file);
									now = now.AddSeconds(1);
								}
							}
							break;
						case "RecentFilesMax":
							try
							{
								this._RecentFilesMax = Convert.ToInt32(xNodeLoop.InnerText);
							}
							catch 
							{
								this._RecentFilesMax = 5;
							}
							break;
						case "CurrentFiles":
							if (_CurrentFiles.Count > 0)	// don't open other current files if opened with argument
								break;
							foreach (XmlNode xN in xNodeLoop.ChildNodes)
							{
								string file = xN.InnerText.Trim();
								if (File.Exists(file))			// only add it if it exists
									_CurrentFiles.Add(file);
							}
							break;
						case "Toolbar":
							_Toolbar = new List<string>();
							foreach (XmlNode xN in xNodeLoop.ChildNodes)
							{
								string item = xN.InnerText.Trim();
								_Toolbar.Add(item);
							}
							break;
						case "Help":
							if (xNodeLoop.InnerText.Length > 0)		//empty means to use the default
								_HelpUrl = xNodeLoop.InnerText;
							break;
						case "Support":
							if (xNodeLoop.InnerText.Length > 0)		//empty means to use the default
								_SupportUrl = xNodeLoop.InnerText;
							break;
                        case "EditLines":
                            _ShowEditLines = (xNodeLoop.InnerText.ToLower() == "true") ;
                            break;
                        case "ShowPreviewWaitDialog":
                            _ShowPreviewWaitDialog = (xNodeLoop.InnerText.ToLower() == "true");
                            break;
                        case "OutlineReportItems":
                            this.ShowReportItemOutline = (xNodeLoop.InnerText.ToLower() == "true");
                            break;
                        case "ShowTabbedInterface":
                            this._ShowTabbedInterface = (xNodeLoop.InnerText.ToLower() == "true");
                            break;
                        case "PropertiesLocation":
                            this._PropertiesLocation = GetPropertiesDockStyle(xNodeLoop.InnerText);
                            break;
                        case "PropertiesAutoHide":
                            this._PropertiesAutoHide = (xNodeLoop.InnerText.ToLower() == "true");
                            break;
                        case "MapSubtypes":
                            RdlDesigner.MapSubtypes = xNodeLoop.InnerText.Split(new char[] { ',' });
                            break;

                        default:
							break;
					}
				}
			}
			catch (Exception ex)
			{		// Didn't sucessfully get the startup state but don't really care
				Console.WriteLine(string.Format("Exception in GetStartupState ignored.\n{0}\n{1}", ex.Message, ex.StackTrace));
			}

			if (_Toolbar == null)		// Use this as the default toolbar
				_Toolbar = this.ToolbarDefault;
			return;
		}
		// FuncIDent: IDENTIFIER ( [Expr] [, Expr]*) | IDENTIFIER
		private bool MatchFuncIDent(out IExpr result)
		{
			IExpr e;
			string fullname;			// will hold the full name
			string method;				// will hold method name or second part of name
			string firstPart;			// will hold the collection name
			string thirdPart;			// will hold third part of name
			bool bOnePart;				// simple name: no ! or . in name

			result = null;

			if (curToken.Type != TokenTypes.IDENTIFIER)
				return false;

			// Disentangle method calls from collection references
			method = fullname = curToken.Value;
			curToken = tokens.Extract();

			// Break the name into parts
			char[] breakChars = new char[] {'!', '.'};

			int posBreak = method.IndexOfAny(breakChars);
			if (posBreak > 0)
			{
				bOnePart = false;
				firstPart = method.Substring(0, posBreak);
				method = method.Substring(posBreak+1);		// rest of expression
			}
			else
			{
				bOnePart = true;
				firstPart = method;
			}

			posBreak = method.IndexOf('.');
			if (posBreak > 0)
			{
				thirdPart = method.Substring(posBreak+1);	// rest of expression
				method = method.Substring(0, posBreak);
			}
			else
				thirdPart = null;

			if (curToken.Type != TokenTypes.LPAREN) switch (firstPart)
			{
				case "Fields":
					Field f = idLookup.LookupField(method);
					if (f == null && !this._InAggregate)
						throw new ParserException("Field '" + method + "'  not found.");
					if (thirdPart == null || thirdPart == "Value")
					{
						if (f == null)
						{
                            FunctionField ff;
                            result = ff = new FunctionField(method);
							this._FieldResolve.Add(ff);
						}
						else
							result = new FunctionField(f);	
					}
					else if (thirdPart == "IsMissing")
					{
						if (f == null)
						{
                            FunctionField ff;
							result = ff = new FunctionFieldIsMissing(method);
							this._FieldResolve.Add(ff);
						}
						else
							result = new FunctionFieldIsMissing(f);
					}
					else
						throw new ParserException("Field '" + method + "'  only supports 'Value' and 'IsMissing' properties.");
					return true;
                case "Parameters":  // see ResolveParametersMethod for resolution of MultiValue parameter function reference
					ReportParameter p = idLookup.LookupParameter(method);
					if (p == null)
						throw new ParserException("Report parameter '" + method + "'  not found.");
                    int ci = thirdPart == null? -1: thirdPart.IndexOf(".Count");
                    if (ci > 0)
                        thirdPart = thirdPart.Substring(0, ci);
                    FunctionReportParameter r;                    
					if (thirdPart == null || thirdPart == "Value")
						r = new FunctionReportParameter(p);
					else if (thirdPart == "Label")
						r = new FunctionReportParameterLabel(p);
					else
						throw new ParserException("Parameter '" + method + "'  only supports 'Value' and 'Label' properties.");
                    if (ci > 0)
                        r.SetParameterMethod("Count", null);
                    
                    result = r;
                    return true;
				case "ReportItems":
					Textbox t = idLookup.LookupReportItem(method);
					if (t == null)
						throw new ParserException("ReportItem '" + method + "'  not found.");
					if (thirdPart != null && thirdPart != "Value")
						throw new ParserException("ReportItem '" + method + "'  only supports 'Value' property.");
					result = new FunctionTextbox(t, idLookup.ExpressionName);	
					return true;
				case "Globals":
					e = idLookup.LookupGlobal(method);
					if (e == null)
						throw new ParserException("Globals '" + method + "'  not found.");
					result = e;
					return true;
				case "User":
					e = idLookup.LookupUser(method);
					if (e == null)
						throw new ParserException("User variable '" + method + "'  not found.");
					result = e;
					return true;
				case "Recursive":	// Only valid for some aggregate functions
					result = new IdentifierKey(IdentifierKeyEnum.Recursive);
					return true;
				case "Simple":		// Only valid for some aggregate functions
					result = new IdentifierKey(IdentifierKeyEnum.Simple);
					return true;
				default:
					if (!bOnePart)
						throw new ParserException(string.Format("'{0}' is an unknown identifer.", fullname));

					switch (method.ToLower())		// lexer should probably mark these
					{
						case "true":
						case "false":
							result = new ConstantBoolean(method.ToLower());
							break;
						default:
							// usually this is enum that will be used in an aggregate 
							result = new Identifier(method);
							break;
					}
					return true;
			}

			// We've got an function reference
			curToken = tokens.Extract();		// get rid of '('

			// Got a function now obtain the arguments
			int argCount=0;
			
			bool isAggregate = IsAggregate(method, bOnePart);
			if (_NoAggregate && isAggregate)
				throw new ParserException("Aggregate function '" + method + "' cannot be used within a Grouping expression.");
			if (_InAggregate && isAggregate)
				throw new ParserException("Aggregate function '" + method + "' cannot be nested in another aggregate function.");
			_InAggregate = isAggregate;
			if (_InAggregate)
				_FieldResolve = new List<FunctionField>();

            List<IExpr> largs = new List<IExpr>();
			while(true)
			{
				if (curToken.Type == TokenTypes.RPAREN)
				{	// We've got our function
					curToken = tokens.Extract();
					break;
				}
				if (argCount == 0)
				{
					// don't need to do anything
				}
				else if (curToken.Type == TokenTypes.COMMA)
				{
					curToken = tokens.Extract();
				}
				else
					throw new ParserException("Invalid function arguments.  Found '" + curToken.Value + "'  At column " + Convert.ToString(curToken.StartCol));
				
				MatchExprAndOr(out e);
				if (e == null)
					throw new ParserException("Expecting ',' or ')'.  Found '" + curToken.Value + "'  At column " + Convert.ToString(curToken.StartCol));

				largs.Add(e);
				argCount++;
			}
			if (_InAggregate)
			{
				ResolveFields(method, this._FieldResolve, largs);
				_FieldResolve = null;
				_InAggregate = false;
			}

            IExpr[] args = largs.ToArray();

			object scope;
			bool bSimple;
			if (!bOnePart)				
            {
                result = (firstPart == "Parameters")?
                    ResolveParametersMethod(method, thirdPart, args):
                    ResolveMethodCall(fullname, args);	// throw exception when fails
            }
			else switch(method.ToLower())
			{
				case "iif":
					if (args.Length != 3)
						throw new ParserException("iff function requires 3 arguments." + "  At column " + Convert.ToString(curToken.StartCol));
//  We allow any type for the first argument; it will get converted to boolean at runtime
//					if (args[0].GetTypeCode() != TypeCode.Boolean)
//						throw new ParserException("First argument to iif function must be boolean." + "  At column " + Convert.ToString(curToken.StartCol));
					result = new FunctionIif(args[0], args[1], args[2]);
					break;
				case "choose":
					if (args.Length <= 2)
						throw new ParserException("Choose function requires at least 2 arguments." + "  At column " + Convert.ToString(curToken.StartCol));
					switch (args[0].GetTypeCode())
					{
						case TypeCode.Double:
						case TypeCode.Single:
						case TypeCode.Int32:
						case TypeCode.Decimal:
						case TypeCode.Int16:
						case TypeCode.Int64:
							break;
						default:
							throw new ParserException("First argument to Choose function must be numeric." + "  At column " + Convert.ToString(curToken.StartCol));
					}
					result = new FunctionChoose(args);
					break;
				case "switch":
					if (args.Length <= 2)
						throw new ParserException("Switch function requires at least 2 arguments." + "  At column " + Convert.ToString(curToken.StartCol));
				    if (args.Length % 2 != 0)
						throw new ParserException("Switch function must have an even number of arguments." + "  At column " + Convert.ToString(curToken.StartCol));
					for (int i=0; i < args.Length; i = i+2)
					{
						if (args[i].GetTypeCode() != TypeCode.Boolean)
							throw new ParserException("Switch function must have a boolean expression every other argument." + "  At column " + Convert.ToString(curToken.StartCol));
					}
					result = new FunctionSwitch(args);
					break;
				case "format":
					if (args.Length > 2 || args.Length < 1)
						throw new ParserException("Format function requires 2 arguments." + "  At column " + Convert.ToString(curToken.StartCol));
					if (args.Length == 1)
					{
						result = new FunctionFormat(args[0], new ConstantString(""));
					}
					else
					{
						if (args[1].GetTypeCode() != TypeCode.String)
							throw new ParserException("Second argument to Format function must be a string." + "  At column " + Convert.ToString(curToken.StartCol));
						result = new FunctionFormat(args[0], args[1]);
					}
					break;

				case "fields":
					if (args.Length != 1)
						throw new ParserException("Fields collection requires exactly 1 argument." + "  At column " + Convert.ToString(curToken.StartCol));
					result = new FunctionFieldCollection(idLookup.Fields, args[0]);
                    if (curToken.Type == TokenTypes.DOT)
                    {	// user placed "."                  TODO: generalize this code
                        curToken = tokens.Extract();                // skip past dot operator
                        if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value")
                            curToken = tokens.Extract();            // only support "value" property for now
                        else
                            throw new ParserException(curToken.Value + " is not a known property for Fields." + "  At column " + Convert.ToString(curToken.StartCol));
                    }
                    break;
				case "parameters":
					if (args.Length != 1)
						throw new ParserException("Parameters collection requires exactly 1 argument." + "  At column " + Convert.ToString(curToken.StartCol));
					result = new FunctionParameterCollection(idLookup.Parameters, args[0]);
                    if (curToken.Type == TokenTypes.DOT)
                    {	// user placed "." 
                        curToken = tokens.Extract();                // skip past dot operator
                        if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value")
                            curToken = tokens.Extract();            // only support "value" property for now
                        else
                            throw new ParserException(curToken.Value + " is not a known property for Fields." + "  At column " + Convert.ToString(curToken.StartCol));
                    }
                    break;
				case "reportitems":
					if (args.Length != 1)
						throw new ParserException("ReportItems collection requires exactly 1 argument." + "  At column " + Convert.ToString(curToken.StartCol));
					result = new FunctionReportItemCollection(idLookup.ReportItems, args[0]);
                    if (curToken.Type == TokenTypes.DOT)
                    {	// user placed "." 
                        curToken = tokens.Extract();                // skip past dot operator
                        if (curToken.Type == TokenTypes.IDENTIFIER && curToken.Value.ToLowerInvariant() == "value")
                            curToken = tokens.Extract();            // only support "value" property for now
                        else
                            throw new ParserException(curToken.Value + " is not a known property for Fields." + "  At column " + Convert.ToString(curToken.StartCol));
                    }
                    break;
				case "globals":
					if (args.Length != 1)
						throw new ParserException("Globals collection requires exactly 1 argument." + "  At column " + Convert.ToString(curToken.StartCol));
					result = new FunctionGlobalCollection(idLookup.Globals, args[0]);
					break;
				case "user":
					if (args.Length != 1)
						throw new ParserException("User collection requires exactly 1 argument." + "  At column " + Convert.ToString(curToken.StartCol));
					result = new FunctionUserCollection(idLookup.User, args[0]);
					break;
				case "sum":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrSum aggrFS = new FunctionAggrSum(_DataCache, args[0], scope);
					aggrFS.LevelCheck = bSimple;
					result = aggrFS;
					break;
				case "avg":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrAvg aggrFA = new FunctionAggrAvg(_DataCache, args[0], scope);
					aggrFA.LevelCheck = bSimple;
					result = aggrFA;
					break;
				case "min":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrMin aggrFMin = new FunctionAggrMin(_DataCache, args[0], scope);
					aggrFMin.LevelCheck = bSimple;
					result = aggrFMin;
					break;
				case "max":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrMax aggrFMax = new FunctionAggrMax(_DataCache, args[0], scope);
					aggrFMax.LevelCheck = bSimple;
					result = aggrFMax;
					break;
				case "first":
					scope = ResolveAggrScope(args, 2, out bSimple);
					result = new FunctionAggrFirst(_DataCache, args[0], scope);
					break;
				case "last":
					scope = ResolveAggrScope(args, 2, out bSimple);
					result = new FunctionAggrLast(_DataCache, args[0], scope);
					break;
				case "next":
					scope = ResolveAggrScope(args, 2, out bSimple);
					result = new FunctionAggrNext(_DataCache, args[0], scope);
					break;
				case "previous":
				    scope = ResolveAggrScope(args, 2, out bSimple);
					result = new FunctionAggrPrevious(_DataCache, args[0], scope);
					break;
				case "level":
					scope = ResolveAggrScope(args, 1, out bSimple);
					result = new FunctionAggrLevel(scope);
					break;
                case "aggregate":
                    scope = ResolveAggrScope(args, 2, out bSimple);
                    FunctionAggrArray aggr = new FunctionAggrArray(_DataCache, args[0], scope);
                    aggr.LevelCheck = bSimple;
                    result = aggr;
                    break;
                case "count":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrCount aggrFC = new FunctionAggrCount(_DataCache, args[0], scope);
					aggrFC.LevelCheck = bSimple;
					result = aggrFC;
					break;
				case "countrows":
					scope = ResolveAggrScope(args, 1, out bSimple);
					FunctionAggrCountRows aggrFCR = new FunctionAggrCountRows(scope);
					aggrFCR.LevelCheck = bSimple;
					result = aggrFCR;
					break;
                case "countdistinct":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrCountDistinct aggrFCD = new FunctionAggrCountDistinct(_DataCache, args[0], scope);
					aggrFCD.LevelCheck = bSimple;
					result = aggrFCD;
					break;
				case "rownumber":
					scope = ResolveAggrScope(args, 1, out bSimple);
					IExpr texpr = new ConstantDouble("0");
					result = new FunctionAggrRvCount(_DataCache, texpr, scope);
					break;
				case "runningvalue":
					if (args.Length < 2 || args.Length > 3)
						throw new ParserException("RunningValue takes 2 or 3 arguments." + "  At column " + Convert.ToString(curToken.StartCol));
					string aggrFunc = args[1].EvaluateString(null, null);
					if (aggrFunc == null)
						throw new ParserException("RunningValue 'Function' argument is invalid." + "  At column " + Convert.ToString(curToken.StartCol));
					scope = ResolveAggrScope(args, 3, out bSimple);
					switch(aggrFunc.ToLower())
					{
						case "sum":
							result = new FunctionAggrRvSum(_DataCache, args[0], scope);
							break;
						case "avg":
							result = new FunctionAggrRvAvg(_DataCache, args[0], scope);
							break;
						case "count":
							result = new FunctionAggrRvCount(_DataCache, args[0], scope);
							break;
						case "max":
							result = new FunctionAggrRvMax(_DataCache, args[0], scope);
							break;
						case "min":
							result = new FunctionAggrRvMin(_DataCache, args[0], scope);
							break;
						case "stdev":
							result = new FunctionAggrRvStdev(_DataCache, args[0], scope);
							break;
						case "stdevp":
							result = new FunctionAggrRvStdevp(_DataCache, args[0], scope);
							break;
						case "var":
							result = new FunctionAggrRvVar(_DataCache, args[0], scope);
							break;
						case "varp":
							result = new FunctionAggrRvVarp(_DataCache, args[0], scope);
							break;
						default:
							throw new ParserException("RunningValue function '" + aggrFunc + "' is not supported.  At column " + Convert.ToString(curToken.StartCol));
					}
					break;
				case "stdev":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrStdev aggrSDev = new FunctionAggrStdev(_DataCache, args[0], scope);
					aggrSDev.LevelCheck = bSimple;
					result = aggrSDev;
					break;
				case "stdevp":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrStdevp aggrSDevP = new FunctionAggrStdevp(_DataCache, args[0], scope);
					aggrSDevP.LevelCheck = bSimple;
					result = aggrSDevP;
					break;
				case "var":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrVar aggrVar = new FunctionAggrVar(_DataCache, args[0], scope);
					aggrVar.LevelCheck = bSimple;
					result = aggrVar;
					break;
				case "varp":
					scope = ResolveAggrScope(args, 2, out bSimple);
					FunctionAggrVarp aggrVarP = new FunctionAggrVarp(_DataCache, args[0], scope);
					aggrVarP.LevelCheck = bSimple;
					result = aggrVarP;
					break;
				default:
					result = ResolveMethodCall(fullname, args);		// through exception when fails
					break;
			}

			return true;
		}
		private void BuildPrivate(Graphics g)
        {
            PageText model = new PageText("");
            model.AllowSelect = false;
            model.Page = this.Page;
			model.HyperLink=null;
			model.Tooltip=null;
			int fontSizeModel = 3;

			if (_items != null)		// this has already been built
				return;
            _items = new List<PageItem>();
			_StyleStack = new Stack();

			// The first item is always a text box with the border and background attributes
			PageText pt = new PageText("");
            pt.AllowSelect = true;                // This item represents HTML item for selection in RdlViewer
            pt.Page = this.Page;
            pt.HtmlParent = this;
			pt.X = this.X;
			pt.Y = this.Y;
			pt.H = this.H;
			pt.W = this.W;
			pt.CanGrow = false;
			pt.SI = this.SI.Clone() as StyleInfo;
			pt.SI.PaddingBottom = pt.SI.PaddingLeft = pt.SI.PaddingRight = pt.SI.PaddingTop = 0;
			pt.SI.TextAlign = TextAlignEnum.Left;
			_items.Add(pt);

			// Now we create multiple items that represent what is in the box
			PageTextHtmlLexer hl = new PageTextHtmlLexer(this.Text);
			List<string> tokens = hl.Lex();

			float textWidth = this.W - pt.SI.PaddingLeft - pt.SI.PaddingRight;
			// Now set the default style for the rest of the members
			StyleInfo si = this.SI.Clone() as StyleInfo;
			si.BStyleBottom = si.BStyleLeft = si.BStyleRight = si.BStyleTop = BorderStyleEnum.None;
			pt.SI.TextAlign = TextAlignEnum.Left;
			pt.SI.VerticalAlign = VerticalAlignEnum.Top;
			si.BackgroundColor = Color.Empty;
			si.BackgroundGradientType = BackgroundGradientTypeEnum.None;
			si.BackgroundImage = null;

			bool bFirstInLine=true;
			StringBuilder sb = new StringBuilder(); // this will hold the accumulating line
			float lineXPos=0;
			float xPos = 0;
			float yPos = 0;
			float maxLineHeight=0;
			float maxDescent=0;
			float descent;				// working value for descent
			SizeF ms;
			bool bWhiteSpace=false;
			List<PageItem> lineItems = new List<PageItem>();
			foreach (string token in tokens)
			{
				if (token[0] == PageTextHtmlLexer.HTMLCMD)		// indicates an HTML command
				{
					// we need to create a PageText since the styleinfo is changing
					if (sb.Length != 0)
					{
						pt = new PageText(sb.ToString());
                        pt.AllowSelect = false;
                        pt.Page = this.Page;
                        pt.HtmlParent = this;
						pt.HyperLink = model.HyperLink;
						pt.Tooltip = model.Tooltip;
						pt.NoClip = true;
						sb = new StringBuilder();
						pt.X = this.X + lineXPos;
						pt.Y = this.Y + yPos;
						pt.CanGrow = false;
						pt.SI = CurrentStyle(si).Clone() as StyleInfo;
						_items.Add(pt);
						lineItems.Add(pt);
						ms = this.MeasureString(pt.Text, pt.SI, g, out descent);
						maxDescent = Math.Max(maxDescent, descent);
						pt.W = ms.Width;
						pt.H = ms.Height;
						pt.Descent = descent;
						maxLineHeight = Math.Max(maxLineHeight, ms.Height);
						lineXPos = xPos;
					}
					// Now reset the styleinfo
					StyleInfo cs = CurrentStyle(si);
					string ltoken = token.Substring(1,Math.Min(token.Length-1,10)).ToLower();
					if (ltoken == "<b>" || ltoken == "<strong>")
						cs.FontWeight = FontWeightEnum.Bold;
					else if (ltoken == "</b>" || ltoken == "</strong>")
						cs.FontWeight = FontWeightEnum.Normal;
					else if (ltoken == "<i>" || ltoken == "<cite>" || ltoken == "<var>" || ltoken == "<em>")
						cs.FontStyle = FontStyleEnum.Italic;
					else if (ltoken == "</i>" || ltoken == "</cite>" || ltoken == "</var>" || ltoken == "</em>")
						cs.FontStyle = FontStyleEnum.Normal;
					else if (ltoken == "<code>" || ltoken == "<samp>")
						cs.FontFamily = "Courier New";
					else if (ltoken == "</code>" || ltoken == "</samp>")
						cs.FontFamily = this.SI.FontFamily;
					else if (ltoken == "<kbd>")
					{
						cs.FontFamily = "Courier New";
						cs.FontWeight = FontWeightEnum.Bold;
					}
					else if (ltoken == "</kdd>")
					{
						cs.FontFamily = this.SI.FontFamily;
						cs.FontWeight = FontWeightEnum.Normal;
					}
					else if (ltoken == "<big>")
					{	// big makes it bigger by 20% for each time over the baseline of 3
						fontSizeModel++;
						float inc = 1;
						for (int i=3; i < fontSizeModel; i++)
						{
							inc += .2f;
						}
						float h = this.SI.FontSize * inc;
						cs.FontSize = h;
					}
					else if (ltoken == "</big>")
					{	// undoes the effect of big
						fontSizeModel--;
						float inc = 1;
						for (int i=3; i < fontSizeModel; i++)
						{
							inc += .2f;
						}
						float h = this.SI.FontSize / inc;
						cs.FontSize = h;
					}
					else if (ltoken == "<small>")
					{	// small makes it smaller by 20% for each time under the baseline of 3
						fontSizeModel--;
						float inc = 1;
						for (int i=3; i > fontSizeModel; i--)
						{
							inc += .2f;
						}
						float h = this.SI.FontSize / inc;
						cs.FontSize = h;
					}
					else if (ltoken == "</small>")
					{	// undoes the effect of small
						fontSizeModel++;
						float inc = 1;
						for (int i=3; i > fontSizeModel; i--)
						{
							inc += .2f;
						}
						float h = this.SI.FontSize * inc;
						cs.FontSize = h;
					}
					else if (ltoken.StartsWith("<br"))
					{
						yPos += maxLineHeight;
						NormalizeLineHeight(lineItems, maxLineHeight, maxDescent);
						maxLineHeight = xPos = lineXPos = maxDescent = 0;
						bFirstInLine = true;
						bWhiteSpace = false;
					}
                    else if (ltoken.StartsWith("<hr"))
                    {   // Add a line
                        // Process existing line if any
                        yPos += maxLineHeight;
                        NormalizeLineHeight(lineItems, maxLineHeight, maxDescent);
                        maxLineHeight = xPos = lineXPos = maxDescent = 0;
                        bFirstInLine = true;
                        bWhiteSpace = false;

                        PageLine pl = new PageLine();
                        pl.AllowSelect = false;
                        pl.Page = this.Page;
                        const int horzLineHeight = 10;
                        pl.SI = cs.Clone() as StyleInfo;
                        pl.SI.BStyleLeft = BorderStyleEnum.Ridge;
                        pl.Y = pl.Y2 = this.Y + yPos + horzLineHeight / 2;
                        pl.X = this.X;
                        pl.X2 = pl.X + this.W;
                        _items.Add(pl);
                        yPos += horzLineHeight;  // skip past horizontal line
                    }
                    else if (ltoken.StartsWith("<p"))
					{
						yPos += maxLineHeight * 2;
						NormalizeLineHeight(lineItems, maxLineHeight, maxDescent);
						maxLineHeight = xPos = lineXPos = maxDescent = 0;
						bFirstInLine = true;
						bWhiteSpace = false;
					}
					else if (ltoken.StartsWith("<a"))
					{
						BuildAnchor(token.Substring(1), cs, model);
					}
                    else if (ltoken.StartsWith("<img"))
                    {
                        PageImage pimg = BuildImage(g, token.Substring(1), cs, model);
                        if (pimg != null)   // We got an image; add to process list
                        {
                            pimg.Y = this.Y + yPos;
                            pimg.X = this.X;
                            _items.Add(pimg);
                            yPos += pimg.H;	        // Increment y position
                            maxLineHeight = xPos = lineXPos = maxDescent = 0;
                            bFirstInLine = true;
                            bWhiteSpace = false;
                        }
                    }
                    else if (ltoken == "</a>")
                    {
                        model.HyperLink = model.Tooltip = null;
                        PopStyle();
                    }
                    else if (ltoken.StartsWith("<span"))
                    {
                        HandleStyle(token.Substring(1), si);
                    }
                    else if (ltoken == "</span>")
                    {   // we really should match span and font but it shouldn't matter very often?
                        PopStyle();
                    }
                    else if (ltoken.StartsWith("<font"))
                    {
                        HandleFont(token.Substring(1), si);
                    }
                    else if (ltoken == "</font>")
                    {   // we really should match span and font but it shouldn't matter very often?
                        PopStyle();
                    }
                    continue;
				}
				if (token == PageTextHtmlLexer.WHITESPACE)
				{
					if (!bFirstInLine)
						bWhiteSpace = true;
					continue;
				}

				if (token != PageTextHtmlLexer.EOF)
				{
					string ntoken;
                    if (token == PageTextHtmlLexer.NBSP.ToString())
                        ntoken = bWhiteSpace ? "  " : " ";
                    else
                        ntoken = bWhiteSpace ? " " + token : token;
                    ntoken = ntoken.Replace(PageTextHtmlLexer.NBSP, ' ');

					bWhiteSpace = false;			// can only use whitespace once
					ms = this.MeasureString(ntoken, CurrentStyle(si), g, out descent);
					if (xPos + ms.Width < textWidth)
					{
						bFirstInLine = false;
						sb.Append(ntoken);

						maxDescent = Math.Max(maxDescent, descent);
						maxLineHeight = Math.Max(maxLineHeight, ms.Height);
						xPos += ms.Width;
						continue;
					}
				}
				else if (sb.Length == 0)	// EOF and no previous string means we're done
					continue;

				pt = new PageText(sb.ToString());
                pt.AllowSelect = false;
                pt.Page = this.Page;
                pt.HtmlParent = this;
                pt.NoClip = true;
				pt.HyperLink = model.HyperLink;
				pt.Tooltip = model.Tooltip;
				sb = new StringBuilder();
				sb.Append(token.Replace(PageTextHtmlLexer.NBSP, ' '));
				pt.SI = CurrentStyle(si).Clone() as StyleInfo;
				ms = this.MeasureString(pt.Text, pt.SI, g, out descent);
				pt.X = this.X + lineXPos;
				pt.Y = this.Y + yPos;
				pt.H = ms.Height;
				pt.W = ms.Width; 
				pt.Descent = descent;
				pt.CanGrow = false;
				_items.Add(pt);
				lineItems.Add(pt);
				maxDescent = Math.Max(maxDescent, descent);
				maxLineHeight = Math.Max(maxLineHeight, ms.Height);
				yPos += maxLineHeight;	// Increment y position
				NormalizeLineHeight(lineItems, maxLineHeight, maxDescent);
				lineXPos = maxLineHeight = maxDescent = 0;	// start line height over

				// Now set the xPos just after the current token
				ms = this.MeasureString(token, CurrentStyle(si), g, out descent);
				xPos = ms.Width;	 
			}
		
			_TotalHeight = yPos;		// set the calculated height of the result
			_StyleStack = null;
			return;
		}
		private void menuViewBrowser_Click(object sender, System.EventArgs e)
		{
			MDIChild mc = this.ActiveMdiChild as MDIChild;
			if (mc == null)
				return;

			try
			{
				menuToolsStartProcess(true);		// start desktop if not already up

				DesktopConfig dc = DialogToolOptions.DesktopConfiguration;

				string rdlfile = Path.GetFileNameWithoutExtension(mc.SourceFile) + "_" + (++TEMPRDL_INC).ToString() + TEMPRDL;
				string file; 
				if (Path.IsPathRooted(dc.Directory))
					file = dc.Directory + Path.DirectorySeparatorChar + rdlfile;
				else
					file = AppDomain.CurrentDomain.BaseDirectory +  
						 dc.Directory + Path.DirectorySeparatorChar + rdlfile;

				if (_TempReportFiles == null)
				{
					_TempReportFiles = new List<string>();
					_TempReportFiles.Add(file);
				}
				else
				{
					if (!_TempReportFiles.Contains(file))
						_TempReportFiles.Add(file);
				}
				StreamWriter sw = File.CreateText(file);
				sw.Write(mc.SourceRdl);
				sw.Close();
		 // http://localhost:8080/aReport.rdlc?rs:Format=HTML
				string url = string.Format("http://localhost:{0}/{1}?rd:Format=HTML", dc.Port, rdlfile);
				System.Diagnostics.Process.Start(url);
				
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Unable to Show Report");
			}
			
		}
        private void HighlightString(Graphics g, PageText dtext, RectangleF r, Font f, StringFormat sf)
        {
            if (_HighlightText == null || _HighlightText.Length == 0)
                return;         // nothing to highlight
            bool bhighlightItem = dtext == _HighlightItem ||
                    (_HighlightItem != null && dtext.HtmlParent == _HighlightItem);
            if (!(_HighlightAll || bhighlightItem))
                return;         // not highlighting all and not on current highlight item

            string hlt = _HighlightCaseSensitive ? _HighlightText : _HighlightText.ToLower();
            string text = _HighlightCaseSensitive ? dtext.Text : dtext.Text.ToLower();

            if (text.IndexOf(hlt) < 0)
                return;         // string not in text

            StringFormat sf2 = null;
            try
            {
                // Create a CharacterRange array with the highlight location and length
                // Handle multiple occurences of text
                List<CharacterRange> rangel = new List<CharacterRange>();
                int loc = text.IndexOf(hlt);
                int hlen = hlt.Length;
                int len = text.Length;
                while (loc >= 0)
                {
                    rangel.Add(new CharacterRange(loc, hlen));
                    if (loc + hlen < len)  // out of range of text
                        loc = text.IndexOf(hlt, loc + hlen);
                    else
                        loc = -1;
                }

                if (rangel.Count <= 0)      // we should have gotten one; but
                    return;

                CharacterRange[] ranges = rangel.ToArray();

                // Construct a new StringFormat object.
                sf2 = sf.Clone() as StringFormat;

                // Set the ranges on the StringFormat object.
                sf2.SetMeasurableCharacterRanges(ranges);

                // Get the Regions to highlight by calling the 
                // MeasureCharacterRanges method.
                if (r.Width <= 0 || r.Height <= 0)
                {
                    SizeF ts = g.MeasureString(dtext.Text, f);
                    r.Height = ts.Height;
                    r.Width = ts.Width;
                }
                Region[] charRegion = g.MeasureCharacterRanges(dtext.Text, f, r, sf2);

                // Fill in the region using a semi-transparent color to highlight
                foreach (Region rg in charRegion)
                {
                    Color hl = bhighlightItem ? _HighlightItemColor : _HighlightAllColor;
                    g.FillRegion(new SolidBrush(Color.FromArgb(50, hl)), rg);
                }
            }
            catch { }   // if highlighting fails we don't care; need to continue
            finally
            {
                if (sf2 != null)
                    sf2.Dispose();
            }
        }