# Plan: Add Coursepoint via Right-Click Context Menu
*Status: COMPLETE*

## Context

Users currently cannot add new course waypoints from the MapWindow DLL — they can only drag
existing waypoints. This plan adds a right-click context menu with an "Add Coursepoint" option
that lets the user click on the map to append waypoints to the selected group's course.

The HC callback function pointers (`NewPtr`, `DispPtr`, etc.) passed during `DLLInit` are
currently inside a `{ }` comment block (MapWindow.lpr:91-141) and never captured. We need to
uncomment and clean up this code so the DLL can allocate `PHCPathRec` nodes via HC's memory
manager.

---

## Interaction Flow

1. User right-clicks on the globe
2. If a group is selected (`gSelectedGroupUUID <> ''`), popup menu appears with **Add Coursepoint**
3. User clicks **Add Coursepoint** → enters placement mode (cursor changes to `crCross`)
4. Each left-click appends a new waypoint at that position to the selected group's course
   - Works even if the group has no existing course (creates the first waypoint)
   - Stays in placement mode — user can keep clicking to add more points
5. Right-click or ESC exits placement mode (cursor reverts to default)

---

## Changes

### 1. `mainform.pas` — callback fields, popup menu, placement state

**Add public fields** for HC callbacks (only the ones we need now + likely future use):

```pascal
NewPtr: THCNewPtr;
DisposPtr: THCDisposPtr;
HCQueueHarpoonEvent: THCQueueHarpoonEvent;
HCdllCheckLatLonDepth: THCdllCheckLatLonDepth;
HCMessageLogLine: THCMessageLogLine;
```

**Add placement state field:**

```pascal
FAddingCoursepoint: Boolean;  // true while in "click to place" mode
```

**Add TPopupMenu + TMenuItem** in the form class published section:

```pascal
pmGlobe: TPopupMenu;
miAddCoursepoint: TMenuItem;
procedure miAddCoursepointClick(Sender: TObject);
```

**Add to `mainform.lfm`** — new TPopupMenu after the `icons: TImageList` block (before final `end`):

```
  object pmGlobe: TPopupMenu
    Left = 500
    Top = 354
    object miAddCoursepoint: TMenuItem
      Caption = 'Add Coursepoint'
      OnClick = miAddCoursepointClick
    end
  end
```

**`miAddCoursepointClick`**: set `FAddingCoursepoint := True`, change `glbMap.Cursor := crCross`.

**Modify `glbMapMouseDown`**:
- `mbRight` + `gSelectedGroupUUID <> ''` → show `pmGlobe` at mouse position
- `mbLeft` + `FAddingCoursepoint` → call `AppendCoursepoint` (see below), then exit
  (skip waypoint drag begin)

**Modify `glbMapKeyDown`**:
- ESC while `FAddingCoursepoint` → set `FAddingCoursepoint := False`, restore cursor

**Add `AppendCoursepoint(X, Y: Integer)` procedure** (private on TfrmMain or standalone):
1. Look up selected group: find `gSelectedGroupUUID` in `FGroupMap` → `PGroupMapEntry` → `GroupPtr`
2. Convert click to slivers: `DeviceXYToLL(X, Y, ptLL)` → `HCDegSliverLonF/LatF(GlobeUnitsToDecimal(...))`
3. Allocate: `pNew := PHCPathRec(NewPtr(SizeOf(THCPathRec)))`
4. Initialize: `pNew^.DestLon := ...; pNew^.DestLat := ...; pNew^.next := nil; pNew^.pathEvents := nil;`
5. Link into list:
   - If `GroupPtr^.track = nil` → `GroupPtr^.track := pNew; pNew^.prev := nil;` (first waypoint)
   - Else walk to last node → `last^.next := pNew; pNew^.prev := last;`
6. `glbMap.Invalidate` — the next `GameTime` tick rebuilds the track visualization

### 2. `MapWindow.lpr` — uncomment callback capture

**Replace the `{ }` comment block (lines 91-141)** with clean callback capture code.
Remove `memDLLInit` references, remove `uLazGUIGlobals` references, keep the callback
assignment loop:

