# Plan: Insert Course Point via Right-Click on Waypoint Dot
*Status: COMPLETE*

## Context

Users can append waypoints to the end of a group's course but cannot insert one between
existing waypoints. This plan adds an "Insert Course Point" option to the right-click
context menu that appears when the user right-clicks on an existing waypoint dot. The new
point is inserted *before* the clicked waypoint.

---

## Interaction Flow

1. Course display is active (Selected or All Groups mode) showing waypoint dots
2. User right-clicks on an existing waypoint dot
3. Context menu appears with **Append Course Point** and **Insert Course Point**
   - "Insert Course Point" is only visible when a waypoint dot was hit-tested
4. User clicks **Insert Course Point** → enters insertion placement mode (cursor → `crCross`)
5. User left-clicks on the map → new waypoint is inserted *before* the right-clicked
   waypoint in the linked list
6. Stays in insertion mode — each subsequent left-click inserts another point before
   the same target waypoint (new points stack up between the preceding waypoint and
   the target, in click order)
7. Right-click or ESC exits insertion mode (cursor reverts to default)

**Multi-shot placement**: like append mode, insert mode stays active for multiple clicks.
All inserted points go before the same target waypoint chosen in step 2.

---

## Changes

### 1. `mainform.pas` — insertion state, menu item, hit-testing, placement

**Add fields** to `TfrmMain` public section:

```pascal
FInsertingCoursepoint: Boolean;              // CC 20260316 true while in insert placement mode
FInsertBeforePathRec: PHCPathRec;            // CC 20260316 the path record to insert before
FInsertGroupEntry: PGroupMapEntry;           // CC 20260316 group entry owning the target waypoint
```

**Add published menu item + handler:**

```pascal
miInsertCoursepoint: TMenuItem;
procedure miInsertCoursepointClick(Sender: TObject);
```

**Add private procedure:**

```pascal
procedure InsertCoursepoint(X, Y: Integer);  // CC 20260316
```

**Modify `glbMapMouseUp`** (right-click path):

Before showing the popup, hit-test waypoint dots (reuse the same pixel-distance scan
from `WaypointDragBegin`). Store the hit result so the menu handler knows what was clicked:

```pascal
// Right-click MouseUp — hit-test waypoint dots, then show popup
if (Button = mbRight) and (not FAddingCoursepoint) and (not FInsertingCoursepoint)
  and (gSelectedGroupUUID <> '') then
begin
  HitTestWaypointDot(X, Y);   // sets FInsertBeforePathRec + FInsertGroupEntry or nils them
  miInsertCoursepoint.Visible := (FInsertBeforePathRec <> nil);
  pmGlobe.PopUp(Mouse.CursorPos.X, Mouse.CursorPos.Y);
  Exit;
end;
```

**Add `HitTestWaypointDot(X, Y)` private procedure:**

Walk all group entries whose course is visible, scan dot chains for a dot within
`HIT_PX` (8 pixels) of (X, Y). On hit, walk the PHCPathRec linked list to find
the corresponding node. Store results in `FInsertBeforePathRec` and
`FInsertGroupEntry`. On miss, set both to `nil`.

This is essentially the same scan as `WaypointDragBegin` in `uWaypointDrag.pas`
(lines 56-88) but extracted to a standalone procedure on `TfrmMain` and storing
into the insert fields instead of the drag state.

**`miInsertCoursepointClick` handler:**

```pascal
procedure TfrmMain.miInsertCoursepointClick(Sender: TObject);
begin
  FInsertingCoursepoint := True;
  glbMap.Cursor := crCross;
  glbMap.MouseZoomButton := mzbNone;
  glbMap.MouseRotateButton := mzbNone;
end;
```

**Modify `glbMapMouseDown`** — add insertion placement handling alongside append:

```pascal
// Left-click in insert placement mode — place and stay in mode
if FInsertingCoursepoint then
begin
  InsertCoursepoint(X, Y);
  Exit;
end;
```

Right-click in insert mode cancels (same as append):

```pascal
if FInsertingCoursepoint then
begin
  FInsertingCoursepoint := False;
  glbMap.Cursor := crDefault;
  glbMap.MouseZoomButton := mzbRight;
  glbMap.MouseRotateButton := mzbLeft;
end;
```

**Modify `glbMapKeyDown`** — ESC also exits insert mode (same pattern as append).

**`InsertCoursepoint(X, Y)` procedure:**

