//******************************************************************
        /// <summary>
        /// Adds a new item to the list of rules. The item is placed in edit
        /// mode so the user can rename the new rule.
        /// </summary>
        public void AddNewRule()
        {
            //**************************************************************
            // Validate the current state.

            if (! CanAddNewRule())
            {
                string sMessage = "Invalid state: "
                    + "A call to RuleListViewer.AddNewRule() is not "
                    + "allowed if RuleListViewer.CanAddNewRule() returns "
                    + "false.";
                throw new Exception(sMessage);
            }

            //**************************************************************
            // Prevent modification to a read-only list.

            if (ReadOnly)
            {
                return;
            }

            //**************************************************************
            // Set the Modified property to true.
            //
            // If the list is empty and Modified is false, adding a blank
            // new rule does not set Modified to true. (Modified will be set
            // to true later if the user makes any changes to the blank new
            // rule, or if the user makes any other changes to the list.)

            if (moListView.Items.Count > 0)
            {
                Modified = true;
            }

            //**************************************************************
            // Clear the selection.

            ClearSelection();

            //**************************************************************
            // Clear the linked FindPatternViewer and ReplacePatternViewer.

            UpdateFindAndReplacePatternViewers(null);

            //**************************************************************
            // Reset the TreeTransferData object.

            ResetTreeTransferData();

            //**************************************************************
            // Clear the undo information.

            ClearUndoInformation();

            //**************************************************************
            // Create a new rule item and append it to the list.

            RuleListViewerItem oRuleItem = new RuleListViewerItem();
            oRuleItem.Text = "(new rule)";
            oRuleItem.FindPatternRoot = new SyntaxNode();
            oRuleItem.ReplacePatternRoot = new SyntaxNode();
            moListView.Items.Add(oRuleItem);

            //**************************************************************
            // Select the item. Make sure it is scrolled into view and has
            // focus within the ListView.

            oRuleItem.Selected = true;
            oRuleItem.EnsureVisible();
            oRuleItem.Focused = true;

            //**************************************************************
            // Raise the SelectionChanged event.

            OnSelectionChanged(new EventArgs());

            //**************************************************************
            // Raise the ListChanged event.

            OnListChanged(new EventArgs());

            //**************************************************************
            // Place the item in edit mode so the user can edit the name of
            // the new rule. (If the user changes the rule name, the
            // AfterLabelEdit event handler will raise the ListChanged
            // event again.)

            oRuleItem.BeginEdit();
        }
        //******************************************************************
        /// <summary>
        /// Uses the .FindPatternRoot and .ReplacePatternRoot properties of
        /// the given oSelectedRule object to populate the linked
        /// FindPatternViewer and ReplacePatternViewer. The SelectedRule
        /// property is set to the given oSelectedRule object.
        /// </summary>
        private void UpdateFindAndReplacePatternViewers(
			RuleListViewerItem oSelectedRule)
        {
            //**************************************************************
            // Use the given oSelectedRule to initialize a TreeTransfer
            // object, which will be used to populate the find-pattern and
            // replace-pattern viewers.
            //
            // If oSelectedRule is null, blank nodes will be used to
            // populate the find-pattern and replace-pattern viewers.
            //
            // If oSelectedRule is not null, its .FindPatternRoot tree and
            // .ReplacePatternRoot tree will be used to populate the
            // find-pattern and replace-pattern viewers.

            TreeTransfer oTreeTransfer = new TreeTransfer();
            if (oSelectedRule == null)
            {
                oTreeTransfer.ParseTreeRoot = null;
                oTreeTransfer.FindPatternRoot = new SyntaxNode();
                oTreeTransfer.ReplacePatternRoot = new SyntaxNode();
            }
            else
            {
                oTreeTransfer.ParseTreeRoot = null;
                oTreeTransfer.FindPatternRoot
                    = oSelectedRule.FindPatternRoot;
                oTreeTransfer.ReplacePatternRoot
                    = oSelectedRule.ReplacePatternRoot;
            }

            //**************************************************************
            // Temporarily set the SelectedRule property to null, because we
            // do not want the LinkedFindPatternViewer TreeChanged event and
            // the LinkedReplacePatternViewer TreeChanged event to update
            // the SelectedRule (and set the Modified property to true) when
            // we populate the find-pattern and replace-pattern trees.

            moSelectedRule = null;

            //**************************************************************
            // Populate the linked find-pattern viewer.

            if (LinkedFindPatternViewer != null)
            {
                LinkedFindPatternViewer.PopulateTree(oTreeTransfer);
            }

            //**************************************************************
            // Populate the linked replace-pattern viewer.

            if (LinkedReplacePatternViewer != null)
            {
                LinkedReplacePatternViewer.PopulateTree(oTreeTransfer);
            }

            //**************************************************************
            // Now set the SelectedRule property to the given oSelectedRule
            // object, because we want the TreeChanged events to update the
            // SelectedRule (and set the Modified property to true) when the
            // user edits the find-pattern and replace-pattern trees.

            moSelectedRule = oSelectedRule;
        }
        //******************************************************************
        /// <summary>
        /// Inserts the given rules (oTransferRules) into the ListView,
        /// starting at the indicated index (iFirstIndex). The inserted
        /// rules are selected.
        /// </summary>
        private void InsertRules(int iFirstIndex,
			TransferRuleCollection oTransferRules)
        {
            Debug.Assert(iFirstIndex >= 0);
            Debug.Assert(oTransferRules != null);
            Debug.Assert(oTransferRules.Count > 0);

            //**************************************************************
            // Clear the selection.

            ClearSelection();

            //**************************************************************
            // Insert the given rules starting at the indicated index.

            int iIndex = iFirstIndex;
            foreach (TransferRule oRule in oTransferRules)
            {
                //**********************************************************
                // Create the item to display in the ListView.

                RuleListViewerItem oRuleItem = new RuleListViewerItem();
                oRuleItem.Text = oRule.RuleName;
                oRuleItem.FindPatternRoot = oRule.FindPatternRoot;
                oRuleItem.ReplacePatternRoot = oRule.ReplacePatternRoot;

                //**********************************************************
                // Insert or append the item in the ListView.

                if (iIndex < moListView.Items.Count)
                {
                    moListView.Items.Insert(iIndex,oRuleItem);
                }
                else
                {
                    moListView.Items.Add(oRuleItem);
                }

                //**********************************************************
                // Select the item and make sure it is scrolled into view.

                oRuleItem.Selected = true;
                oRuleItem.EnsureVisible();

                //**********************************************************
                // Increment the index.

                ++iIndex;
            }

            //**************************************************************
            // Make sure the first selected item is scrolled into view and
            // has focus within the ListView.

            RuleListViewerItem oFirstSelectedItem = FirstSelectedItem();
            if (oFirstSelectedItem != null)
            {
                oFirstSelectedItem.EnsureVisible();
                oFirstSelectedItem.Focused = true;
            }
        }
        //******************************************************************
        /// <summary>
        /// Makes sure a rule is selected when the input focus leaves this
        /// control.
        /// </summary>
        private void RuleListViewer_Leave(object oSender,EventArgs oArgs)
        {
            try
            {
                //**********************************************************
                // If the list is empty (and not read-only), add a new rule
                // to the list and select it.

                if (moListView.Items.Count == 0)
                {
                    if (! ReadOnly)
                    {
                        //**************************************************
                        // Set the Modified property to true.

                        Modified = true;

                        //**************************************************
                        // Reset the TreeTransferData object.

                        ResetTreeTransferData();

                        //**************************************************
                        // Clear the undo information.

                        ClearUndoInformation();

                        //**************************************************
                        // Create a new rule item and append it to the list.

                        RuleListViewerItem oRuleItem
                            = new RuleListViewerItem();
                        oRuleItem.Text = "(new rule)";
                        oRuleItem.FindPatternRoot = new SyntaxNode();
                        oRuleItem.ReplacePatternRoot = new SyntaxNode();
                        moListView.Items.Add(oRuleItem);

                        //**************************************************
                        // Select the item.

                        oRuleItem.Selected = true;

                        //**************************************************
                        // Raise the ListChanged event.

                        OnListChanged(new EventArgs());
                    }
                }

                //**********************************************************
                // If the list is not empty, one of the items should be the
                // SelectedRule.

                if (moListView.Items.Count > 0)
                {
                    Debug.Assert(SelectedRule != null);
                    Debug.Assert(moListView.Items.Contains(SelectedRule));
                }

                //**********************************************************
                // Make sure the SelectedRule item is selected.

                if (SelectedRule != null)
                {
                    if (SelectedRule != FirstSelectedItem())
                    {
                        ClearSelection();
                        SelectedRule.Selected = true;
                    }
                }

                //**********************************************************
                // Make sure the SelectedRule item is scrolled into view and
                // has focus within the ListView.

                if (SelectedRule != null)
                {
                    SelectedRule.EnsureVisible();
                    SelectedRule.Focused = true;
                }
            }
            catch (Exception oException)
            {
                ShowException(oException);
            }
        }
        //******************************************************************
        /// <summary>
        /// Loads and displays the indicated rule file (sFileName). The
        /// Modified property is set to false after the file is loaded.
        /// </summary>
        public void LoadRuleFile(string sFileName)
        {
            //**************************************************************
            // Validate the parameters.

            if ((sFileName == null) || (sFileName == ""))
            {
                string sMessage = "Invalid argument: "
                    + "RuleListViewer.LoadRuleFile() requires "
                    + "a file name that is not null or blank.";
                throw new Exception(sMessage);
            }

            //**************************************************************
            // Clear the linked FindPatternViewer and ReplacePatternViewer.

            UpdateFindAndReplacePatternViewers(null);

            //**************************************************************
            // Reset the TreeTransferData object.

            ResetTreeTransferData();

            //**************************************************************
            // Clear the undo information.

            ClearUndoInformation();

            //**************************************************************
            // Clear the list of items in the ListView.

            moListView.Items.Clear();

            //**************************************************************
            // Create a RuleReader to read from the indicated rule file.

            StreamReader oStreamReader = new StreamReader(sFileName);
            RuleReader oRuleReader = new RuleReader(oStreamReader);

            //**************************************************************
            // For each rule loaded from the rule file, create a new rule
            // item and append it to the ListView.

            while (oRuleReader.Read())
            {
                RuleListViewerItem oRuleItem = new RuleListViewerItem();
                oRuleItem.Text
                    = oRuleReader.RuleName;
                oRuleItem.FindPatternRoot
                    = oRuleReader.FindPatternRoot;
                oRuleItem.ReplacePatternRoot
                    = oRuleReader.ReplacePatternRoot;
                moListView.Items.Add(oRuleItem);
            }

            //**************************************************************
            // Close the RuleReader.

            oRuleReader.Close();

            //**************************************************************
            // Select the first item in the ListView. Make sure it is
            // scrolled into view and has focus within the ListView.

            if (moListView.Items.Count > 0)
            {
                ListViewItem oItem = moListView.Items[0];
                oItem.Selected = true;
                oItem.EnsureVisible();
                oItem.Focused = true;
            }

            //**************************************************************
            // Set the Modified property to false.

            Modified = false;

            //**************************************************************
            // Raise the SelectionChanged event.

            OnSelectionChanged(new EventArgs());

            //**************************************************************
            // Raise the ListChanged event.

            OnListChanged(new EventArgs());
        }