예제 #1
0
        public static void DrawYAxes(LJD.Graphics g, Resources resources, PlotsDrawingData pdd, float yAxesAreaWidth, PlotsViewMetrics m)
        {
            float x    = yAxesAreaWidth;
            var   font = resources.AxesFont;

            foreach (var axis in pdd.YAxes)
            {
                float maxLabelWidth = 0;
                foreach (var p in axis.Points)
                {
                    var pt = new PointF(x - (p.IsMajorMark ? resources.MajorAxisMarkSize : resources.MinorAxisMarkSize), p.Position);
                    g.DrawLine(resources.AxesPen, pt, new PointF(x, p.Position));
                    if (p.Label != null)
                    {
                        g.DrawString(p.Label, font, resources.DataPointLabelBrush, pt, resources.YAxisPointLabelFormat);
                    }
                    maxLabelWidth = Math.Max(maxLabelWidth, g.MeasureString(p.Label ?? "", resources.AxesFont).Width);
                }
                x -= (resources.MajorAxisMarkSize + maxLabelWidth);
                g.PushState();
                g.TranslateTransform(x, m.Size.Height / 2);
                g.RotateTransform(-90);
                g.DrawString(axis.Label, resources.AxesFont, resources.DataPointLabelBrush, new PointF(0, 0), resources.YAxisLabelFormat);
                g.PopState();
                x -= (g.MeasureString(axis.Label, resources.AxesFont).Height + resources.YAxesPadding);
            }
        }
예제 #2
0
 public static void DrawEvents(
     LJD.Graphics g,
     ViewMetrics viewMetrics,
     IViewEvents eventsHandler)
 {
     foreach (var evt in Metrics.GetEventMetrics(g, eventsHandler, viewMetrics))
     {
         if (evt.VertLinePoints != null)
         {
             g.PushState();
             g.EnableAntialiasing(true);
             g.DrawLines(viewMetrics.UserEventPen, evt.VertLinePoints);
             g.PopState();
         }
         else
         {
             g.DrawLine(viewMetrics.UserEventPen, evt.VertLineA, evt.VertLineB);
         }
         if (evt.Icon != null)
         {
             g.DrawImage(evt.Icon, evt.IconRect);
         }
         g.FillRectangle(viewMetrics.EventRectBrush, evt.CaptionRect);
         g.DrawRectangle(viewMetrics.EventRectPen, evt.CaptionRect);
         g.DrawString(evt.Event.Caption, viewMetrics.EventCaptionFont,
                      viewMetrics.EventCaptionBrush, evt.CaptionDrawingOrigin,
                      viewMetrics.EventCaptionStringFormat);
     }
 }
예제 #3
0
        public void DrawCaptionsView(
            LJD.Graphics g,
            ViewMetrics viewMetrics,
            IViewModel eventsHandler,
            Action <string, Rectangle, int, int, bool> drawCaptionWithHighlightedRegion
            )
        {
            PushGraphicsStateForDrawingActivites(g, viewMetrics, HitTestResult.AreaCode.CaptionsPanel);

            int  availableHeight = viewMetrics.ActivitiesViewHeight;
            bool darkMode        = eventsHandler.ColorTheme == Presenters.ColorThemeMode.Dark;

            foreach (var a in eventsHandler.OnDrawActivities())
            {
                int y = viewMetrics.GetActivityY(a.Index);
                if (y < 0)
                {
                    continue;
                }
                if (y > availableHeight)
                {
                    break;
                }

                var h = viewMetrics.LineHeight - 1;
                var sequenceDiagramTextRect = new Rectangle(0, y, viewMetrics.SequenceDiagramAreaWidth, h);
                var foldingAreaRect         = new Rectangle(
                    sequenceDiagramTextRect.Right + 1 /* box padding */, y + (h - viewMetrics.FoldingAreaWidth) / 2, viewMetrics.FoldingAreaWidth, viewMetrics.FoldingAreaWidth);
                var textRect = new Rectangle(
                    foldingAreaRect.Right + 3 /* text padding */, y, viewMetrics.ActivitesCaptionsViewWidth - foldingAreaRect.Right, h);
                var lineRect = new Rectangle(0, y, viewMetrics.ActivitesCaptionsViewWidth, h);
                if (a.IsSelected)
                {
                    g.FillRectangle(res.SelectedActivityBackgroundBrush, lineRect);
                }
                else if (!darkMode && a.Color.HasValue)
                {
                    using (var bgBrush = MakeBrush(a.Color.Value))
                        g.FillRectangle(bgBrush, lineRect);
                }
                if (!string.IsNullOrEmpty(a.SequenceDiagramText))
                {
                    g.DrawString(a.SequenceDiagramText, res.ActivitesCaptionsFont, res.ActivitesCaptionsBrush, sequenceDiagramTextRect);
                }
                if (a.IsFolded.HasValue)
                {
                    g.DrawRectangle(res.FoldingSignPen, foldingAreaRect);
                    int pad = 2;
                    g.DrawLine(res.FoldingSignPen, foldingAreaRect.Left + pad, foldingAreaRect.MidY(), foldingAreaRect.Right - pad, foldingAreaRect.MidY());
                    if (a.IsFolded == true)
                    {
                        g.DrawLine(res.FoldingSignPen, foldingAreaRect.MidX(), foldingAreaRect.Top + pad, foldingAreaRect.MidX(), foldingAreaRect.Bottom - pad);
                    }
                }
                drawCaptionWithHighlightedRegion(a.Caption, textRect, a.CaptionSelectionBegin, a.CaptionSelectionLength, a.IsError);
            }

            g.PopState();
        }
