Exemple #1
0
        /// <summary>Override; see base.</summary>
        protected override void OnTextChanged(EventArgs e)
        {
            // If a link has focus, trigger the LinkLostFocus event.
            // OnPaint will trigger the LinkGotFocus event as appropriate
            _keyboardFocusOnLink = null;

            _cachedPreferredSizes.Clear();
            _cachedRendering = null;
            base.OnTextChanged(e);
            _mnemonic = '\0';
            TabStop   = false;
            var origText = base.Text;

            try
            {
                _parsed    = EggsML.Parse(origText);
                ParseError = null;

                // We know there are no mnemonics or links in the exception message, so only do this if there was no parse error
                extractMnemonicEtc(_parsed);
            }
            catch (EggsMLParseException epe)
            {
                ParseError = epe;
                var msg = "";
                int ind = 0;
                if (epe.FirstIndex != null)
                {
                    ind  = epe.FirstIndex.Value;
                    msg += EggsML.Escape(origText.Substring(0, ind));
                    msg += "<Red>={0}=".Fmt(EggsML.Escape(origText.Substring(ind, 1)));
                    ind++;
                }
                msg += EggsML.Escape(origText.Substring(ind, epe.Index - ind));
                ind  = epe.Index;
                if (epe.Length > 0)
                {
                    msg += "<Red>={0}=".Fmt(EggsML.Escape(origText.Substring(ind, epe.Length)));
                    ind += epe.Length;
                }
                msg    += "<Red>= ← (" + EggsML.Escape(epe.Message) + ")=";
                msg    += EggsML.Escape(origText.Substring(ind));
                _parsed = EggsML.Parse(msg);
            }
            autosize();
            Invalidate();
        }
Exemple #2
0
        /// <summary>Override; see base.</summary>
        protected override bool ProcessDialogKey(Keys keyData)
        {
            // This handles Tab and Shift-Tab.
            // The Enter and Space keys are handled in OnKeyDown() instead.

            if (keyData != Keys.Tab && keyData != (Keys.Tab | Keys.Shift))
                return base.ProcessDialogKey(keyData);

            var shift = keyData == (Keys.Tab | Keys.Shift);
            var links = _specialLocations.OfType<linkLocationInfo>();

            _keyboardFocusOnLink = shift
                ? links.TakeWhile(l => l != _keyboardFocusOnLink).LastOrDefault()
                : links.SkipWhile(l => l != _keyboardFocusOnLink).Skip(1).FirstOrDefault();

            return _keyboardFocusOnLink == null
                ? base.ProcessDialogKey(keyData)
                : true;
        }
Exemple #3
0
 /// <summary>Override; see base.</summary>
 protected override void OnLostFocus(EventArgs e)
 {
     _lastHadFocus = false;
     if (_formJustDeactivated)
         _formJustDeactivated = false;
     else
         _keyboardFocusOnLink = null;
     base.OnLostFocus(e);
 }
Exemple #4
0
        /// <summary>Override; see base.</summary>
        protected override void OnGotFocus(EventArgs e)
        {
            _lastHadFocus = true;
            if (_keyboardFocusOnLink == null)
            {
                var links = _specialLocations.OfType<linkLocationInfo>();
                _keyboardFocusOnLink = Control.ModifierKeys.HasFlag(Keys.Shift) ? links.LastOrDefault() : links.FirstOrDefault();
            }

            // Only call the base if this is not the late invocation from the paint event
            if (!(e is PaintEventArgs))
                base.OnGotFocus(e);
        }
Exemple #5
0
 /// <summary>Override; see base.</summary>
 protected override void OnMouseDown(MouseEventArgs e)
 {
     if (_mouseOnLink != null)
     {
         _mouseIsDownOnLink = true;
         _keyboardFocusOnLink = _mouseOnLink;
         Focus();
         Invalidate();
     }
     base.OnMouseDown(e);
 }
Exemple #6
0
 /// <summary>Override; see base.</summary>
 protected override void OnMouseLeave(EventArgs e)
 {
     if (_cachedRendering != null && TabStop)
     {
         Cursor = Cursors.Default;
         _mouseOnLink = null;
         Invalidate();
     }
     _tooltip.Hide(this);
     _tooltipText = null;
     base.OnMouseLeave(e);
 }
