// 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; }