예제 #4
0
        public static void DrawMeasurer(
            LJD.Graphics g,
            ViewMetrics viewMetrics,
            IViewEvents eventsHandler)
        {
            var drawInfo = eventsHandler.OnDrawMeasurer();

            if (!drawInfo.MeasurerVisible)
            {
                return;
            }
            double viewWidth  = viewMetrics.ActivitiesViewWidth;
            int    viewHeight = viewMetrics.ActivitiesViewHeight;
            var    x1         = Metrics.SafeGetScreenX(drawInfo.X1, viewWidth);
            var    x2         = Metrics.SafeGetScreenX(drawInfo.X2, viewWidth);

            g.DrawLine(viewMetrics.MeasurerPen, x1, viewMetrics.MeasurerTop, x1, viewHeight);
            g.DrawLine(viewMetrics.MeasurerPen, x2, viewMetrics.MeasurerTop, x2, viewHeight);
            g.DrawLine(viewMetrics.MeasurerPen, x1, viewMetrics.MeasurerTop, x2, viewMetrics.MeasurerTop);
            var        textSz = g.MeasureString(drawInfo.Text, viewMetrics.MeasurerTextFont);
            RectangleF textRect;
            int        textHPadding = 2;

            if (textSz.Width < (x2 - x1 - 5))
            {
                textRect = new RectangleF(
                    (x2 + x1 - textSz.Width - textHPadding) / 2,
                    viewMetrics.MeasurerTop - textSz.Height / 2,
                    textSz.Width + textHPadding,
                    textSz.Height
                    );
            }
            else
            {
                textRect = new RectangleF(
                    x2 + 5,
                    viewMetrics.MeasurerTop - textSz.Height / 2,
                    textSz.Width + textHPadding,
                    textSz.Height
                    );
                if (textRect.Right > viewMetrics.ActivitiesViewWidth)
                {
                    textRect.X = x1 - 5 - textRect.Width;
                }
            }
            g.FillRectangle(viewMetrics.MeasurerTextBoxBrush, textRect);
            g.DrawRectangle(viewMetrics.MeasurerTextBoxPen, new RectangleF(textRect.X, textRect.Y, textRect.Width, textRect.Height));
            g.DrawString(drawInfo.Text,
                         viewMetrics.MeasurerTextFont, viewMetrics.MeasurerTextBrush,
                         new PointF((textRect.X + textRect.Right) / 2, (textRect.Y + textRect.Bottom) / 2),
                         viewMetrics.MeasurerTextFormat);
        }
예제 #5
0
        public static void DrawXAxis(LJD.Graphics g, Resources resources, PlotsDrawingData pdd, float height)
        {
            var majorMarkerHeight = (height - g.MeasureString("123", resources.AxesFont).Height) * 3f / 4f;
            var minorMarkerHeight = majorMarkerHeight / 2f;

            foreach (var x in pdd.XAxis.Points)
            {
                g.DrawLine(
                    resources.AxesPen,
                    new PointF(x.Position, 0),
                    new PointF(x.Position, x.IsMajorMark ? majorMarkerHeight : minorMarkerHeight)
                    );
                g.DrawString(x.Label, resources.AxesFont, resources.DataPointLabelBrush, new PointF(x.Position, height), resources.XAxisPointLabelFormat);
            }
        }