Exemple #7
0
        private void checkForLinksAndTooltips(Point p)
        {
            var anyLink = false;
            var anyTooltip = false;

            if (Enabled)
            {
                foreach (var location in _specialLocations)
                    foreach (var rectangle in location.Rectangles)
                        if (rectangle.Contains(p))
                        {
                            if (location is linkLocationInfo)
                            {
                                if (_mouseOnLink != location)
                                {
                                    Cursor = _cursorHand;
                                    _mouseOnLink = (linkLocationInfo) location;
                                    Invalidate();
                                }
                                anyLink = true;
                            }
                            else
                            {
                                // tooltip
                                var tooltipText = ((tooltipLocationInfo) location).Tooltip;
                                if (_tooltipText != tooltipText)
                                {
                                    _tooltip.Show(tooltipText, this, rectangle.Left, rectangle.Bottom + 5);
                                    _tooltipText = tooltipText;
                                }
                                anyTooltip = true;
                            }
                        }
            }

            if (_mouseOnLink != null && !anyLink)
            {
                Cursor = Cursors.Default;
                _mouseOnLink = null;
                Invalidate();
            }

            if (!anyTooltip)
            {
                _tooltip.Hide(this);
                _tooltipText = null;
            }
        }
Exemple #8
0
        /// <summary>Override; see base.</summary>
        protected override bool ProcessMnemonic(char charCode)
        {
            if (!Enabled || !Visible)
                return false;

            // Main mnemonic, which takes focus to the next control in the form
            if (_mnemonic == char.ToUpperInvariant(charCode) && Parent != null)
            {
                OnMnemonic();
                return true;
            }

            // Mnemonics for links within the label, which trigger the link
            var applicableLinks = _specialLocations.OfType<linkLocationInfo>().Where(link => link.Mnemonic == charCode).ToArray();
            if (applicableLinks.Length == 0)
                return false;
            else if (applicableLinks.Length == 1)
            {
                // One applicable link: activate it
                _keyboardFocusOnLink = applicableLinks[0];
                Focus();
                if (LinkActivated != null)
                    LinkActivated(this, new LinkEventArgs(applicableLinks[0].LinkID, applicableLinks[0].Rectangles));
            }
            else
            {
                // More than one applicable link: cycle between between them without activating them (must press Enter or Space to activate them)
                _keyboardFocusOnLink = _keyboardFocusOnLink.NullOr(kf => applicableLinks.SkipWhile(loc => loc != kf).Skip(1).FirstOrDefault()) ?? applicableLinks.FirstOrDefault();
                Focus();
            }
            return true;
        }
