Drawing Indicator Window ,

I noticed it was taking longer than expected to add text, numbers, and drawings directly onto the chart. Because of that, I moved forward with developing the new Drawing Panel to make the process faster, cleaner, and easier to use. If anyone has any additional ideas or features to add to this, let me know. I know the calculator still needs more upgrades. I truly hope this helps anyone making instructional videos, tutorials, or live presentations. This tool is built to make chart marking, text, numbers, and drawings faster and easier while presenting. This is just the basic version for now. Hope this helps a bit, I managed to get the drawing speed faster, I Will post the code below,

Key Features

Freehand Marker Drawing

  • Draw directly on any NinjaTrader chart

  • Adjustable marker thickness

  • Multiple color selections

  • Smooth low-latency drawing engine

  • Select, move, resize, and delete drawings

Text Annotation System

  • Add custom notes anywhere on the chart

  • Adjustable text size

  • Custom text colors

  • Optional text background

  • Background automatically sizes to the text

  • Drag and reposition text objects

  • Individual object deletion with on chart delete controls

Formula Renderer

  • Convert formulas into professional chart overlays

  • Supports fractions and mathematical notation

  • Gradient background styling

  • Adjustable formula sizing

  • Render formulas directly onto the chart

  • Move and resize rendered formulas

Integrated Calculator

  • Built-in calculator window

  • Open and close independently from the main panel

  • Send calculations directly to the formula renderer

  • Designed for fast chart-side calculations

Algebra Tools

  • Slope Formula

  • Point-Slope Formula

  • Quadratic Formula Templates

  • Exponents

  • Square Roots

  • Equation Building Tools

Geometry Tools

  • Distance Formula

  • Midpoint Formula

  • Pythagorean Theorem

  • Triangle Area

  • Rectangle Area

  • Circle Area

  • Quick-access geometry templates

Image Overlay System

  • Import images directly from files

  • Display images on the chart

  • Move and resize imported graphics

  • Useful for educational examples, screenshots, and references

Object Management

  • Select individual objects

  • Resize drawings

  • Resize text

  • Resize formulas

  • Resize images

  • Delete selected objects

  • Clear all chart annotations instantly

Professional User Interface

  • Dark sci-fi themed design

  • Neon accent colors

  • Scrollable control panel

  • Movable windows

  • Organized tool sections

  • Optimized for multi-monitor setups

Designed For

  • Futures Traders

  • Day Traders

  • Scalpers

  • Trading Educators

  • Content Creators

  • Live Streamers

  • Market Analysts

  • Students Learning Math or Trading