예제 #6
0
        public static void DrawCaptionsView(
            LJD.Graphics g,
            ViewMetrics viewMetrics,
            IViewEvents eventsHandler,
            int sequenceDiagramAreaWidth,
            Action <string, Rectangle, int, int, bool> drawCaptionWithHighlightedRegion
            )
        {
            PushGraphicsStateForDrawingActivites(g, viewMetrics, HitTestResult.AreaCode.CaptionsPanel);

            int availableHeight = viewMetrics.ActivitiesViewHeight;

            foreach (var a in eventsHandler.OnDrawActivities())
            {
                int y = Metrics.GetActivityY(viewMetrics, a.Index);
                if (y < 0)
                {
                    continue;
                }
                if (y > availableHeight)
                {
                    break;
                }

                var h = viewMetrics.LineHeight - 1;
                var sequenceDiagramTextRect = new Rectangle(0, y, sequenceDiagramAreaWidth, h);
                var textRect = new Rectangle(
                    sequenceDiagramTextRect.Right + 3 /* text padding */, y, viewMetrics.ActivitesCaptionsViewWidth - sequenceDiagramTextRect.Right, h);
                var lineRect = new Rectangle(0, y, viewMetrics.ActivitesCaptionsViewWidth, h);
                if (a.IsSelected)
                {
                    g.FillRectangle(viewMetrics.SelectedLineBrush, lineRect);
                }
                else if (a.Color.HasValue)
                {
                    using (var bgBrush = MakeBrush(a.Color.Value))
                        g.FillRectangle(bgBrush, lineRect);
                }
                if (!string.IsNullOrEmpty(a.SequenceDiagramText))
                {
                    g.DrawString(a.SequenceDiagramText, viewMetrics.ActivitesCaptionsFont, viewMetrics.ActivitesCaptionsBrush, sequenceDiagramTextRect);
                }
                drawCaptionWithHighlightedRegion(a.Caption, textRect, a.CaptionSelectionBegin, a.CaptionSelectionLength, a.IsError);
            }

            g.PopState();
        }
예제 #7
0
        public static void DrawRulerLines(
            LJD.Graphics g,
            ViewMetrics viewMetrics,
            IViewEvents eventHandler,
            DrawScope scope,
            Size viewSize
            )
        {
            double availableWidth = viewSize.Width;

            foreach (var m in Metrics.GetRulerMarks(viewMetrics, eventHandler, scope))
            {
                int x = (int)(m.X * availableWidth);
                g.DrawString(m.Label, viewMetrics.RulerMarkFont, viewMetrics.RulerMarkBrush, new Point(x, 2));
                g.DrawLine(viewMetrics.RulerLinePen, x, 0, x, viewSize.Height);
            }
        }
예제 #8
0
        public void DrawLeftPanelView(LJD.Graphics g, Point origin, Size viewSize)
        {
            var w = viewSize.Width;

            foreach (var message in eventsHandler.ArrowsDrawInfo)
            {
                int y = origin.Y + message.Y;

                if (message.SelectionState != ArrowSelectionState.NotSelected)
                {
                    g.FillRectangle(
                        resources.SelectedLineBrush,
                        new Rectangle(0, y - message.Height + 2, w, message.Height - 2)
                        );
                }

                var focusedMessageImageSz = resources.FocusedMessageImage.GetSize(width: 4.5f);
                g.DrawString(message.Delta, resources.Font, resources.NormalArrowTextBrush,
                             new RectangleF(0, y - message.Height, w - focusedMessageImageSz.Width - 4 * resources.DpiScale, message.Height),
                             resources.TimeDeltaFormat);

                if (message.CurrentTimePosition != null)
                {
                    var img = resources.FocusedMessageImage;
                    var sz  = focusedMessageImageSz.Scale(resources.DpiScale);
                    g.DrawImage(img, new RectangleF(
                                    w - sz.Width - 3,
                                    y - message.Height / 2 + Math.Sign(message.CurrentTimePosition.Value) * message.Height / 2 - sz.Height / 2,
                                    sz.Width, sz.Height));
                }

                if (message.IsBookmarked)
                {
                    var img = resources.BookmarkImage;
                    var sz  = img.GetSize(width: 12).Scale(resources.DpiScale);
                    g.DrawImage(img, new RectangleF(
                                    2, y - (message.Height + sz.Height) / 2, sz.Width, sz.Height
                                    ));
                }
            }
        }