Exemple #9
0
        private Size doPaintOrMeasure(Graphics g, EggsNode node, Font initialFont, Color initialForeColor, int constrainingWidth,
            List<renderingInfo> renderings = null, List<locationInfo> locations = null)
        {
            var glyphOverhang = TextRenderer.MeasureText(g, "Wg", initialFont, _dummySize) - TextRenderer.MeasureText(g, "Wg", initialFont, _dummySize, TextFormatFlags.NoPadding);
            int x = glyphOverhang.Width / 2, y = glyphOverhang.Height / 2;
            int wrapWidth = WordWrap ? Math.Max(1, constrainingWidth - glyphOverhang.Width) : int.MaxValue;
            int hangingIndent = _hangingIndent * (_hangingIndentUnit == IndentUnit.Spaces ? measure(initialFont, " ", g).Width : 1);
            bool atBeginningOfLine = false;

            // STEP 1: Run the word-wrapping as if TextAlign were TopLeft

            int actualWidth = EggsML.WordWrap(node, new renderState(initialFont, initialForeColor), wrapWidth,
                (state, text) => measure(state.Font, text, g).Width,
                (state, text, width) =>
                {
                    if (state.Mnemonic && !string.IsNullOrWhiteSpace(text))
                        state.ActiveLocations.OfType<linkLocationInfo>().FirstOrDefault().NullOr(link => { link.Mnemonic = char.ToLowerInvariant(text.Trim()[0]); return link; });

                    if (renderings != null && !string.IsNullOrEmpty(text))
                    {
                        renderingInfo info;
                        if (!atBeginningOfLine && renderings.Count > 0 && (info = renderings[renderings.Count - 1]).State == state)
                        {
                            info.Text += text;
                            var rect = info.Rectangle;
                            rect.Width += width;
                            info.Rectangle = rect;
                        }
                        else
                        {
                            info = new renderingInfo(text, new Rectangle(x, y, width, measure(state.Font, " ", g).Height), state);
                            renderings.Add(info);
                        }
                        foreach (var location in state.ActiveLocations)
                        {
                            if (location.Rectangles.Count == 0 || location.Rectangles[location.Rectangles.Count - 1].Y != info.Rectangle.Y)
                                location.Rectangles.Add(info.Rectangle);
                            else
                            {
                                var rect = location.Rectangles[location.Rectangles.Count - 1];
                                rect.Width += width;
                                location.Rectangles[location.Rectangles.Count - 1] = rect;
                            }
                        }
                    }
                    atBeginningOfLine = false;
                    x += width;
                },
                (state, newParagraph, indent) =>
                {
                    atBeginningOfLine = true;
                    var sh = measure(state.Font, " ", g).Height;
                    y += sh;
                    if (newParagraph && _paragraphSpacing > 0)
                        y += (int) (_paragraphSpacing * sh);
                    var newIndent = state.BlockIndent + indent;
                    if (!newParagraph)
                        newIndent += hangingIndent;
                    x = newIndent + glyphOverhang.Width / 2;
                    return newIndent;
                },
                (state, tag, parameter) =>
                {
                    var font = state.Font;
                    switch (tag)
                    {
                        // ITALICS
                        case '/': return Tuple.Create(state.ChangeFont(new Font(font, font.Style | FontStyle.Italic)), 0);

                        // BOLD
                        case '*': return Tuple.Create(state.ChangeFont(new Font(font, font.Style | FontStyle.Bold)), 0);

                        // UNDERLINE
                        case '_': return Tuple.Create(state.ChangeFont(new Font(font, font.Style | FontStyle.Underline)), 0);

                        // MNEMONICS
                        case '&': return Tuple.Create(state.SetMnemonic(), 0);

                        // BULLET POINT
                        case '[':
                            var bulletSize = measure(font, _bullet, g);
                            var advance = bulletSize.Width;
                            if (renderings != null)
                                renderings.Add(new renderingInfo(_bullet, new Rectangle(x, y, advance, bulletSize.Height), new renderState(font, state.Color)));
                            x += advance;
                            return Tuple.Create(state.ChangeBlockIndent(state.BlockIndent + advance), advance);

                        // LINK (e.g. <link target>{link text}, link target may be omitted)
                        case '{':
                            if (locations == null)
                                break;
                            var linkLocation = new linkLocationInfo { LinkID = parameter };
                            locations.Add(linkLocation);
                            return Tuple.Create(state.ChangeColor(Enabled ? LinkColor : SystemColors.GrayText).AddActiveLocation(linkLocation), 0);

                        // TOOLTIP (e.g. <tooltip text>#main text#)
                        case '#':
                            if (string.IsNullOrWhiteSpace(parameter) || locations == null)
                                break;
                            var tooltipLocation = new tooltipLocationInfo { Tooltip = parameter };
                            locations.Add(tooltipLocation);
                            return Tuple.Create(state.AddActiveLocation(tooltipLocation), 0);

                        // COLOUR (e.g. <colour>=coloured text=, revert to default colour if no <colour> specified)
                        case '=':
                            var color = parameter == null ? initialForeColor : (Color) (_colorConverter ?? (_colorConverter = new ColorConverter())).ConvertFromString(parameter);
                            return Tuple.Create(state.ChangeColor(color), 0);
                    }
                    return Tuple.Create(state, 0);
                });

            var totalSize = new Size(actualWidth + glyphOverhang.Width, y + measure(initialFont, " ", g).Height + glyphOverhang.Height);

            // STEP 2: Fix everything according to TextAlign.
            if (renderings != null)
            {
                // 2a: vertical alignment
                int offsetY = 0;
                switch (_textAlign)
                {
                    case ContentAlignment.TopCenter:
                    case ContentAlignment.TopLeft:
                    case ContentAlignment.TopRight:
                        // Already top-aligned: nothing to do
                        break;

                    case ContentAlignment.MiddleCenter:
                    case ContentAlignment.MiddleLeft:
                    case ContentAlignment.MiddleRight:
                        offsetY = ClientSize.Height / 2 - totalSize.Height / 2;
                        goto default;

                    case ContentAlignment.BottomCenter:
                    case ContentAlignment.BottomLeft:
                    case ContentAlignment.BottomRight:
                        offsetY = ClientSize.Height - totalSize.Height;
                        goto default;

                    default:
                        foreach (var inf in renderings)
                        {
                            var rect = inf.Rectangle;
                            rect.Y += offsetY;
                            inf.Rectangle = rect;
                        }
                        break;
                }

                // 2b: horizontal alignment
                foreach (var group in renderings.GroupConsecutiveBy(inf => inf.Rectangle.Y))
                {
                    var width = group.Max(elem => elem.Rectangle.Right);
                    int offsetX = 0;
                    switch (_textAlign)
                    {
                        case ContentAlignment.TopLeft:
                        case ContentAlignment.MiddleLeft:
                        case ContentAlignment.BottomLeft:
                            // Already left-aligned: nothing to do
                            break;

                        case ContentAlignment.TopCenter:
                        case ContentAlignment.MiddleCenter:
                        case ContentAlignment.BottomCenter:
                            offsetX = ClientSize.Width / 2 - width / 2;
                            goto default;

                        case ContentAlignment.TopRight:
                        case ContentAlignment.MiddleRight:
                        case ContentAlignment.BottomRight:
                            offsetX = ClientSize.Width - width;
                            goto default;

                        default:
                            foreach (var inf in group)
                            {
                                var rect = inf.Rectangle;
                                rect.X += offsetX;
                                inf.Rectangle = rect;
                            }
                            break;
                    }
                }
            }

            return totalSize;
        }