```pascal
procedure TfrmMain.InsertCoursepoint(X, Y: Integer);
var
  ptLL: TPointLL;
  pNew: PHCPathRec;
  pTarget: PHCPathRec;
  pPrev: PHCPathRec;
begin
  if not Assigned(NewPtr) then Exit;
  if FInsertBeforePathRec = nil then Exit;
  if FInsertGroupEntry = nil then Exit;

  pTarget := FInsertBeforePathRec;

  // Convert screen click to slivers
  glbMap.Projection.DeviceXYToLL(X, Y, ptLL);
  pNew := PHCPathRec(NewPtr(SizeOf(THCPathRec)));
  pNew^.DestLon := HCDegSliverLonF(GlobeUnitsToDecimal(ptLL.iLongX));
  pNew^.DestLat := HCDegSliverLatF(GlobeUnitsToDecimal(ptLL.iLatY));
  pNew^.pathEvents := nil;

  // Insert pNew before pTarget in doubly-linked list
  pNew^.next := pTarget;
  if pTarget^.prev = nil then
  begin
    // pTarget was head — pNew becomes new head
    pNew^.prev := nil;
    FInsertGroupEntry^.GroupPtr^.track := pNew;
  end
  else
  begin
    pPrev := PHCPathRec(pTarget^.prev);
    pPrev^.next := pNew;
    pNew^.prev := pPrev;
  end;
  pTarget^.prev := pNew;

  // Don't clear FInsertBeforePathRec — stays valid for next click
  // (pNew is now just before pTarget, so subsequent inserts continue
  //  to land before the same original target waypoint)
  glbMap.Invalidate;
end;
```

### 2. `mainform.lfm` — add menu item

Add `miInsertCoursepoint` to `pmGlobe` after `miAddCoursepoint`:

```
  object pmGlobe: TPopupMenu
    Left = 500
    Top = 354
    object miAddCoursepoint: TMenuItem
      Caption = 'Append Course Point'
      OnClick = miAddCoursepointClick
    end
    object miInsertCoursepoint: TMenuItem
      Caption = 'Insert Course Point'
      OnClick = miInsertCoursepointClick
      Visible = False
    end
  end
```

`Visible = False` by default — only made visible in `glbMapMouseUp` when a dot is hit.

### 3. `mainform.pas` — `FormCreate` initialization

```pascal
FInsertingCoursepoint := False;
FInsertBeforePathRec := nil;
FInsertGroupEntry := nil;
```

### 4. `MapWindow\MapWindow.txt` — history entry

```
MapWindow 20260316 2025.025+
  Insert Course Point — right-click an existing waypoint dot to open a
   context menu with "Insert Course Point". Left-click on the map to place
   new waypoints before the clicked dot. Stays in placement mode for
   multiple insertions; ESC or right-click exits.
```

---

## Files Modified

| File | Change |
|------|--------|
| `mainform.pas` | Add insertion state fields, `HitTestWaypointDot`, `InsertCoursepoint`, `miInsertCoursepointClick`; modify `glbMapMouseUp`, `glbMapMouseDown`, `glbMapKeyDown` |
| `mainform.lfm` | Add `miInsertCoursepoint: TMenuItem` to `pmGlobe` |
| `MapWindow\MapWindow.txt` | Add history entry |

---

## Key Data Structures

**THCPathRec** (doubly-linked list) — insert-before operation:

```
Before:  ... ↔ pPrev ↔ pTarget ↔ ...
After:   ... ↔ pPrev ↔ pNew ↔ pTarget ↔ ...
```

If `pTarget` is the head (`prev = nil`):
```
Before:  track → pTarget ↔ ...
After:   track → pNew ↔ pTarget ↔ ...
```

**Dot-to-PathRec mapping**: waypoint dots in `TPointStore` are indexed 0..N-1 matching
the linked list traversal order. Walk the linked list `idx` steps to find the
corresponding `PHCPathRec`.

---

## Edge Cases

- **No waypoint dot under cursor**: `miInsertCoursepoint.Visible := False` — menu item hidden
- **NewPtr not assigned**: guard and exit silently
- **Insert before the first waypoint**: new node becomes list head (`GroupPtr^.track := pNew`)
- **Right-click or ESC while in insert mode**: cancel without placing
- **Append mode active when right-clicking a dot**: right-click already exits append mode
  before reaching the popup logic, so no conflict

---

## Verification

1. `lazbuild --build-all MapWindow.lpi`
2. Launch HC, load a scenario, enable course display (Selected or All Groups)
3. Select a group that has 2+ course waypoints
4. Right-click on a waypoint dot → verify "Insert Course Point" appears in menu
5. Click "Insert Course Point" → cursor changes to crosshair
6. Left-click on the map → verify new waypoint dot appears *before* the right-clicked dot
   and the course line re-routes through the new point
7. Left-click again → verify a second point is inserted (stays in placement mode),
   and it also lands before the same original target dot
8. Press ESC → verify cursor reverts and clicking no longer inserts
9. Right-click away from any dot → verify only "Append Course Point" appears (no "Insert")
9. Test inserting before the *first* waypoint — verify it becomes the new first leg
10. Press ESC during insert placement → verify cursor reverts, no point placed
11. Right-click during insert placement → verify cancel
12. Drag the newly inserted waypoint → verify existing drag still works