예제 #9
0
 public void DrawRoleCaptions(LJD.Graphics g)
 {
     foreach (var role in GetRoleCaptionsMetrics(g))
     {
         g.DrawRectangle(resources.CaptionRectPen, role.Box);
         g.DrawString(
             role.DrawInfo.DisplayName,
             role.IsLink ? resources.UnderlinedFont : resources.Font,
             role.IsLink ? resources.LinkCaptionBrush : resources.NoLinkCaptionBrush,
             role.Box,
             resources.RoleCaptionFormat);
         if (role.IsFocused && resources.FocusedMsgSlaveVert != null)
         {
             var img = resources.FocusedMsgSlaveVert;
             var sz  = img.GetSize(width: 10f).Scale(resources.DpiScale);
             g.DrawImage(img, new RectangleF(
                             role.Box.X + (role.Box.Width - sz.Width) / 2, role.Box.Bottom - sz.Height - 1,
                             sz.Width, sz.Height
                             ));
         }
     }
 }
 public LogViewerControlAdapter()
 {
     NSBundle.LoadNib("LogViewerControl", this);
     InnerView.Init(this);
     InitScrollView();
     InitDrawingContext();
     InitCursorTimer();
     InnerView.Menu = new NSMenu()
     {
         Delegate = new ContextMenuDelegate()
         {
             owner = this
         }
     };
     DragDropIconView.OnPaint = (dirtyRect) =>
     {
         using (var g = new LJD.Graphics())
         {
             float penW = 2;
             var   p    = new Pen(Color.Gray, penW, new [] { 5f, 2.5f });
             var   r    = new RectangleF(new PointF(), DragDropIconView.Frame.Size.ToSizeF());
             r.Inflate(-penW, -penW);
             g.DrawRoundRectangle(p, r, 25);
             r.Inflate(-5, -5);
             using (var f = new Font(
                        NSFont.SystemFontOfSize(NSFont.SystemFontSize).FontName,
                        (float)(NSFont.SystemFontSize * 1.2f), FontStyle.Regular))
             {
                 g.DrawString(
                     "Drop logs here\n(files, URLs, archives)",
                     f, Brushes.Black, r,
                     new StringFormat(StringAlignment.Center, StringAlignment.Center)
                     );
             }
         }
     };
 }