Exemple #10
0
        /// <summary>Override; see base.</summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            Color initialColor = Enabled ? ForeColor : SystemColors.GrayText;

            if (_cachedRendering == null || _cachedRenderingWidth != ClientSize.Width || _cachedRenderingColor != initialColor)
            {
                _cachedRendering = new List<renderingInfo>();
                _cachedRenderingWidth = ClientSize.Width;
                _cachedRenderingColor = initialColor;
                _specialLocations.Clear();
                doPaintOrMeasure(e.Graphics, _parsed, Font, initialColor, _cachedRenderingWidth, _cachedRendering, _specialLocations);

                // If this control has focus and it has a link in it, focus the first link. (This triggers the LinkGotFocus event.)
                if (!_lastHadFocus)
                    _keyboardFocusOnLink = null;
                else if (_keyboardFocusOnLink == null)
                    _keyboardFocusOnLink = _specialLocations.OfType<linkLocationInfo>().FirstOrDefault();

                checkForLinksAndTooltips(PointToClient(Control.MousePosition));
            }

            foreach (var item in _cachedRendering)
            {
                if (item.Rectangle.Bottom < e.ClipRectangle.Top || item.Rectangle.Right < e.ClipRectangle.Left || item.Rectangle.Left > e.ClipRectangle.Right)
                    continue;
                if (item.Rectangle.Top > e.ClipRectangle.Bottom)
                    break;
                var font = item.State.Font;
                if ((item.State.Mnemonic && ShowKeyboardCues) || (_mouseOnLink != null && item.State.ActiveLocations.Contains(_mouseOnLink)))
                    font = new Font(font, font.Style | FontStyle.Underline);
                TextRenderer.DrawText(e.Graphics, item.Text,
                    font,
                    item.Rectangle.Location,
                    (_mouseIsDownOnLink && item.State.ActiveLocations.Contains(_mouseOnLink)) ||
                    (_spaceIsDownOnLink && item.State.ActiveLocations.Contains(_keyboardFocusOnLink))
                        ? LinkActiveColor : item.State.Color,
                    TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
            }

            if (_keyboardFocusOnLink != null)
            {
                if (!_specialLocations.Contains(_keyboardFocusOnLink))
                    _keyboardFocusOnLinkPrivate = null;   // set the private one so that no event is triggered
                else
                    foreach (var rectangle in _keyboardFocusOnLink.Rectangles)
                        ControlPaint.DrawFocusRectangle(e.Graphics, rectangle);
            }
        }