```pascal
  // Capture HC callback function pointers
  funcTmp := funcRefs;
  while (funcTmp <> nil) do
  begin
    if (funcTmp^.funcName = 'NewPtr') then
      frmMain.NewPtr := THCNewPtr(funcTmp^.funcPtr)
    else if (funcTmp^.funcName = 'DispPtr') then
      frmMain.DisposPtr := THCDisposPtr(funcTmp^.funcPtr)
    else if (funcTmp^.funcName = 'QueueHarpoonEvent') then
      frmMain.HCQueueHarpoonEvent := THCQueueHarpoonEvent(funcTmp^.funcPtr)
    else if (funcTmp^.funcName = 'dllCheckLatLonDepth') then
      frmMain.HCdllCheckLatLonDepth := THCdllCheckLatLonDepth(funcTmp^.funcPtr)
    else if (funcTmp^.funcName = 'MessageLogLine') then
      frmMain.HCMessageLogLine := THCMessageLogLine(funcTmp^.funcPtr);
    funcTmp := funcTmp^.Next;
  end;
```

Note: `NewPtr` is assigned as `THCNewPtr(funcTmp^.funcPtr)` (typed cast, not raw Pointer) so
the field type matches. Drop `annex_id_to_disk_id` and `disk_id_to_annex_id` — not needed.

### 3. `MapWindow\MapWindow.txt` — history entry

Add a new entry at the top of the History section following the existing format:

```
MapWindow 20260314 2025.025+
  Add Coursepoint — right-click a selected group to open a context menu
   with "Add Coursepoint". Click on the map to append waypoints to the
   group's course. Stay in placement mode to add multiple points; ESC
   or right-click to exit. Works for groups with or without existing
   course waypoints. Uncommented DLLInit callback capture (NewPtr, etc.)
   to allocate PHCPathRec via HC's memory manager.
```

---

## Files Modified

| File | Change |
|------|--------|
| `mainform.pas` | Add callback fields, `FAddingCoursepoint` state, `miAddCoursepointClick`, `AppendCoursepoint`, modify `glbMapMouseDown`/`glbMapKeyDown` |
| `mainform.lfm` | Add `pmGlobe: TPopupMenu` with `miAddCoursepoint: TMenuItem` |
| `MapWindow.lpr` | Replace commented-out DLLInit block with clean callback capture loop |
| `MapWindow\MapWindow.txt` | Add history entry for new Add Coursepoint feature |

---

## Key Data Structures

**THCPathRec** (uHCTypeDefs.pas:242-252) — doubly-linked list node:
```
next, prev: Pointer;       // FPC builds use untyped Pointer
DestLon, DestLat: LongInt; // HC slivers
pathEvents: Pointer;       // nil for new waypoints
```

**Coordinate conversion chain** (click → HC slivers):
```
DeviceXYToLL(X, Y, ptLL)                           // screen → TPointLL
GlobeUnitsToDecimal(ptLL.iLongX) → decimal degrees  // TPointLL → degrees
HCDegSliverLonF(degrees) → LongInt slivers          // degrees → HC slivers
```

**Memory**: must use `NewPtr(SizeOf(THCPathRec))` — HC owns the memory and will free it.

---

## Edge Cases

- **No group selected**: right-click does nothing (don't show menu)
- **NewPtr is nil**: guard `AppendCoursepoint` — if `NewPtr` is not assigned, exit silently
  (older HC versions might not pass it)
- **Group has no existing course** (`track = nil`): set `track := pNew` directly
- **Right-click while in placement mode**: exit placement mode (same as ESC)

---

## Verification

1. `lazbuild --build-all MapWindow.lpi`
2. Launch HC, load a scenario
3. Select a group by clicking its icon
4. Right-click → verify "Add Coursepoint" appears in context menu
5. Click "Add Coursepoint" → cursor changes to crosshair
6. Click on the map → verify a new waypoint dot appears and the course line extends
7. Click again → verify a second waypoint is appended (stays in placement mode)
8. Press ESC → verify cursor reverts and clicking no longer adds waypoints
9. Test with a group that has NO existing course — verify first waypoint is created
10. Test right-click when no group selected — verify no menu appears
11. Enable course display mode and verify the new waypoints render with great circle arcs
12. Drag one of the new waypoints — verify existing drag still works