예제 #11
0
        public static void DrawPlotsArea(
            LJD.Graphics g,
            Resources resources,
            PlotsDrawingData pdd,
            PlotsViewMetrics m
            )
        {
            g.DrawRectangle(resources.AxesPen, new RectangleF(new PointF(), m.Size));

            foreach (var x in pdd.XAxis.Points)
            {
                g.DrawLine(resources.GridPen, new PointF(x.Position, 0), new PointF(x.Position, m.Size.Height));
            }

            g.PushState();
            g.EnableAntialiasing(true);
            foreach (var s in pdd.TimeSeries)
            {
                var  pen       = resources.GetTimeSeriesPen(s.Color);
                var  prevPt    = new PointF();
                bool isFirstPt = true;
                foreach (var pt in s.Points)
                {
                    if (!isFirstPt && s.DrawLine)
                    {
                        g.DrawLine(pen, prevPt, pt);
                    }
                    prevPt = pt;
                    DrawPlotMarker(g, resources, pen, pt, s.Marker);
                    isFirstPt = false;
                }
            }
            foreach (var e in pdd.Events)
            {
                if ((e.Type & EventDrawingData.EventType.Group) != 0)
                {
                    var captionSz = g.MeasureString(e.Text, resources.GroupCaptionFont);
                    var round     = 2f;
                    captionSz.Width  += round * 2;
                    captionSz.Height += round * 2;
                    var captionRect = new RectangleF(
                        e.X + e.Width / 2 - captionSz.Width / 2, 1, captionSz.Width, captionSz.Height);
                    var vertLineRect = new RectangleF(e.X, captionRect.Top, e.Width, m.Size.Height);

                    if ((e.Type & EventDrawingData.EventType.ParsedEvent) != 0)
                    {
                        g.FillRectangle(resources.ParsedEventsGroupBrush, vertLineRect);
                    }
                    if ((e.Type & EventDrawingData.EventType.Bookmark) != 0)
                    {
                        g.FillRectangle(resources.BookmarksGroupGrush, vertLineRect);
                    }

                    g.FillRoundRectangle(LJD.Brushes.Red, captionRect, round);
                    g.DrawRoundRectangle(LJD.Pens.White, captionRect, round);
                    g.DrawString(e.Text, resources.GroupCaptionFont, LJD.Brushes.White,
                                 new PointF(captionRect.X + round, captionRect.Y + round));
                }
                else
                {
                    LJD.Pen   pen;
                    LJD.Brush brush;
                    LJD.Image icon;
                    if ((e.Type & EventDrawingData.EventType.Bookmark) != 0)
                    {
                        pen   = resources.BookmarkPen;
                        brush = resources.BookmarkBrush;
                        icon  = resources.BookmarkIcon;
                    }
                    else
                    {
                        pen   = resources.ParsedEventPen;
                        brush = resources.ParsedEventBrush;
                        icon  = resources.ParsedEventIcon;
                    }
                    g.DrawLine(pen, new PointF(e.X, 0), new PointF(e.X, m.Size.Height));
                    if (icon != null)
                    {
                        float iconWidth = 10;                         // todo: hardcoded
                        g.DrawImage(icon, new RectangleF(
                                        e.X - iconWidth / 2, 1, iconWidth, iconWidth * icon.Height / icon.Width));
                    }
                    if (e.Text != null)
                    {
                        g.PushState();
                        g.TranslateTransform(e.X, 6);
                        g.RotateTransform(-90);
                        g.DrawString(e.Text, resources.EventTextFont, brush,
                                     new PointF(), resources.EventTextFormat);
                        g.PopState();
                    }
                }
            }
            g.PopState();

            if (pdd.FocusedMessageX != null)
            {
                g.DrawLine(LJD.Pens.Blue, new PointF(pdd.FocusedMessageX.Value, 0), new PointF(pdd.FocusedMessageX.Value, m.Size.Height));
            }

            pdd.UpdateThrottlingWarning();
        }