Exemple #11
0
        /// <summary>Override; see base.</summary>
        protected override void OnTextChanged(EventArgs e)
        {
            // If a link has focus, trigger the LinkLostFocus event.
            // OnPaint will trigger the LinkGotFocus event as appropriate
            _keyboardFocusOnLink = null;

            _cachedPreferredSizes.Clear();
            _cachedRendering = null;
            base.OnTextChanged(e);
            _mnemonic = '\0';
            TabStop = false;
            var origText = base.Text;
            try
            {
                _parsed = EggsML.Parse(origText);
                ParseError = null;

                // We know there are no mnemonics or links in the exception message, so only do this if there was no parse error
                extractMnemonicEtc(_parsed);
            }
            catch (EggsMLParseException epe)
            {
                ParseError = epe;
                var msg = "";
                int ind = 0;
                if (epe.FirstIndex != null)
                {
                    ind = epe.FirstIndex.Value;
                    msg += EggsML.Escape(origText.Substring(0, ind));
                    msg += "<Red>={0}=".Fmt(EggsML.Escape(origText.Substring(ind, 1)));
                    ind++;
                }
                msg += EggsML.Escape(origText.Substring(ind, epe.Index - ind));
                ind = epe.Index;
                if (epe.Length > 0)
                {
                    msg += "<Red>={0}=".Fmt(EggsML.Escape(origText.Substring(ind, epe.Length)));
                    ind += epe.Length;
                }
                msg += "<Red>= ← (" + EggsML.Escape(epe.Message) + ")=";
                msg += EggsML.Escape(origText.Substring(ind));
                _parsed = EggsML.Parse(msg);
            }
            autosize();
            Invalidate();
        }
Exemple #12
0
        /// <summary>Override; see base.</summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            Color initialColor = Enabled ? ForeColor : SystemColors.GrayText;

            if (_cachedRendering == null || _cachedRenderingWidth != ClientSize.Width || _cachedRenderingColor != initialColor)
            {
                _cachedRendering      = new List <renderingInfo>();
                _cachedRenderingWidth = ClientSize.Width;
                _cachedRenderingColor = initialColor;
                _specialLocations.Clear();
                doPaintOrMeasure(e.Graphics, _parsed, Font, initialColor, _cachedRenderingWidth, _cachedRendering, _specialLocations);

                // If this control has focus and it has a link in it, focus the first link. (This triggers the LinkGotFocus event.)
                if (!_lastHadFocus)
                {
                    _keyboardFocusOnLink = null;
                }
                else if (_keyboardFocusOnLink == null)
                {
                    _keyboardFocusOnLink = _specialLocations.OfType <linkLocationInfo>().FirstOrDefault();
                }

                checkForLinksAndTooltips(PointToClient(Control.MousePosition));
            }

            foreach (var item in _cachedRendering)
            {
                if (item.Rectangle.Bottom < e.ClipRectangle.Top || item.Rectangle.Right < e.ClipRectangle.Left || item.Rectangle.Left > e.ClipRectangle.Right)
                {
                    continue;
                }
                if (item.Rectangle.Top > e.ClipRectangle.Bottom)
                {
                    break;
                }
                var font = item.State.Font;
                if ((item.State.Mnemonic && ShowKeyboardCues) || (_mouseOnLink != null && item.State.ActiveLocations.Contains(_mouseOnLink)))
                {
                    font = new Font(font, font.Style | FontStyle.Underline);
                }
                TextRenderer.DrawText(e.Graphics, item.Text,
                                      font,
                                      item.Rectangle.Location,
                                      (_mouseIsDownOnLink && item.State.ActiveLocations.Contains(_mouseOnLink)) ||
                                      (_spaceIsDownOnLink && item.State.ActiveLocations.Contains(_keyboardFocusOnLink))
                        ? LinkActiveColor : item.State.Color,
                                      TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
            }

            if (_keyboardFocusOnLink != null)
            {
                if (!_specialLocations.Contains(_keyboardFocusOnLink))
                {
                    _keyboardFocusOnLinkPrivate = null;   // set the private one so that no event is triggered
                }
                else
                {
                    foreach (var rectangle in _keyboardFocusOnLink.Rectangles)
                    {
                        ControlPaint.DrawFocusRectangle(e.Graphics, rectangle);
                    }
                }
            }
        }