5 Likes
    private class TextNote
    {
        public string Text;
        public WpfPoint Position;
        public WpfBrush Color;
        public int Size;
        public bool ShowBackground;
    }

    private class ImageNote
    {
        public string FilePath;
        public WpfPoint Position;
        public double Width = 280;
        public double Height = 140;
    }

    private List<StrokeItem> strokes;
    private List<TextNote> textNotes;
    private List<ImageNote> imageNotes;

    private StrokeItem currentStroke;

    private bool isDrawing;
    private bool drawMode;
    private bool moveMode;
    private bool selectMode;

    private int selectedStrokeIndex = -1;
    private int selectedTextIndex = -1;
    private int selectedImageIndex = -1;

    private WpfPoint lastMousePoint;

    private Window panelWindow;
    private TextBox pasteBox;
    private TextBox formulaBox;
    private TextBlock selectedInfo;
    private Slider selectedWidthSlider;
    private Slider selectedTextSizeSlider;
    private Slider selectedImageSizeSlider;
    private CheckBox textBackgroundCheckBox;
    private Window calculatorWindow;
    private TextBox calculatorDisplay;

    private FactoryDWrite dwFactory;

    protected override void OnStateChange()
    {
        if (State == State.SetDefaults)
        {
            Name = "WTMarkerDrawingPanel";
            Calculate = Calculate.OnEachTick;
            IsOverlay = true;
            DisplayInDataBox = false;
            DrawOnPricePanel = true;
            PaintPriceMarkers = false;
            IsSuspendedWhileInactive = false;

            MarkerWidth = 5;
            MarkerColor = WpfBrushes.DeepSkyBlue;
            TextColor = WpfBrushes.White;
            TextSize = 18;
            ShowTextBackground = true;
        }
        else if (State == State.DataLoaded)
        {
            strokes = new List<StrokeItem>();
            textNotes = new List<TextNote>();
            imageNotes = new List<ImageNote>();
            dwFactory = new FactoryDWrite();
        }
        else if (State == State.Historical)
        {
            if (ChartControl != null)
            {
                ChartControl.Dispatcher.InvokeAsync(() =>
                {
                    ChartControl.PreviewMouseDown += ChartMouseDown;
                    ChartControl.PreviewMouseMove += ChartMouseMove;
                    ChartControl.PreviewMouseUp += ChartMouseUp;
                    OpenPanel();
                });
            }
        }
        else if (State == State.Terminated)
        {
            if (ChartControl != null)
            {
                ChartControl.Dispatcher.InvokeAsync(() =>
                {
                    ChartControl.PreviewMouseDown -= ChartMouseDown;
                    ChartControl.PreviewMouseMove -= ChartMouseMove;
                    ChartControl.PreviewMouseUp -= ChartMouseUp;
                });
            }

            if (dwFactory != null)
            {
                dwFactory.Dispose();
                dwFactory = null;
            }

            CloseCalculator();
            ClosePanel();
        }
    }

    protected override void OnBarUpdate() { }

    private D2D1Brush MakeDxBrush(WpfBrush brush)
    {
        WpfSolidColorBrush solid = brush as WpfSolidColorBrush;
        if (solid == null)
            solid = WpfBrushes.White as WpfSolidColorBrush;

        Color c = solid.Color;
        return new D2D1SolidColorBrush(
            RenderTarget,
            new SharpDX.Color4(c.R / 255f, c.G / 255f, c.B / 255f, c.A / 255f));
    }

    private D2D1Brush MakeDxColor(byte r, byte g, byte b, byte a)
    {
        return new D2D1SolidColorBrush(
            RenderTarget,
            new SharpDX.Color4(r / 255f, g / 255f, b / 255f, a / 255f));
    }

    private WpfBrush SciFiPanelBrush()
    {
        LinearGradientBrush brush = new LinearGradientBrush();
        brush.StartPoint = new WpfPoint(0, 0);
        brush.EndPoint = new WpfPoint(1, 1);
        brush.GradientStops.Add(new GradientStop(Color.FromRgb(0, 0, 0), 0.0));
        brush.GradientStops.Add(new GradientStop(Color.FromRgb(3, 8, 16), 0.55));
        brush.GradientStops.Add(new GradientStop(Color.FromRgb(0, 0, 0), 1.0));
        return brush;
    }

    private WpfBrush SciFiControlBrush(byte r, byte g, byte b)
    {
        LinearGradientBrush brush = new LinearGradientBrush();
        brush.StartPoint = new WpfPoint(0, 0);
        brush.EndPoint = new WpfPoint(1, 1);
        brush.GradientStops.Add(new GradientStop(Color.FromRgb(2, 4, 8), 0.0));
        brush.GradientStops.Add(new GradientStop(Color.FromRgb(r, g, b), 0.55));
        brush.GradientStops.Add(new GradientStop(Color.FromRgb(0, 0, 0), 1.0));
        return brush;
    }

    private void StyleButton(Button btn, WpfBrush borderColor, WpfBrush textColor)
    {
        btn.Background = SciFiControlBrush(5, 12, 22);
        btn.Foreground = textColor;
        btn.BorderBrush = borderColor;
        btn.BorderThickness = new Thickness(1.2);
        btn.FontWeight = FontWeights.Bold;
        btn.FontSize = 14;
    }

    private void StyleTextBox(TextBox tb, WpfBrush borderColor)
    {
        tb.Background = new WpfSolidColorBrush(Color.FromRgb(5, 7, 12));
        tb.Foreground = WpfBrushes.White;
        tb.BorderBrush = borderColor;
        tb.BorderThickness = new Thickness(1.1);
        tb.CaretBrush = WpfBrushes.White;
    }

    private TextBlock MakeSectionLabel(string text, WpfBrush color)
    {
        TextBlock tb = new TextBlock();
        tb.Text = text;
        tb.Foreground = color;
        tb.FontWeight = FontWeights.Bold;
        tb.FontSize = 15;
        tb.Margin = new Thickness(0, 10, 0, 4);
        return tb;
    }

    private void ChartMouseDown(object sender, MouseButtonEventArgs e)
    {
        if (ChartPanel == null)
            return;

        WpfPoint p = e.GetPosition(ChartPanel);
        lastMousePoint = p;

        if (selectMode || moveMode)
        {
            if (TryDeleteAtPoint(p))
            {
                e.Handled = true;
                ForceRefresh();
                return;
            }

            SelectItemAtPoint(p);
            UpdateSelectedControls();
            e.Handled = true;
            ForceRefresh();
            return;
        }

        if (!drawMode || e.LeftButton != MouseButtonState.Pressed)
            return;

        isDrawing = true;

        currentStroke = new StrokeItem();
        currentStroke.Color = MarkerColor;
        currentStroke.Width = MarkerWidth;
        currentStroke.Points.Add(p);

        strokes.Add(currentStroke);

        selectedStrokeIndex = strokes.Count - 1;
        selectedTextIndex = -1;
        selectedImageIndex = -1;

        UpdateSelectedControls();

        e.Handled = true;
        ForceRefresh();
    }

    private void ChartMouseMove(object sender, MouseEventArgs e)
    {
        if (ChartPanel == null)
            return;

        WpfPoint p = e.GetPosition(ChartPanel);

        if (moveMode && e.LeftButton == MouseButtonState.Pressed)
        {
            double dx = p.X - lastMousePoint.X;
            double dy = p.Y - lastMousePoint.Y;

            if (selectedStrokeIndex >= 0 && selectedStrokeIndex < strokes.Count)
                MoveStroke(selectedStrokeIndex, dx, dy);

            if (selectedTextIndex >= 0 && selectedTextIndex < textNotes.Count)
                textNotes[selectedTextIndex].Position = new WpfPoint(
                    textNotes[selectedTextIndex].Position.X + dx,
                    textNotes[selectedTextIndex].Position.Y + dy);

            if (selectedImageIndex >= 0 && selectedImageIndex < imageNotes.Count)
                imageNotes[selectedImageIndex].Position = new WpfPoint(
                    imageNotes[selectedImageIndex].Position.X + dx,
                    imageNotes[selectedImageIndex].Position.Y + dy);

            lastMousePoint = p;
            e.Handled = true;

            if (ChartControl != null)
                ChartControl.InvalidateVisual();

            return;
        }

        if (!drawMode || !isDrawing || currentStroke == null)
            return;

        if (e.LeftButton != MouseButtonState.Pressed)
            return;

        WpfPoint last = currentStroke.Points[currentStroke.Points.Count - 1];
        double drawDx = p.X - last.X;
        double drawDy = p.Y - last.Y;

        if ((drawDx * drawDx + drawDy * drawDy) < 64)
            return;

        currentStroke.Points.Add(p);

        e.Handled = true;

        if (ChartControl != null)
            ChartControl.InvalidateVisual();
    }

    private void ChartMouseUp(object sender, MouseButtonEventArgs e)
    {
        isDrawing = false;
        currentStroke = null;

        if (drawMode || moveMode || selectMode)
            e.Handled = true;

        ForceRefresh();
    }

    protected override void OnRender(ChartControl chartControl, ChartScale chartScale)
    {
        base.OnRender(chartControl, chartScale);

        if (RenderTarget == null || ChartPanel == null)
            return;

        for (int s = 0; s < strokes.Count; s++)
        {
            StrokeItem stroke = strokes[s];
            if (stroke == null || stroke.Points == null || stroke.Points.Count < 2)
                continue;

            using (D2D1Brush brush = MakeDxBrush(stroke.Color))
            {
                for (int i = 1; i < stroke.Points.Count; i++)
                {
                    Vector2 p1 = new Vector2(
                        (float)(ChartPanel.X + stroke.Points[i - 1].X),
                        (float)(ChartPanel.Y + stroke.Points[i - 1].Y));

                    Vector2 p2 = new Vector2(
                        (float)(ChartPanel.X + stroke.Points[i].X),
                        (float)(ChartPanel.Y + stroke.Points[i].Y));

                    RenderTarget.DrawLine(p1, p2, brush, stroke.Width);
                }
            }

            if (s == selectedStrokeIndex)
                DrawStrokeDeleteX(stroke);
        }

        for (int i = 0; i < textNotes.Count; i++)
        {
            TextNote note = textNotes[i];
            if (note == null || string.IsNullOrWhiteSpace(note.Text))
                continue;

            using (TextFormat format = new TextFormat(dwFactory, "Arial", note.Size))
            using (TextLayout layout = new TextLayout(dwFactory, note.Text, format, 2000, 500))
            {
                float textWidth = Math.Max(20f, layout.Metrics.WidthIncludingTrailingWhitespace);
                float textHeight = Math.Max((float)(note.Size + 10), layout.Metrics.Height);

                SharpDX.RectangleF rect = new SharpDX.RectangleF(
                    (float)(ChartPanel.X + note.Position.X),
                    (float)(ChartPanel.Y + note.Position.Y),
                    textWidth + 8,
                    textHeight + 8);

                if (note.ShowBackground)
                {
                    SharpDX.RectangleF bgRect = new SharpDX.RectangleF(
                        rect.Left - 8,
                        rect.Top - 6,
                        textWidth + 24,
                        textHeight + 14);

                    using (D2D1Brush bgBrush = MakeDxColor(0, 8, 24, 220))
                        RenderTarget.FillRectangle(bgRect, bgBrush);
                }

                using (D2D1Brush textBrush = MakeDxBrush(note.Color))
                    RenderTarget.DrawText(note.Text, format, rect, textBrush);
            }

            if (i == selectedTextIndex)
                DrawTextDeleteX(note);
        }

        if (isDrawing)
            return;

        for (int i = 0; i < imageNotes.Count; i++)
        {
            ImageNote img = imageNotes[i];
            if (img == null || string.IsNullOrEmpty(img.FilePath))
                continue;

            try
            {
                using (SharpDX.WIC.ImagingFactory factory = new SharpDX.WIC.ImagingFactory())
                using (SharpDX.WIC.BitmapDecoder decoder = new SharpDX.WIC.BitmapDecoder(factory, img.FilePath, SharpDX.WIC.DecodeOptions.CacheOnDemand))
                using (SharpDX.WIC.BitmapFrameDecode frame = decoder.GetFrame(0))
                using (SharpDX.WIC.FormatConverter converter = new SharpDX.WIC.FormatConverter(factory))
                {
                    converter.Initialize(frame, SharpDX.WIC.PixelFormat.Format32bppPBGRA);

                    using (SharpDX.Direct2D1.Bitmap dxBmp = SharpDX.Direct2D1.Bitmap.FromWicBitmap(RenderTarget, converter))
                    {
                        SharpDX.RectangleF rect = new SharpDX.RectangleF(
                            (float)(ChartPanel.X + img.Position.X),
                            (float)(ChartPanel.Y + img.Position.Y),
                            (float)img.Width,
                            (float)img.Height);

                        RenderTarget.DrawBitmap(dxBmp, rect, 1.0f, SharpDX.Direct2D1.BitmapInterpolationMode.Linear);
                    }
                }
            }
            catch (Exception ex)
            {
                DrawErrorText("Image failed: " + ex.Message, img.Position);
            }

            if (i == selectedImageIndex)
                DrawImageDeleteX(img);
        }
    }

    private void DrawErrorText(string text, WpfPoint position)
    {
        using (D2D1Brush textBrush = MakeDxBrush(WpfBrushes.Red))
        using (TextFormat format = new TextFormat(dwFactory, "Arial", 14))
        {
            SharpDX.RectangleF rect = new SharpDX.RectangleF(
                (float)(ChartPanel.X + position.X),
                (float)(ChartPanel.Y + position.Y),
                600,
                120);

            RenderTarget.DrawText(text, format, rect, textBrush);
        }
    }

    private void OpenPanel()
    {
        if (panelWindow != null)
            return;

        panelWindow = new Window();
        panelWindow.Title = "WT Marker Tool";
        panelWindow.Width = 380;
        panelWindow.Height = 770;
        panelWindow.Topmost = true;
        panelWindow.ResizeMode = ResizeMode.CanResize;
        panelWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
        panelWindow.Background = SciFiPanelBrush();

        ScrollViewer scroll = new ScrollViewer();
        scroll.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        scroll.Background = SciFiPanelBrush();

        StackPanel main = new StackPanel();
        main.Margin = new Thickness(12);
        main.Background = SciFiPanelBrush();

        Button drawBtn = new Button();
        drawBtn.Content = "DRAW MODE: OFF";
        drawBtn.Height = 30;
        drawBtn.Margin = new Thickness(0, 0, 0, 6);
        StyleButton(drawBtn, WpfBrushes.DodgerBlue, WpfBrushes.DodgerBlue);
        drawBtn.Click += (s, e) =>
        {
            drawMode = !drawMode;
            if (drawMode)
            {
                moveMode = false;
                selectMode = false;
            }
            drawBtn.Content = drawMode ? "DRAW MODE: ON" : "DRAW MODE: OFF";
        };

        Button moveBtn = new Button();
        moveBtn.Content = "MOVE / DRAG MODE: OFF";
        moveBtn.Height = 30;
        moveBtn.Margin = new Thickness(0, 0, 0, 6);
        StyleButton(moveBtn, WpfBrushes.MediumPurple, WpfBrushes.MediumPurple);
        moveBtn.Click += (s, e) =>
        {
            moveMode = !moveMode;
            if (moveMode)
            {
                drawMode = false;
                selectMode = false;
            }
            moveBtn.Content = moveMode ? "MOVE / DRAG MODE: ON" : "MOVE / DRAG MODE: OFF";
            drawBtn.Content = drawMode ? "DRAW MODE: ON" : "DRAW MODE: OFF";
        };

        Button selectBtn = new Button();
        selectBtn.Content = "SELECT MODE: OFF";
        selectBtn.Height = 30;
        selectBtn.Margin = new Thickness(0, 0, 0, 10);
        StyleButton(selectBtn, WpfBrushes.Cyan, WpfBrushes.Cyan);
        selectBtn.Click += (s, e) =>
        {
            selectMode = !selectMode;
            if (selectMode)
            {
                drawMode = false;
                moveMode = false;
            }
            selectBtn.Content = selectMode ? "SELECT MODE: ON" : "SELECT MODE: OFF";
            drawBtn.Content = drawMode ? "DRAW MODE: ON" : "DRAW MODE: OFF";
            moveBtn.Content = moveMode ? "MOVE / DRAG MODE: ON" : "MOVE / DRAG MODE: OFF";
        };

        TextBlock widthText = MakeSectionLabel("New Drawing Width: " + MarkerWidth, WpfBrushes.White);

        Slider widthSlider = new Slider();
        widthSlider.Minimum = 1;
        widthSlider.Maximum = 50;
        widthSlider.Value = MarkerWidth;
        widthSlider.IsSnapToTickEnabled = true;
        widthSlider.TickFrequency = 1;
        widthSlider.Margin = new Thickness(0, 0, 0, 8);
        widthSlider.ValueChanged += (s, e) =>
        {
            MarkerWidth = (int)widthSlider.Value;
            widthText.Text = "New Drawing Width: " + MarkerWidth;
        };

        TextBlock colorText = MakeSectionLabel("New Drawing / Text Color", WpfBrushes.White);

        WrapPanel colors = new WrapPanel();
        colors.Children.Add(MakeColorButton("Blue", WpfBrushes.DodgerBlue));
        colors.Children.Add(MakeColorButton("Green", WpfBrushes.LimeGreen));
        colors.Children.Add(MakeColorButton("Red", WpfBrushes.Red));
        colors.Children.Add(MakeColorButton("Yellow", WpfBrushes.Gold));
        colors.Children.Add(MakeColorButton("White", WpfBrushes.White));
        colors.Children.Add(MakeColorButton("Black", WpfBrushes.Black));

        textBackgroundCheckBox = new CheckBox();
        textBackgroundCheckBox.Content = "Show Background On New Text";
        textBackgroundCheckBox.Foreground = WpfBrushes.White;
        textBackgroundCheckBox.IsChecked = ShowTextBackground;
        textBackgroundCheckBox.Margin = new Thickness(0, 8, 0, 8);
        textBackgroundCheckBox.Checked += (s, e) =>
        {
            ShowTextBackground = true;
            if (selectedTextIndex >= 0 && selectedTextIndex < textNotes.Count)
                textNotes[selectedTextIndex].ShowBackground = true;
            ForceRefresh();
        };
        textBackgroundCheckBox.Unchecked += (s, e) =>
        {
            ShowTextBackground = false;
            if (selectedTextIndex >= 0 && selectedTextIndex < textNotes.Count)
                textNotes[selectedTextIndex].ShowBackground = false;
            ForceRefresh();
        };

        TextBlock formulaLabel = MakeSectionLabel("FORMULA RENDERER", WpfBrushes.MediumPurple);

        formulaBox = new TextBox();
        formulaBox.Height = 45;
        formulaBox.Text = "m=(11-3)/(5-1)=8/4=2";
        formulaBox.TextWrapping = TextWrapping.Wrap;
        StyleTextBox(formulaBox, WpfBrushes.MediumPurple);

        Button renderFormulaBtn = new Button();
        renderFormulaBtn.Content = "RENDER FORMULA TO CHART";
        renderFormulaBtn.Height = 30;
        renderFormulaBtn.Margin = new Thickness(0, 6, 0, 6);
        StyleButton(renderFormulaBtn, WpfBrushes.MediumPurple, WpfBrushes.MediumPurple);
        renderFormulaBtn.Click += (s, e) => RenderFormulaToChart();

        Button calculatorBtn = new Button();
        calculatorBtn.Content = "OPEN CALCULATOR";
        calculatorBtn.Height = 30;
        calculatorBtn.Margin = new Thickness(0, 0, 0, 8);
        StyleButton(calculatorBtn, WpfBrushes.DodgerBlue, WpfBrushes.DodgerBlue);
        calculatorBtn.Click += (s, e) => OpenCalculator();

        TextBlock pasteLabel = MakeSectionLabel("TEXT TOOLS", WpfBrushes.Cyan);

        TextBlock pasteSmallLabel = new TextBlock();
        pasteSmallLabel.Text = "Paste plain text:";
        pasteSmallLabel.Foreground = WpfBrushes.White;
        pasteSmallLabel.Margin = new Thickness(0, 2, 0, 4);

        pasteBox = new TextBox();
        pasteBox.Height = 65;
        pasteBox.AcceptsReturn = true;
        pasteBox.TextWrapping = TextWrapping.Wrap;
        pasteBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        StyleTextBox(pasteBox, WpfBrushes.Cyan);

        Button addTextBtn = new Button();
        addTextBtn.Content = "ADD PASTED TEXT TO CHART";
        addTextBtn.Height = 30;
        addTextBtn.Margin = new Thickness(0, 6, 0, 6);
        StyleButton(addTextBtn, WpfBrushes.Cyan, WpfBrushes.Cyan);
        addTextBtn.Click += (s, e) => AddPastedText();

        Button loadImageBtn = new Button();
        loadImageBtn.Content = "LOAD IMAGE FROM FILE";
        loadImageBtn.Height = 30;
        loadImageBtn.Margin = new Thickness(0, 0, 0, 10);
        StyleButton(loadImageBtn, WpfBrushes.DarkCyan, WpfBrushes.Cyan);
        loadImageBtn.Click += (s, e) => LoadImageFromFile();

        selectedInfo = new TextBlock();
        selectedInfo.Text = "Selected: None";
        selectedInfo.Foreground = WpfBrushes.White;
        selectedInfo.Margin = new Thickness(0, 6, 0, 6);

        selectedWidthSlider = new Slider();
        selectedWidthSlider.Minimum = 1;
        selectedWidthSlider.Maximum = 50;
        selectedWidthSlider.Value = MarkerWidth;
        selectedWidthSlider.IsSnapToTickEnabled = true;
        selectedWidthSlider.TickFrequency = 1;
        selectedWidthSlider.Margin = new Thickness(0, 0, 0, 8);
        selectedWidthSlider.ValueChanged += (s, e) =>
        {
            if (selectedStrokeIndex >= 0 && selectedStrokeIndex < strokes.Count)
            {
                strokes[selectedStrokeIndex].Width = (int)selectedWidthSlider.Value;
                ForceRefresh();
            }
        };

        selectedTextSizeSlider = new Slider();
        selectedTextSizeSlider.Minimum = 8;
        selectedTextSizeSlider.Maximum = 60;
        selectedTextSizeSlider.Value = TextSize;
        selectedTextSizeSlider.IsSnapToTickEnabled = true;
        selectedTextSizeSlider.TickFrequency = 1;
        selectedTextSizeSlider.Margin = new Thickness(0, 0, 0, 8);
        selectedTextSizeSlider.ValueChanged += (s, e) =>
        {
            if (selectedTextIndex >= 0 && selectedTextIndex < textNotes.Count)
            {
                textNotes[selectedTextIndex].Size = (int)selectedTextSizeSlider.Value;
                ForceRefresh();
            }
        };

        selectedImageSizeSlider = new Slider();
        selectedImageSizeSlider.Minimum = 50;
        selectedImageSizeSlider.Maximum = 900;
        selectedImageSizeSlider.Value = 280;
        selectedImageSizeSlider.IsSnapToTickEnabled = true;
        selectedImageSizeSlider.TickFrequency = 10;
        selectedImageSizeSlider.Margin = new Thickness(0, 0, 0, 8);
        selectedImageSizeSlider.ValueChanged += (s, e) =>
        {
            if (selectedImageIndex >= 0 && selectedImageIndex < imageNotes.Count)
            {
                ImageNote img = imageNotes[selectedImageIndex];
                double ratio = img.Height / img.Width;
                img.Width = selectedImageSizeSlider.Value;
                img.Height = selectedImageSizeSlider.Value * ratio;
                ForceRefresh();
            }
        };

        Button deleteSelectedBtn = new Button();
        deleteSelectedBtn.Content = "DELETE SELECTED";
        deleteSelectedBtn.Height = 30;
        deleteSelectedBtn.Margin = new Thickness(0, 8, 0, 6);
        StyleButton(deleteSelectedBtn, WpfBrushes.Red, WpfBrushes.Red);
        deleteSelectedBtn.Click += (s, e) => DeleteSelected();

        Button clearBtn = new Button();
        clearBtn.Content = "CLEAR ALL";
        clearBtn.Height = 30;
        StyleButton(clearBtn, WpfBrushes.Gray, WpfBrushes.White);
        clearBtn.Click += (s, e) =>
        {
            strokes.Clear();
            textNotes.Clear();
            imageNotes.Clear();
            selectedStrokeIndex = -1;
            selectedTextIndex = -1;
            selectedImageIndex = -1;
            UpdateSelectedControls();
            ForceRefresh();
        };

        main.Children.Add(drawBtn);
        main.Children.Add(moveBtn);
        main.Children.Add(selectBtn);
        main.Children.Add(widthText);
        main.Children.Add(widthSlider);
        main.Children.Add(colorText);
        main.Children.Add(colors);
        main.Children.Add(textBackgroundCheckBox);
        main.Children.Add(formulaLabel);
        main.Children.Add(formulaBox);
        main.Children.Add(renderFormulaBtn);
        main.Children.Add(calculatorBtn);
        main.Children.Add(pasteLabel);
        main.Children.Add(pasteSmallLabel);
        main.Children.Add(pasteBox);
        main.Children.Add(addTextBtn);
        main.Children.Add(MakeSectionLabel("IMAGE TOOLS", WpfBrushes.DarkCyan));
        main.Children.Add(loadImageBtn);
        main.Children.Add(MakeSectionLabel("SELECTED OBJECT", WpfBrushes.LightGray));
        main.Children.Add(selectedInfo);
        main.Children.Add(MakeSliderLabel("Selected Drawing Width"));
        main.Children.Add(selectedWidthSlider);
        main.Children.Add(MakeSliderLabel("Selected Text Size"));
        main.Children.Add(selectedTextSizeSlider);
        main.Children.Add(MakeSliderLabel("Selected Formula / Image Size"));
        main.Children.Add(selectedImageSizeSlider);
        main.Children.Add(MakeSectionLabel("ACTIONS", WpfBrushes.Red));
        main.Children.Add(deleteSelectedBtn);
        main.Children.Add(clearBtn);

        scroll.Content = main;
        panelWindow.Content = scroll;
        panelWindow.Closed += (s, e) => panelWindow = null;
        panelWindow.Show();
    }

    private TextBlock MakeSliderLabel(string text)
    {
        TextBlock tb = new TextBlock();
        tb.Text = text;
        tb.Foreground = WpfBrushes.White;
        tb.Margin = new Thickness(0, 4, 0, 0);
        return tb;
    }

    private Button MakeColorButton(string name, WpfBrush brush)
    {
        Button btn = new Button();
        btn.Content = name;
        btn.Width = 80;
        btn.Height = 28;
        btn.Margin = new Thickness(2);
        btn.Background = brush;
        btn.BorderBrush = WpfBrushes.Black;
        btn.BorderThickness = new Thickness(1.1);
        btn.FontWeight = FontWeights.Bold;

        if (brush == WpfBrushes.White || brush == WpfBrushes.Gold || brush == WpfBrushes.Yellow)
            btn.Foreground = WpfBrushes.Black;
        else
            btn.Foreground = WpfBrushes.White;

        btn.Click += (s, e) =>
        {
            MarkerColor = brush;
            TextColor = brush;

            if (selectedStrokeIndex >= 0 && selectedStrokeIndex < strokes.Count)
                strokes[selectedStrokeIndex].Color = brush;

            if (selectedTextIndex >= 0 && selectedTextIndex < textNotes.Count)
                textNotes[selectedTextIndex].Color = brush;

            ForceRefresh();
        };

        return btn;
    }

    private void AddPastedText()
    {
        if (pasteBox == null || string.IsNullOrWhiteSpace(pasteBox.Text))
            return;

        TextNote note = new TextNote();
        note.Text = pasteBox.Text;
        note.Position = new WpfPoint(80, 80);
        note.Color = TextColor;
        note.Size = TextSize;
        note.ShowBackground = ShowTextBackground;

        textNotes.Add(note);

        selectedTextIndex = textNotes.Count - 1;
        selectedStrokeIndex = -1;
        selectedImageIndex = -1;

        UpdateSelectedControls();
        ForceRefresh();
    }

    private void RenderFormulaToChart()
    {
        if (formulaBox == null || string.IsNullOrWhiteSpace(formulaBox.Text))
            return;

        string path = RenderFormulaImage(formulaBox.Text);
        if (string.IsNullOrEmpty(path) || !File.Exists(path))
            return;

        ImageNote note = new ImageNote();
        note.FilePath = path;
        note.Position = new WpfPoint(100, 100);

        try
        {
            BitmapImage temp = new BitmapImage();
            temp.BeginInit();
            temp.UriSource = new Uri(path);
            temp.CacheOption = BitmapCacheOption.OnLoad;
            temp.EndInit();

            note.Width = temp.PixelWidth;
            note.Height = temp.PixelHeight;
        }
        catch
        {
            note.Width = 360;
            note.Height = 90;
        }

        imageNotes.Add(note);

        selectedImageIndex = imageNotes.Count - 1;
        selectedTextIndex = -1;
        selectedStrokeIndex = -1;

        UpdateSelectedControls();
        ForceRefresh();
    }

    private string RenderFormulaImage(string formula)
    {
        Typeface regular = new Typeface("Cambria Math");
        Typeface italic = new Typeface(new FontFamily("Cambria Math"), FontStyles.Italic, FontWeights.Normal, FontStretches.Normal);

        double font = 30;
        double paddingLeft = 20;
        double paddingRight = 24;
        double contentWidth = MeasureFormulaContentWidth(formula, font, regular, italic);

        int width = Math.Max(140, (int)Math.Ceiling(paddingLeft + contentWidth + paddingRight));
        int height = 140;

        DrawingVisual visual = new DrawingVisual();

        using (DrawingContext dc = visual.RenderOpen())
        {
            if (ShowTextBackground)
            {
                LinearGradientBrush gradient = new LinearGradientBrush();
                gradient.StartPoint = new WpfPoint(0, 0);
                gradient.EndPoint = new WpfPoint(1, 1);
                gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0, 0, 0), 0.0));
                gradient.GradientStops.Add(new GradientStop(Color.FromRgb(5, 18, 45), 0.55));
                gradient.GradientStops.Add(new GradientStop(Color.FromRgb(0, 0, 0), 1.0));
                dc.DrawRectangle(gradient, null, new Rect(0, 0, width, height));
            }
            else
            {
                dc.DrawRectangle(WpfBrushes.Transparent, null, new Rect(0, 0, width, height));
            }
            double x = paddingLeft;
            double y = 42;

            string[] parts = formula.Split('=');
            for (int i = 0; i < parts.Length; i++)
            {
                string part = parts[i].Trim();

                if (part.Contains("/"))
                    x = DrawFraction(dc, part, x, y, font, regular);
                else
                {
                    Typeface tf = i == 0 && part.Length <= 3 ? italic : regular;
                    x = DrawText(dc, part, x, y + 16, font, tf, WpfBrushes.White);
                }

                if (i < parts.Length - 1)
                    x = DrawText(dc, "=", x + 10, y + 16, font, regular, WpfBrushes.White) + 10;
            }
        }

        RenderTargetBitmap bmp = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
        bmp.Render(visual);

        string dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "NinjaTrader 8", "templates", "WTFormulaImages");
        if (!Directory.Exists(dir))
            Directory.CreateDirectory(dir);

        string file = Path.Combine(dir, "formula_" + DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".png");

        PngBitmapEncoder encoder = new PngBitmapEncoder();
        encoder.Frames.Add(BitmapFrame.Create(bmp));

        using (FileStream fs = new FileStream(file, FileMode.Create))
            encoder.Save(fs);

        return file;
    }

    private double MeasureFormulaContentWidth(string formula, double size, Typeface regular, Typeface italic)
    {
        double x = 0;
        string[] parts = formula.Split('=');

        for (int i = 0; i < parts.Length; i++)
        {
            string part = parts[i].Trim();

            if (part.Contains("/"))
                x += MeasureFractionWidth(part, size, regular);
            else
            {
                Typeface tf = i == 0 && part.Length <= 3 ? italic : regular;
                FormattedText ft = new FormattedText(part, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, tf, size, WpfBrushes.White);
                x += ft.WidthIncludingTrailingWhitespace;
            }

            if (i < parts.Length - 1)
            {
                FormattedText eq = new FormattedText("=", CultureInfo.InvariantCulture, FlowDirection.LeftToRight, regular, size, WpfBrushes.White);
                x += 20 + eq.WidthIncludingTrailingWhitespace;
            }
        }

        return x;
    }

    private double MeasureFractionWidth(string frac, double size, Typeface typeface)
    {
        string numerator = "";
        string denominator = "";
        ParseFraction(frac, out numerator, out denominator);

        numerator = CleanFormulaText(numerator);
        denominator = CleanFormulaText(denominator);

        FormattedText top = new FormattedText(numerator, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, size, WpfBrushes.White);
        FormattedText bottom = new FormattedText(denominator, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, size, WpfBrushes.White);

        return Math.Max(top.Width, bottom.Width) + 26;
    }

    private double DrawText(DrawingContext dc, string text, double x, double y, double size, Typeface typeface, WpfBrush brush)
    {
        FormattedText ft = new FormattedText(text, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, size, brush);
        dc.DrawText(ft, new WpfPoint(x, y));
        return x + ft.WidthIncludingTrailingWhitespace;
    }

    private double DrawFraction(DrawingContext dc, string frac, double x, double y, double size, Typeface typeface)
    {
        string numerator = "";
        string denominator = "";
        ParseFraction(frac, out numerator, out denominator);

        numerator = CleanFormulaText(numerator);
        denominator = CleanFormulaText(denominator);

        FormattedText top = new FormattedText(numerator, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, size, WpfBrushes.White);
        FormattedText bottom = new FormattedText(denominator, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, typeface, size, WpfBrushes.White);

        double w = Math.Max(top.Width, bottom.Width) + 18;
        double topX = x + (w - top.Width) / 2;
        double bottomX = x + (w - bottom.Width) / 2;

        dc.DrawText(top, new WpfPoint(topX, y - 18));
        Pen linePen = new Pen(WpfBrushes.White, 2);
        dc.DrawLine(linePen, new WpfPoint(x, y + 24), new WpfPoint(x + w, y + 24));
        dc.DrawText(bottom, new WpfPoint(bottomX, y + 30));

        return x + w + 8;
    }

    private void ParseFraction(string text, out string numerator, out string denominator)
    {
        numerator = text;
        denominator = "1";

        int slash = text.IndexOf('/');
        if (slash < 0)
            return;

        numerator = text.Substring(0, slash).Trim();
        denominator = text.Substring(slash + 1).Trim();

        numerator = StripOuterParentheses(numerator);
        denominator = StripOuterParentheses(denominator);
    }

    private string StripOuterParentheses(string s)
    {
        s = s.Trim();
        while (s.StartsWith("(") && s.EndsWith(")") && s.Length > 2)
            s = s.Substring(1, s.Length - 2).Trim();
        return s;
    }

    private string CleanFormulaText(string s)
    {
        s = s.Replace("-", " − ");
        s = s.Replace("+", " + ");
        s = s.Replace("*", " × ");
        s = s.Replace("  ", " ");
        return s.Trim();
    }

    private void LoadImageFromFile()
    {
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.Filter = "Image Files|*.png;*.jpg;*.jpeg;*.bmp";
        dlg.Title = "Choose Image";

        if (dlg.ShowDialog() != true)
            return;

        ImageNote note = new ImageNote();
        note.FilePath = dlg.FileName;
        note.Position = new WpfPoint(100, 100);

        try
        {
            BitmapImage temp = new BitmapImage();
            temp.BeginInit();
            temp.UriSource = new Uri(dlg.FileName);
            temp.CacheOption = BitmapCacheOption.OnLoad;
            temp.EndInit();

            double originalW = temp.PixelWidth;
            double originalH = temp.PixelHeight;

            note.Width = 280;
            note.Height = originalW > 0 ? 280 * (originalH / originalW) : 140;
        }
        catch
        {
            note.Width = 280;
            note.Height = 140;
        }

        imageNotes.Add(note);

        selectedImageIndex = imageNotes.Count - 1;
        selectedTextIndex = -1;
        selectedStrokeIndex = -1;

        UpdateSelectedControls();
        ForceRefresh();
    }

    private void DeleteSelected()
    {
        if (selectedImageIndex >= 0 && selectedImageIndex < imageNotes.Count)
            imageNotes.RemoveAt(selectedImageIndex);
        else if (selectedTextIndex >= 0 && selectedTextIndex < textNotes.Count)
            textNotes.RemoveAt(selectedTextIndex);
        else if (selectedStrokeIndex >= 0 && selectedStrokeIndex < strokes.Count)
            strokes.RemoveAt(selectedStrokeIndex);

        selectedImageIndex = -1;
        selectedTextIndex = -1;
        selectedStrokeIndex = -1;

        UpdateSelectedControls();
        ForceRefresh();
    }

    private void SelectItemAtPoint(WpfPoint p)
    {
        selectedImageIndex = HitTestImage(p);
        selectedTextIndex = selectedImageIndex == -1 ? HitTestText(p) : -1;
        selectedStrokeIndex = selectedImageIndex == -1 && selectedTextIndex == -1 ? HitTestStroke(p) : -1;
    }

    private int HitTestImage(WpfPoint p)
    {
        for (int i = imageNotes.Count - 1; i >= 0; i--)
        {
            ImageNote img = imageNotes[i];
            if (p.X >= img.Position.X - 22 && p.X <= img.Position.X + img.Width && p.Y >= img.Position.Y - 8 && p.Y <= img.Position.Y + img.Height)
                return i;
        }
        return -1;
    }

    private int HitTestText(WpfPoint p)
    {
        for (int i = textNotes.Count - 1; i >= 0; i--)
        {
            TextNote note = textNotes[i];
            double width = EstimateTextWidth(note.Text, note.Size);
            double height = EstimateTextHeight(note.Text, note.Size);
            if (p.X >= note.Position.X - 22 && p.X <= note.Position.X + width + 20 && p.Y >= note.Position.Y - 8 && p.Y <= note.Position.Y + height + 20)
                return i;
        }
        return -1;
    }

    private int HitTestStroke(WpfPoint p)
    {
        for (int i = strokes.Count - 1; i >= 0; i--)
        {
            if (strokes[i] == null || strokes[i].Points == null || strokes[i].Points.Count == 0)
                continue;

            double minX = double.MaxValue;
            double minY = double.MaxValue;
            double maxX = double.MinValue;
            double maxY = double.MinValue;

            foreach (WpfPoint pt in strokes[i].Points)
            {
                minX = Math.Min(minX, pt.X);
                minY = Math.Min(minY, pt.Y);
                maxX = Math.Max(maxX, pt.X);
                maxY = Math.Max(maxY, pt.Y);
            }

            double pad = strokes[i].Width + 14;
            if (p.X >= minX - pad && p.X <= maxX + pad && p.Y >= minY - pad && p.Y <= maxY + pad)
                return i;
        }
        return -1;
    }

    private double EstimateTextWidth(string text, int size)
    {
        if (string.IsNullOrEmpty(text))
            return 40;

        string[] lines = text.Split('\n');
        double max = 0;

        foreach (string line in lines)
        {
            double w = line.Length * size * 0.58;
            if (w > max)
                max = w;
        }

        return Math.Max(40, max);
    }

    private double EstimateTextHeight(string text, int size)
    {
        int lines = 1;

        if (!string.IsNullOrEmpty(text))
            lines = Math.Max(1, text.Split('\n').Length);

        return Math.Max(40, lines * (size + 8));
    }

    private bool TryDeleteAtPoint(WpfPoint p)
    {
        for (int i = textNotes.Count - 1; i >= 0; i--)
        {
            TextNote note = textNotes[i];

            if (p.X >= note.Position.X - 24 &&
                p.X <= note.Position.X - 2 &&
                p.Y >= note.Position.Y - 4 &&
                p.Y <= note.Position.Y + 20)
            {
                textNotes.RemoveAt(i);
                selectedTextIndex = -1;
                selectedStrokeIndex = -1;
                selectedImageIndex = -1;
                UpdateSelectedControls();
                return true;
            }
        }

        for (int i = imageNotes.Count - 1; i >= 0; i--)
        {
            ImageNote img = imageNotes[i];

            if (p.X >= img.Position.X - 24 &&
                p.X <= img.Position.X - 2 &&
                p.Y >= img.Position.Y - 4 &&
                p.Y <= img.Position.Y + 20)
            {
                imageNotes.RemoveAt(i);
                selectedTextIndex = -1;
                selectedStrokeIndex = -1;
                selectedImageIndex = -1;
                UpdateSelectedControls();
                return true;
            }
        }

        for (int i = strokes.Count - 1; i >= 0; i--)
        {
            WpfPoint pos = GetStrokeTopRight(strokes[i]);

            if (p.X >= pos.X + 6 &&
                p.X <= pos.X + 24 &&
                p.Y >= pos.Y - 10 &&
                p.Y <= pos.Y + 12)
            {
                strokes.RemoveAt(i);
                selectedTextIndex = -1;
                selectedStrokeIndex = -1;
                selectedImageIndex = -1;
                UpdateSelectedControls();
                return true;
            }
        }

        return false;
    }

    private void MoveStroke(int index, double dx, double dy)
    {
        if (index < 0 || index >= strokes.Count)
            return;

        for (int i = 0; i < strokes[index].Points.Count; i++)
        {
            strokes[index].Points[i] = new WpfPoint(strokes[index].Points[i].X + dx, strokes[index].Points[i].Y + dy);
        }
    }

    private WpfPoint GetStrokeTopRight(StrokeItem stroke)
    {
        double maxX = double.MinValue;
        double minY = double.MaxValue;

        foreach (WpfPoint p in stroke.Points)
        {
            maxX = Math.Max(maxX, p.X);
            minY = Math.Min(minY, p.Y);
        }
        return new WpfPoint(maxX, minY);
    }

    private void DrawStrokeDeleteX(StrokeItem stroke)
    {
        WpfPoint pos = GetStrokeTopRight(stroke);
        using (D2D1Brush red = MakeDxBrush(WpfBrushes.Red))
        {
            float x = (float)(ChartPanel.X + pos.X + 8);
            float y = (float)(ChartPanel.Y + pos.Y - 8);
            RenderTarget.DrawLine(new Vector2(x, y), new Vector2(x + 12, y + 12), red, 3);
            RenderTarget.DrawLine(new Vector2(x + 12, y), new Vector2(x, y + 12), red, 3);
        }
    }

    private void DrawTextDeleteX(TextNote note)
    {
        using (D2D1Brush red = MakeDxBrush(WpfBrushes.Red))
        {
            float x = (float)(ChartPanel.X + note.Position.X - 18);
            float y = (float)(ChartPanel.Y + note.Position.Y);
            RenderTarget.DrawLine(new Vector2(x, y), new Vector2(x + 12, y + 12), red, 3);
            RenderTarget.DrawLine(new Vector2(x + 12, y), new Vector2(x, y + 12), red, 3);
        }
    }

    private void DrawImageDeleteX(ImageNote img)
    {
        using (D2D1Brush red = MakeDxBrush(WpfBrushes.Red))
        {
            float x = (float)(ChartPanel.X + img.Position.X - 18);
            float y = (float)(ChartPanel.Y + img.Position.Y);
            RenderTarget.DrawLine(new Vector2(x, y), new Vector2(x + 12, y + 12), red, 3);
            RenderTarget.DrawLine(new Vector2(x + 12, y), new Vector2(x, y + 12), red, 3);
        }
    }


    private void OpenCalculator()
    {
        if (calculatorWindow != null)
        {
            calculatorWindow.Activate();
            return;
        }

        calculatorWindow = new Window();
        calculatorWindow.Title = "WT Calculator";
        calculatorWindow.Width = 520;
        calculatorWindow.Height = 760;
        calculatorWindow.MinWidth = 520;
        calculatorWindow.MinHeight = 650;
        calculatorWindow.Topmost = true;
        calculatorWindow.ResizeMode = ResizeMode.CanResize;
        calculatorWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
        calculatorWindow.Background = SciFiPanelBrush();

        Grid outer = new Grid();
        outer.Margin = new Thickness(12);
        outer.Background = SciFiPanelBrush();
        outer.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        outer.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        outer.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        outer.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        outer.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

        Grid header = new Grid();
        header.Margin = new Thickness(0, 0, 0, 10);
        header.ColumnDefinitions.Add(new ColumnDefinition());
        header.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(48) });

        TextBlock title = new TextBlock();
        title.Text = "WT CALCULATOR";
        title.Foreground = WpfBrushes.DodgerBlue;
        title.FontWeight = FontWeights.Bold;
        title.FontSize = 17;
        title.VerticalAlignment = VerticalAlignment.Center;
        Grid.SetColumn(title, 0);
        header.Children.Add(title);

        Button closeBtn = new Button();
        closeBtn.Content = "X";
        closeBtn.Height = 34;
        closeBtn.Width = 42;
        closeBtn.Margin = new Thickness(0);
        StyleButton(closeBtn, WpfBrushes.Red, WpfBrushes.Red);
        closeBtn.Click += (s, e) => CloseCalculator();
        Grid.SetColumn(closeBtn, 1);
        header.Children.Add(closeBtn);

        calculatorDisplay = new TextBox();
        calculatorDisplay.Height = 50;
        calculatorDisplay.FontSize = 22;
        calculatorDisplay.TextAlignment = TextAlignment.Right;
        calculatorDisplay.Margin = new Thickness(0, 0, 0, 12);
        calculatorDisplay.Padding = new Thickness(8, 6, 8, 6);
        StyleTextBox(calculatorDisplay, WpfBrushes.DodgerBlue);

        ScrollViewer calcScroll = new ScrollViewer();
        calcScroll.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
        calcScroll.Background = SciFiPanelBrush();

        StackPanel calcMain = new StackPanel();
        calcMain.Background = SciFiPanelBrush();

        TextBlock basicLabel = MakeSectionLabel("BASIC CALCULATOR", WpfBrushes.Cyan);
        calcMain.Children.Add(basicLabel);

        UniformGrid grid = new UniformGrid();
        grid.Columns = 4;
        grid.Rows = 5;
        grid.Margin = new Thickness(0, 0, 0, 12);

        string[] keys = new string[]
        {
            "7", "8", "9", "/",
            "4", "5", "6", "*",
            "1", "2", "3", "-",
            "0", ".", "(", ")",
            "C", "DEL", "^", "+"
        };

        for (int i = 0; i < keys.Length; i++)
        {
            Button b = new Button();
            b.Content = keys[i];
            b.MinHeight = 48;
            b.Margin = new Thickness(4);
            StyleButton(b, WpfBrushes.Cyan, WpfBrushes.White);
            string key = keys[i];
            b.Click += (s, e) => CalculatorKey(key);
            grid.Children.Add(b);
        }

        calcMain.Children.Add(grid);

        TextBlock algebraLabel = MakeSectionLabel("ALGEBRA PRESETS", WpfBrushes.MediumPurple);
        calcMain.Children.Add(algebraLabel);

        UniformGrid algebraGrid = new UniformGrid();
        algebraGrid.Columns = 2;
        algebraGrid.Margin = new Thickness(0, 0, 0, 12);
        algebraGrid.Children.Add(MakeCalculatorPresetButton("Slope", "m=(y2-y1)/(x2-x1)", WpfBrushes.MediumPurple));
        algebraGrid.Children.Add(MakeCalculatorPresetButton("Point Slope", "y-y1=m(x-x1)", WpfBrushes.MediumPurple));
        algebraGrid.Children.Add(MakeCalculatorPresetButton("Slope Intercept", "y=mx+b", WpfBrushes.MediumPurple));
        algebraGrid.Children.Add(MakeCalculatorPresetButton("Quadratic", "x=(-b+sqrt(b^2-4ac))/(2a)", WpfBrushes.MediumPurple));
        algebraGrid.Children.Add(MakeCalculatorPresetButton("Difference", "change=(new-old)/(old)", WpfBrushes.MediumPurple));
        algebraGrid.Children.Add(MakeCalculatorPresetButton("Percent", "percent=part/whole", WpfBrushes.MediumPurple));
        calcMain.Children.Add(algebraGrid);

        TextBlock geometryLabel = MakeSectionLabel("GEOMETRY PRESETS", WpfBrushes.DarkCyan);
        calcMain.Children.Add(geometryLabel);

        UniformGrid geometryGrid = new UniformGrid();
        geometryGrid.Columns = 2;
        geometryGrid.Margin = new Thickness(0, 0, 0, 12);
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Triangle Area", "A=(b*h)/2", WpfBrushes.DarkCyan));
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Rectangle Area", "A=l*w", WpfBrushes.DarkCyan));
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Circle Area", "A=pi*r^2", WpfBrushes.DarkCyan));
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Pythagorean", "c^2=a^2+b^2", WpfBrushes.DarkCyan));
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Distance", "d=sqrt((x2-x1)^2+(y2-y1)^2)", WpfBrushes.DarkCyan));
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Midpoint X", "Mx=(x1+x2)/2", WpfBrushes.DarkCyan));
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Midpoint Y", "My=(y1+y2)/2", WpfBrushes.DarkCyan));
        geometryGrid.Children.Add(MakeCalculatorPresetButton("Circle Circumference", "C=2*pi*r", WpfBrushes.DarkCyan));
        calcMain.Children.Add(geometryGrid);

        calcScroll.Content = calcMain;

        Grid actionRow = new Grid();
        actionRow.Margin = new Thickness(0, 10, 0, 10);
        actionRow.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
        actionRow.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });

        Button equalsBtn = new Button();
        equalsBtn.Content = "=";
        equalsBtn.Height = 42;
        equalsBtn.Margin = new Thickness(0, 0, 6, 0);
        StyleButton(equalsBtn, WpfBrushes.LimeGreen, WpfBrushes.LimeGreen);
        equalsBtn.Click += (s, e) => CalculateDisplay();
        Grid.SetColumn(equalsBtn, 0);
        actionRow.Children.Add(equalsBtn);

        Button sendFormulaBtn = new Button();
        sendFormulaBtn.Content = "SEND TO FORMULA";
        sendFormulaBtn.Height = 42;
        sendFormulaBtn.Margin = new Thickness(6, 0, 0, 0);
        StyleButton(sendFormulaBtn, WpfBrushes.MediumPurple, WpfBrushes.MediumPurple);
        sendFormulaBtn.Click += (s, e) =>
        {
            if (formulaBox != null && calculatorDisplay != null)
                formulaBox.Text = calculatorDisplay.Text;
        };
        Grid.SetColumn(sendFormulaBtn, 1);
        actionRow.Children.Add(sendFormulaBtn);

        TextBlock help = new TextBlock();
        help.Text = "Basic math evaluates with =. Algebra and geometry presets are sent to the formula renderer.";
        help.Foreground = WpfBrushes.LightGray;
        help.TextWrapping = TextWrapping.Wrap;
        help.Margin = new Thickness(0, 2, 0, 0);

        Grid.SetRow(header, 0);
        Grid.SetRow(calculatorDisplay, 1);
        Grid.SetRow(calcScroll, 2);
        Grid.SetRow(actionRow, 3);
        Grid.SetRow(help, 4);

        outer.Children.Add(header);
        outer.Children.Add(calculatorDisplay);
        outer.Children.Add(calcScroll);
        outer.Children.Add(actionRow);
        outer.Children.Add(help);

        calculatorWindow.Content = outer;
        calculatorWindow.Closed += (s, e) => calculatorWindow = null;
        calculatorWindow.Show();
    }

    private Button MakeCalculatorPresetButton(string label, string formula, WpfBrush color)
    {
        Button b = new Button();
        b.Content = label;
        b.MinHeight = 38;
        b.Margin = new Thickness(4);
        StyleButton(b, color, color);
        b.Click += (s, e) =>
        {
            if (calculatorDisplay != null)
            {
                calculatorDisplay.Text = formula;
                calculatorDisplay.CaretIndex = calculatorDisplay.Text.Length;
            }
        };
        return b;
    }

    private void CloseCalculator()
    {
        if (calculatorWindow != null)
        {
            Window temp = calculatorWindow;
            calculatorWindow = null;
            temp.Close();
        }
    }

    private void CalculatorKey(string key)
    {
        if (calculatorDisplay == null)
            return;

        if (key == "C")
        {
            calculatorDisplay.Text = "";
            return;
        }

        if (key == "DEL")
        {
            if (calculatorDisplay.Text.Length > 0)
                calculatorDisplay.Text = calculatorDisplay.Text.Substring(0, calculatorDisplay.Text.Length - 1);
            return;
        }

        calculatorDisplay.Text += key;
        calculatorDisplay.CaretIndex = calculatorDisplay.Text.Length;
    }

    private void CalculateDisplay()
    {
        if (calculatorDisplay == null)
            return;

        try
        {
            double result = EvaluateExpression(calculatorDisplay.Text);
            calculatorDisplay.Text = result.ToString("0.########", CultureInfo.InvariantCulture);
            calculatorDisplay.CaretIndex = calculatorDisplay.Text.Length;
        }
        catch (Exception ex)
        {
            calculatorDisplay.Text = "ERROR: " + ex.Message;
            calculatorDisplay.CaretIndex = calculatorDisplay.Text.Length;
        }
    }

    private double EvaluateExpression(string expression)
    {
        MathParser parser = new MathParser(expression);
        return parser.Parse();
    }

    private class MathParser
    {
        private readonly string text;
        private int pos;

        public MathParser(string expression)
        {
            text = expression == null ? "" : expression.Replace(" ", "");
            pos = 0;
        }

        public double Parse()
        {
            double value = ParseExpression();
            if (pos < text.Length)
                throw new Exception("Bad input");
            return value;
        }

        private double ParseExpression()
        {
            double value = ParseTerm();

            while (pos < text.Length)
            {
                char op = text[pos];
                if (op != '+' && op != '-')
                    break;

                pos++;
                double right = ParseTerm();

                if (op == '+')
                    value += right;
                else
                    value -= right;
            }

            return value;
        }

        private double ParseTerm()
        {
            double value = ParsePower();

            while (pos < text.Length)
            {
                char op = text[pos];
                if (op != '*' && op != '/')
                    break;

                pos++;
                double right = ParsePower();

                if (op == '*')
                    value *= right;
                else
                    value /= right;
            }

            return value;
        }

        private double ParsePower()
        {
            double value = ParseFactor();

            while (pos < text.Length && text[pos] == '^')
            {
                pos++;
                double right = ParseFactor();
                value = Math.Pow(value, right);
            }

            return value;
        }

        private double ParseFactor()
        {
            if (pos >= text.Length)
                throw new Exception("Missing value");

            if (text[pos] == '+')
            {
                pos++;
                return ParseFactor();
            }

            if (text[pos] == '-')
            {
                pos++;
                return -ParseFactor();
            }

            if (text[pos] == '(')
            {
                pos++;
                double value = ParseExpression();
                if (pos >= text.Length || text[pos] != ')')
                    throw new Exception("Missing )");
                pos++;
                return value;
            }

            return ParseNumber();
        }

        private double ParseNumber()
        {
            int start = pos;

            while (pos < text.Length && (char.IsDigit(text[pos]) || text[pos] == '.'))
                pos++;

            if (start == pos)
                throw new Exception("Number expected");

            string number = text.Substring(start, pos - start);
            double value;

            if (!double.TryParse(number, NumberStyles.Float, CultureInfo.InvariantCulture, out value))
                throw new Exception("Bad number");

            return value;
        }
    }

    private void UpdateSelectedControls()
    {
        if (selectedInfo == null)
            return;

        if (selectedStrokeIndex >= 0 && selectedStrokeIndex < strokes.Count)
        {
            selectedInfo.Text = "Selected: Drawing #" + (selectedStrokeIndex + 1);
            selectedWidthSlider.Value = strokes[selectedStrokeIndex].Width;
        }
        else if (selectedTextIndex >= 0 && selectedTextIndex < textNotes.Count)
        {
            selectedInfo.Text = "Selected: Text #" + (selectedTextIndex + 1);
            selectedTextSizeSlider.Value = textNotes[selectedTextIndex].Size;
            if (textBackgroundCheckBox != null)
                textBackgroundCheckBox.IsChecked = textNotes[selectedTextIndex].ShowBackground;
        }
        else if (selectedImageIndex >= 0 && selectedImageIndex < imageNotes.Count)
        {
            selectedInfo.Text = "Selected: Formula / Image #" + (selectedImageIndex + 1);
            selectedImageSizeSlider.Value = imageNotes[selectedImageIndex].Width;
        }
        else
        {
            selectedInfo.Text = "Selected: None";
        }
    }

    private void ClosePanel()
    {
        if (panelWindow != null)
        {
            Window temp = panelWindow;
            panelWindow = null;
            temp.Close();
        }
    }

    #region Properties

    [Range(1, 50)]
    [Display(Name = "Marker Width", Order = 1, GroupName = "Marker Settings")]
    public int MarkerWidth { get; set; }

    [XmlIgnore]
    [Display(Name = "Marker Color", Order = 2, GroupName = "Marker Settings")]
    public WpfBrush MarkerColor { get; set; }

    [Range(8, 60)]
    [Display(Name = "Text Size", Order = 3, GroupName = "Marker Settings")]
    public int TextSize { get; set; }

    [XmlIgnore]
    [Display(Name = "Text Color", Order = 4, GroupName = "Marker Settings")]
    public WpfBrush TextColor { get; set; }

    [Display(Name = "Show Text Background", Order = 5, GroupName = "Marker Settings")]
    public bool ShowTextBackground { get; set; }

    #endregion
}

}