/// <summary>
        /// Append text.  If we were at the bottom, scroll to the bottom.  Otherwise leave the scroll position
        /// where it is.
        /// </summary>
        /// <param name="text"></param>
        public void AppendMaybeScroll(string text)
        {
            bool bottom = m_bottom;

            this.AppendText(BottomScrollRichText.EscapeRTF(text));

            if (bottom)
            {
                this.ScrollToBottom();
            }
        }
        /// <summary>
        /// Add a line that has a colored tag string, followed by a space, followed by
        /// a chunk of text in the default color, followed by a newline.
        ///
        /// Note: Although this seems kind of random, it's needed in several places.
        /// </summary>
        /// <param name="tagColor">The color to use for the tag</param>
        /// <param name="tag">The tag string</param>
        /// <param name="text">The main text</param>
        public void AppendMaybeScroll(Color tagColor, string tag, string text)
        {
            this.SuspendLayout();

            // This should always be called on the GUI thread, right?
            // Assume so.  No locking.

            bool bottom = m_bottom;
            int  start  = 0;
            int  len    = 0;

            if (!bottom)
            {
                start = this.SelectionStart;
                len   = this.SelectionLength;
            }

            this.AppendText(tagColor, tag);
            this.AppendText(" ");
            this.AppendText(this.ForeColor, BottomScrollRichText.EscapeRTF(text));
            this.AppendText("\r\n");

            string[] lines = this.Lines;

            if (lines.Length > m_maxLines)
            {
                int  rm = 0;
                bool ro = this.ReadOnly;
                this.ReadOnly = false;

                for (int i = 0; i < (lines.Length - m_maxLines); i++)
                {
                    rm += lines[i].Length + 1;
                }

                this.Select(0, rm);
                this.SelectedText = String.Empty;
                this.ReadOnly     = ro;
            }

            if (bottom)
            {
                ScrollToBottom();
                this.SelectionStart  = this.TextLength;
                this.SelectionLength = 0;
            }
            else
            {
                this.SelectionStart  = start;
                this.SelectionLength = len;
            }

            this.ResumeLayout();
        }
        /// <summary>
        /// Append text with the given color to the end of the text area.
        /// Side effect: the existing selection is modified.  Save the selection
        /// if you want to keep it.
        /// </summary>
        /// <param name="c"></param>
        /// <param name="text"></param>
        public void AppendText(Color c, string text)
        {
            this.SelectionLength = 0;
            this.SelectionStart  = this.TextLength;

            string rtf = String.Format(CultureInfo.CurrentCulture,
                                       "{{\\rtf1\\ansi{{{{\\colortbl ;{0}}}\\cf1 {1}}}",
                                       BottomScrollRichText.RTFColor(c),
                                       text);

            this.SelectedRtf = rtf;
        }