예제 #12
0
        public void DrawArrowsView(LJD.Graphics g, Size viewSize, Action <Rectangle> drawFocusRect)
        {
            foreach (var message in eventsHandler.ArrowsDrawInfo)
            {
                if (!message.IsFullyVisible)
                {
                    continue;
                }
                if (message.SelectionState != ArrowSelectionState.NotSelected)
                {
                    int y = message.Y;
                    int h = message.Height;
                    var r = new Rectangle(
                        0, y - h + 2,
                        viewSize.Width, h - 2
                        );
                    g.FillRectangle(resources.SelectedLineBrush, r);
                    if (message.SelectionState == ArrowSelectionState.FocusedSelectedArrow)
                    {
                        drawFocusRect(r);
                    }
                }
            }

            foreach (var role in eventsHandler.RolesDrawInfo)
            {
                var x = role.X;
                g.DrawLine(resources.RolePen, x, 0, x, viewSize.Height);
                foreach (var eo in role.ExecutionOccurrences)
                {
                    LJD.Brush fillBrush;
                    if (eo.DrawMode == ExecutionOccurrenceDrawMode.Normal)
                    {
                        fillBrush = eo.IsHighlighted ? resources.NormalHighlightedExecutionOccurrenceBrush : resources.NormalExecutionOccurrenceBrush;
                    }
                    else if (eo.DrawMode == ExecutionOccurrenceDrawMode.Activity)
                    {
                        fillBrush = eo.IsHighlighted ? resources.ActivityHighlightedExecutionOccurrenceBrush : resources.ActivityExecutionOccurrenceBrush;
                    }
                    else
                    {
                        continue;
                    }
                    g.FillRectangle(fillBrush, eo.Bounds);
                    g.DrawRectangle(eo.IsHighlighted ? resources.HighlightedExecutionOccurrencePen : resources.ExecutionOccurrencePen, eo.Bounds);
                }
            }

            Action <string, LJD.Brush, LJD.Brush, RectangleF> drawTextHelper = (str, back, fore, textRect) =>
            {
                if (back != null)
                {
                    var fillSz   = g.MeasureString(str, resources.Font, resources.ArrowTextFormat, textRect.Size);
                    var fillRect = new RectangleF(textRect.X, textRect.Y + 4, fillSz.Width, textRect.Height - 5);
                    g.FillRectangle(back, fillRect);
                }
                g.DrawString(str, resources.Font, fore, textRect, resources.ArrowTextFormat);
            };

            Action <PointF, bool> drawArrow = (pt, leftToRight) => {
                var pts = new List <PointF>(4);
                pts.Add(pt);
                foreach (var p in resources.ArrowEndShapePoints)
                {
                    pts.Add(new PointF(pt.X + (leftToRight ? 1f : -1f) * p.X, pt.Y + p.Y));
                }
                g.FillPolygon(resources.NormalArrowTextBrush, pts.ToArray());
            };

            foreach (var message in eventsHandler.ArrowsDrawInfo)
            {
                int y         = message.Y;
                int x1        = message.FromX;
                int x2        = message.ToX;
                int h         = message.Height;
                int w         = message.Width;
                int padding   = message.TextPadding;
                var backBrush = message.SelectionState != ArrowSelectionState.NotSelected ? resources.SelectedLineBrush : resources.ControlBackgroundBrush;
                var textBrush = message.Color == ArrowColor.Error ? resources.ErrorArrowTextBrush : resources.NormalArrowTextBrush;

                if (message.Mode == ArrowDrawMode.Arrow ||
                    message.Mode == ArrowDrawMode.DottedArrow)
                {
                    var pen = !message.IsHighlighted ?
                              (message.Mode == ArrowDrawMode.Arrow ? resources.RequestPen : resources.ResponsePen) :
                              (message.Mode == ArrowDrawMode.Arrow ? resources.HighlightedRequestPen : resources.HighlightedResponsePen);
                    if (x1 != x2)
                    {
                        if (message.IsFullyVisible)
                        {
                            drawTextHelper(
                                message.DisplayName,
                                backBrush,
                                textBrush,
                                new RectangleF(
                                    Math.Min(x1, x2) + padding, y - h,
                                    Math.Abs(x1 - x2) - padding * 2, h
                                    )
                                );
                        }

                        var nonHorizontal = message.NonHorizontalDrawingData;
                        if (nonHorizontal != null)
                        {
                            g.DrawLines(
                                pen,
                                new[]
                            {
                                new Point(x1, y),
                                new Point(nonHorizontal.VerticalLineX, y),
                                new Point(nonHorizontal.VerticalLineX, nonHorizontal.Y),
                                new Point(nonHorizontal.ToX, nonHorizontal.Y)
                            }
                                );
                            drawArrow(new PointF(nonHorizontal.ToX, nonHorizontal.Y), x2 > x1);
                        }
                        else
                        {
                            if (message.IsFullyVisible)
                            {
                                g.DrawLine(
                                    pen,
                                    x1, y,
                                    x2, y
                                    );
                                drawArrow(new PointF(x2, y), x2 > x1);
                            }
                        }
                    }
                    else
                    {
                        var midY  = y - h / 2;
                        var loopW = 10;
                        var loopH = 10;

                        drawTextHelper(
                            message.DisplayName,
                            backBrush,
                            textBrush,
                            new RectangleF(
                                x1 + loopW + padding, y - h,
                                message.Width - (loopW + padding * 2), h
                                )
                            );

                        g.DrawLines(
                            pen,
                            new[]
                        {
                            new Point(x1, midY - loopH / 2),
                            new Point(x1 + loopW, midY - loopH / 2),
                            new Point(x1 + loopW, midY + loopH / 2),
                            new Point(x1, midY + loopH / 2),
                        }
                            );
                        drawArrow(new PointF(x1, midY + loopH / 2), false);
                    }
                }
                else if (message.Mode == ArrowDrawMode.Bookmark)
                {
                    if (!message.IsFullyVisible)
                    {
                        continue;
                    }
                    float radius   = h / 4f;
                    var   textRect = new RectangleF(
                        message.MinX - w / 2 + padding, y - h,
                        (message.MaxX - message.MinX + w) - padding * 2, h
                        );
                    var sz = g.MeasureString("ABC", resources.Font, resources.ArrowTextFormat, textRect.Size).ToSize();
                    var r  = new RectangleF(textRect.X, y - sz.Height - 1, textRect.Width, sz.Height);
                    r.Inflate(radius, 0);

                    g.PushState();
                    g.EnableAntialiasing(true);
                    g.FillRoundRectangle(resources.BookmarkArrowBackgroundBrush, r, radius);
                    g.DrawRoundRectangle(resources.BookmarkArrowPen, r, radius);
                    g.PopState();

                    SizeF q = new SizeF(3, 3).Scale(resources.DpiScale);
                    if (x1 - q.Width > r.Left && x1 + q.Width < r.Right)
                    {
                        PointF[] pts;

                        pts = new []
                        {
                            new PointF(x1 - q.Width, r.Top + r.Height / 2),
                            new PointF(x1 - q.Width, r.Top),
                            new PointF(x1, r.Top - q.Height),
                            new PointF(x1 + q.Width, r.Top),
                            new PointF(x1 + q.Width, r.Top + r.Height / 2),
                        };
                        g.FillPolygon(resources.BookmarkArrowBackgroundBrush, pts);
                        g.DrawLines(resources.BookmarkArrowPen, pts.Skip(1).Take(3).ToArray());

                        pts = new []
                        {
                            new PointF(x1 + q.Width, r.Bottom - r.Height / 2),
                            new PointF(x1 + q.Width, r.Bottom),
                            new PointF(x1, r.Bottom + q.Height),
                            new PointF(x1 - q.Width, r.Bottom),
                            new PointF(x1 - q.Width, r.Bottom - r.Height / 2),
                        };
                        g.FillPolygon(resources.BookmarkArrowBackgroundBrush, pts);
                        g.DrawLines(resources.BookmarkArrowPen, pts.Skip(1).Take(3).ToArray());
                    }

                    drawTextHelper(
                        message.DisplayName,
                        null,
                        textBrush,
                        textRect
                        );
                }
                else if (message.Mode == ArrowDrawMode.UserAction ||
                         message.Mode == ArrowDrawMode.StateChange ||
                         message.Mode == ArrowDrawMode.APICall ||
                         message.Mode == ArrowDrawMode.ActivityLabel)
                {
                    if (!message.IsFullyVisible)
                    {
                        continue;
                    }
                    var   icon   = message.Mode == ArrowDrawMode.UserAction ? resources.UserActionImage : null;
                    float radius = h / 4f;
                    var   r      = new RectangleF(
                        x1 - (w - radius), y - h,
                        (w - radius) * 2, h
                        );
                    var sz      = g.MeasureString(message.DisplayName, resources.Font, resources.UserActionTextFormat, r.Size);
                    var szh     = sz.Height;
                    var boxRect = new RectangleF(
                        x1 - sz.Width / 2, y - szh - 1,
                        sz.Width, szh
                        );
                    boxRect.Inflate(radius, 0);
                    g.PushState();
                    g.EnableAntialiasing(true);
                    LJD.Brush backgroundBrush = null;
                    LJD.Pen   strokePen       = resources.UserActionFramePen;
                    if (message.Mode == ArrowDrawMode.UserAction || message.Mode == ArrowDrawMode.APICall)
                    {
                        backgroundBrush = resources.UserActionBrush;
                    }
                    else if (message.Mode == ArrowDrawMode.StateChange)
                    {
                        backgroundBrush = resources.StateChangeBrush;
                    }
                    else if (message.Mode == ArrowDrawMode.ActivityLabel)
                    {
                        backgroundBrush = resources.ActivityExecutionOccurrenceBrush;
                        if (message.IsHighlighted)
                        {
                            strokePen = resources.HighlightedExecutionOccurrencePen;
                        }
                    }
                    g.FillRoundRectangle(
                        backgroundBrush,
                        boxRect,
                        radius
                        );
                    g.DrawRoundRectangle(
                        strokePen,
                        boxRect,
                        radius
                        );
                    g.PopState();
                    g.DrawString(message.DisplayName, resources.Font,
                                 textBrush, boxRect, resources.UserActionTextFormat);
                    if (icon != null)
                    {
                        var iconsz = icon.GetSize(width: 10f).Scale(resources.DpiScale);
                        g.DrawImage(
                            icon,
                            new RectangleF(
                                (int)(boxRect.Left - iconsz.Width * 1.3f),
                                (int)(boxRect.Y + (boxRect.Height - iconsz.Height) / 2),
                                iconsz.Width,
                                iconsz.Height
                                )
                            );
                    }
                }
            }
        }