// Show the correct popup menu, give the box the user clicked.
        void PopupMenu(HitTestResult hitTest)
        {
            string text;

            // Get location for menu to appear.
            Point location = GetPopupMenuLocation(hitTest);

            // Save the line/box we are possibly changing
            popupKind = ChangeKind.None;     // will change this below if we actual pop something up!
            popupLine = hitTest.firstLine;
            popupBox = hitTest.box;

            switch (hitTest.kind) {
                case HitTestKind.NormalBox:
                    if (scoreColumn >= 0 && hitTest.box == scoreColumn) {
                        if (!(renderer.Description[hitTest.firstLine].boxes[0] is Symbol)) {
                            // In score courses, the score is in column A, so allow in-place editing of it, unless its the start triange.
                            popupKind = ChangeKind.Score;
                            popup.ShowPopup(8, (char)0, (char)0, false, MiscText.EnterScore, (string)renderer.Description[hitTest.firstLine].boxes[hitTest.box], 2, descriptionPanel, location);
                        }
                    }
                    else if (hitTest.box == 0) {
                        // Column A:
                        // We don't allow changing the sequence number, so no popup allowed
                    }
                    else if (hitTest.box == 1) {
                        // Column B
                        if (!(renderer.Description[hitTest.firstLine].boxes[0] is Symbol)) {
                            popupKind = ChangeKind.Code;
                            popup.ShowPopup(8, (char) 0, (char) 0, false, MiscText.EnterCode, (string) renderer.Description[hitTest.firstLine].boxes[1], 2, descriptionPanel, location);
                        }
                    }
                    else if (hitTest.box == 4) {
                        // Column E
                        popupKind = ChangeKind.DescriptionBox;
                        popup.ShowPopup(8, 'E', 'D', true, null, null, 0, descriptionPanel, location);
                    }
                    else if (hitTest.box == 5) {
                        // Column F
                        string initialText = "";
                        if (renderer.Description[hitTest.firstLine].boxes[5] is string && renderer.Description[hitTest.firstLine].boxes[5] != null)
                            initialText = (string) renderer.Description[hitTest.firstLine].boxes[5];
                        popupKind = ChangeKind.DescriptionBox;
                        popup.ShowPopup(8, 'F', (char) 0, true, MiscText.EnterDimensions, initialText, 4, descriptionPanel, location);
                    }
                    else {
                        // Column C, D, G, H
                        popupKind = ChangeKind.DescriptionBox;
                        popup.ShowPopup(8, (char)(hitTest.box + 'A'), (char)0, true, null, null, 0, descriptionPanel, location);
                    }
                    break;

                case HitTestKind.Directive:
                    Symbol current = renderer.Description[hitTest.firstLine].boxes[0] as Symbol;
                    if (current != null) {
                        char kind = current.Kind;       // Allow changing in the existing kind only.

                        // Only allow changing the crossing point or finish symbols.
                        if (kind == 'X' || kind == 'Z') {
                            popupKind = ChangeKind.Directive;
                            popup.ShowPopup(1, kind, (char)0, false, null, null, 0, descriptionPanel, location);
                        }
                    }
                    break;

                case HitTestKind.Title:
                    text = MiscText.EnterEventTitle;
                    popupKind = ChangeKind.Title;

                    popup.ShowPopup(8, (char)0, (char)0, false, text, CombineBoxTexts(hitTest.firstLine, hitTest.lastLine, 0, "|"), 8, descriptionPanel, location);
                    break;

                case HitTestKind.SecondaryTitle:
                    text = MiscText.EnterSecondaryTitle;
                    popupKind = ChangeKind.SecondaryTitle;

                    popup.ShowPopup(8, (char) 0, (char) 0, false, text, CombineBoxTexts(hitTest.firstLine, hitTest.lastLine, 0, "|"), 8, descriptionPanel, location);
                    break;

                case HitTestKind.Header:
                    if (hitTest.box == 0 && courseViewKind != CourseView.CourseViewKind.AllControls) {
                        // the course name. Can't change the "All Controls" name.
                        popupKind = ChangeKind.CourseName;
                        string courseName = (string) renderer.Description[hitTest.firstLine].boxes[0];
                        if (isCoursePart && courseName.Length > 2) {
                            // Remove the "-3" etc with the part number.
                            courseName = courseName.Substring(0, courseName.LastIndexOf('-'));
                        }
                        popup.ShowPopup(8, (char) 0, (char) 0, false, MiscText.EnterCourseName, courseName, 6, descriptionPanel, location);
                    }
                    else if (hitTest.box == 1  && courseViewKind == CourseView.CourseViewKind.Normal) {
                        // the length
                        string lengthText;
                        if (hasCustomLength)
                            lengthText = Util.RemoveSuffix((string)renderer.Description[hitTest.firstLine].boxes[1], "km");
                        else
                            lengthText = "";  // automatically calculated length.

                        popupKind = ChangeKind.Length;
                        popup.ShowPopup(8, (char)0, (char)0, false, MiscText.EnterLength, lengthText, 4, descriptionPanel, location);
                    }
                    else if (hitTest.box == 2) {
                        // the climb
                        popupKind = ChangeKind.Climb;
                        popup.ShowPopup(8, (char) 0, (char) 0, false, MiscText.EnterClimb, Util.RemoveMeterSuffix((string) renderer.Description[hitTest.firstLine].boxes[2]), 4, descriptionPanel, location);
                    }
                    break;

                case HitTestKind.Key:
                    popupKind = ChangeKind.Key;
                    popup.ShowPopup(8, (char) 0, (char) 0, false, MiscText.EnterSymbolText, (string) renderer.Description[hitTest.firstLine].boxes[1], 8, descriptionPanel, location);
                    break;

                case HitTestKind.OtherTextLine:
                    popupKind = ChangeKind.TextLine;
                    popup.ShowPopup(8, (char)0, (char)0, false, MiscText.EnterTextLine, CombineBoxTexts(hitTest.firstLine, hitTest.lastLine, 0, "|"), 8, descriptionPanel, location);
                    break;

                default: Debug.Fail("bad hit test kind"); break;
            }
        }
 // Popup the control code popup on the selected line.
 public void PopupControlCode()
 {
     if (firstSelectedLine >= 0) {
         HitTestResult hittest = new HitTestResult();
         hittest.kind = HitTestKind.NormalBox;
         hittest.firstLine = firstSelectedLine;
         hittest.lastLine = lastSelectedLine;
         hittest.box = 1;
         hittest.rect = renderer.BoxBounds(firstSelectedLine, lastSelectedLine, 1, 1);
         PopupMenu(hittest);
     }
 }
 // Given a hit test, determine the location where the upper-left of the popup menu should be.
 // We position it to not obscure the symbol much, but still overlap the cell a bit.
 Point GetPopupMenuLocation(HitTestResult hitTest)
 {
     return new Point((int)Math.Round(hitTest.rect.Left + renderer.CellSize * 0.5F),
                      (int)Math.Round(hitTest.rect.Top + renderer.CellSize * 0.75F));
 }
        // Hit test a point (in device coordinates) so we know what was clicked on.
        // Currently only supports single column.
        public HitTestResult HitTest(PointF point)
        {
            Debug.Assert(numColumns == 1);

            HitTestResult result = new HitTestResult();

            // Compensate for the margin.
            point.X -= margin;
            point.Y -= margin;

            // Get total size, not including margin.
            int width = (int) (WidthInCells() * cellSize);
            int height = (int) (description.Length * cellSize);

            if (point.X < 0 || point.X >= width || point.Y < 0 || point.Y >= height) {
                // Outside the bounds of the description.
                result.kind = HitTestKind.None;
                result.firstLine = result.lastLine = -1;
                result.box = -1;
                result.rect = new RectangleF();
                return result;
            }

            // What line, column are we on?
            int iLine = (int) (point.Y / cellSize);
            Debug.Assert(iLine >= 0 && iLine < description.Length);
            int iCol = (int)(point.X / cellSize);
            Debug.Assert(iCol >= 0 && iCol < WidthInCells());
            result.firstLine = result.lastLine = iLine;
            DescriptionLineKind lineKind = description[iLine].kind;

            switch (lineKind) {
                case DescriptionLineKind.Title:
                    result.kind = HitTestKind.Title;
                    result.box = 0;
                    LineBounds(iLine, out result.firstLine, out result.lastLine);
                    result.rect = new RectangleF(0, result.firstLine * cellSize, width, cellSize * (result.lastLine - result.firstLine + 1));
                    break;

                case DescriptionLineKind.SecondaryTitle:
                    result.kind = HitTestKind.SecondaryTitle;
                    result.box = 0;
                    LineBounds(iLine, out result.firstLine, out result.lastLine);
                    result.rect = new RectangleF(0, result.firstLine * cellSize, width, cellSize * (result.lastLine - result.firstLine + 1));
                    break;

                case DescriptionLineKind.Header2Box:
                    if (iCol < 3) {
                        result.kind = HitTestKind.Header;
                        result.box = 0;
                        result.rect = new RectangleF(0, iLine * cellSize, 3 * cellSize, cellSize);
                    }
                    else if (iCol < 8) {
                        result.kind = HitTestKind.Header;
                        result.box = 1;
                        result.rect = new RectangleF(3 * cellSize, iLine * cellSize, 5 * cellSize, cellSize);
                    }
                    else {
                        result.kind = HitTestKind.None;
                        result.box = -1;
                        result.rect = new RectangleF(8 * cellSize, iLine * cellSize, 5 * cellSize, cellSize);
                    }
                    break;

                case DescriptionLineKind.Header3Box:
                    if (iCol < 3) {
                        result.kind = HitTestKind.Header;
                        result.box = 0;
                        result.rect = new RectangleF(0, iLine * cellSize, 3 * cellSize, cellSize);
                    }
                    else if (iCol < 6) {
                        result.kind = HitTestKind.Header;
                        result.box = 1;
                        result.rect = new RectangleF(3 * cellSize, iLine * cellSize, 3 * cellSize, cellSize);
                    }
                    else if (iCol < 8) {
                        result.kind = HitTestKind.Header;
                        result.box = 2;
                        result.rect = new RectangleF(6 * cellSize, iLine * cellSize, 2 * cellSize, cellSize);
                    }
                    else {
                        result.kind = HitTestKind.None;
                        result.box = -1;
                        result.rect = new RectangleF(8 * cellSize, iLine * cellSize, 5 * cellSize, cellSize);
                    }
                    break;

                case DescriptionLineKind.Normal:
                    if (descriptionKind == DescriptionKind.SymbolsAndText && iCol >= 8) {
                        result.kind = HitTestKind.NormalText;
                        result.box = -1;
                        result.rect = new RectangleF(8 * cellSize, iLine * cellSize, 5 * cellSize, cellSize);
                    }
                    else if (descriptionKind == DescriptionKind.Text && iCol >= 2) {
                        result.kind = HitTestKind.NormalText;
                        result.box = -1;
                        result.rect = new RectangleF(2 * cellSize, iLine * cellSize, 6 * cellSize, cellSize);
                    }
                    else {
                        result.kind = HitTestKind.NormalBox;
                        result.box = iCol;
                        result.rect = new RectangleF(iCol * cellSize, iLine * cellSize, cellSize, cellSize);
                    }
                    break;

                case DescriptionLineKind.Directive:
                    if (descriptionKind == DescriptionKind.SymbolsAndText && iCol >= 8) {
                        result.kind = HitTestKind.DirectiveText;
                        result.box = -1;
                        result.rect = new RectangleF(8 * cellSize, iLine * cellSize, 5 * cellSize, cellSize);
                    }
                    else if (descriptionKind == DescriptionKind.Text) {
                        result.kind = HitTestKind.DirectiveText;
                        result.box = -1;
                        result.rect = new RectangleF(0, iLine * cellSize, 8 * cellSize, cellSize);
                    }
                    else {
                        result.kind = HitTestKind.Directive;
                        result.box = 0;
                        result.rect = new RectangleF(0, iLine * cellSize, 8 * cellSize, cellSize);
                    }
                    break;

                case DescriptionLineKind.Text:
                    result.kind = HitTestKind.OtherTextLine;
                    result.box = 0;
                    LineBounds(iLine, out result.firstLine, out result.lastLine);
                    result.rect = new RectangleF(0, result.firstLine * cellSize, width, cellSize * (result.lastLine - result.firstLine + 1));
                    break;

                case DescriptionLineKind.Key:
                    result.kind = HitTestKind.Key;
                    result.box = 0;
                    result.rect = new RectangleF(0, iLine * cellSize, width, cellSize);
                    break;

                default:
                    Debug.Fail("bad line kind");
                    return result;
            }

            result.rect.Offset(margin, margin);     // recompensate for the margin.
            return result;
        }