8.4 KiB
Editing commands and undo (Phase 27 audit)
The control thread owns project edits and history. Audio plans are immutable snapshots; widgets never pass editable project pointers into the callback. Graph commands and batch note commands use the existing public C command APIs. The GUI groups a completed gesture or accepted dialog as one history step.
Operation audit
| Operation | History / grouping | Engine and external effects |
|---|---|---|
| Track create/delete/name/order; clip create/delete/move/trim/name | One accepted command/drag | Stops playback before mutation; track removal cascades clips/notes/lanes, never files |
| MIDI clip creation and default source routing | One command, including automatic route | Core candidate graph is validated before model adoption |
| Audio duplicate/split/gain/fades | One command including copied metadata/notes | Source WAV is shared; split does not rewrite samples |
| Note add/delete/drag/resize, filtered quantize/transpose/properties/repeat | One gesture/batch, including fresh note IDs | Stops playback; validates all matched notes/capacity before applying |
| Graph add/remove/connect/disconnect/insert gain | One graph command | Candidate and history storage prepared before engine submission; active graph remains immutable |
| Mixer gain/pan/mute/solo, bus create/route, automation points | One released slider/command/point | Stops playback; bus deletion reroutes members without deleting their media |
| Plugin add/replace/control/reset/automation/insert | One accepted command/dialog including wiring | Candidate compiles before installation; old module instances retire on control thread |
| MIDI controller bindings | One channel-map command | Rebuilds immutable MIDI plan; no callback mutation |
| Import/relink audio | One accepted edit, including newly needed track/routes | Import candidate prepared before adoption; source media never copied/deleted by undo |
| Recording start/stop | Start has no document history step; completed take adds one | Undo removes take reference, never finalized WAV; failed add retains saved take for manual import |
| Save / Save As | No history step; retains existing history | Writes document; undo does not restore earlier on-disk file versions |
| New / Open | Fresh baseline after unsaved-change handling | Parse/prepare failure retains old document/history; engine may already be stopped/reset after preparation failure |
| Transport, panic, external connections/reconnect | Not document history | Undo does not reverse playback, live voices, device connections or capture side effects |
| Selection, tabs, snap/grid, zoom/scroll, node layout, meters/reset peaks | Session UI state, no history | No music or media changes |
No arbitrary cross-command grouping or automation recording mode is introduced. An accepted no-op model edit can still consume a step. History holds 32 edits plus a baseline; new accepted edits discard redo, and eviction drops the oldest baseline. Monotonic ID high-water marks survive undo and branches. History is session-only; restart/New/Open discards it.
History ownership and failure boundary
xtc_history_reserve allocates one empty fixed-capacity project/graph snapshot
before an edit mutates the document or a candidate is submitted to the engine.
Failure aborts the command without changing history or dropping redo. The call
is idempotent: storage left by a rejected command is reused. It neither records
an edit nor moves the cursor. GUI arrangement/note/mixer guards reserve before
stopping playback; candidate graph/plugin/map/take paths reserve before
publication. File/media work and compilation can still fail before adoption.
The next xtc_history_record copies model arrays and the bounded 64-node/4096-
edge graph into reserved storage without allocating. Successful record takes
ownership of that storage, remembers IDs, then releases old redo/evicted states.
The snapshot contains metadata/graph configuration only, never sample buffers,
DSP instances, module handles or GUI pointers. An unused reservation is freed
with history. This adds at most one pending metadata snapshot to the 33 retained
states. C clients that call record without reserving retain the original
clone-and-report-allocation-failure behavior; they must handle that result.
All history work is serialized on the control thread, never the audio callback.
Undo/redo checks out an owned candidate without moving the cursor, stops/resets transport and prepares its assets/plans, then accepts the adjacent history step only after installation succeeds. Failed checkout/preparation retains the cursor/document, so retry is possible. Preparation failure may leave transport stopped at zero; it does not resume playback automatically. Snapshot restoration marks the document dirty conservatively, even if it matches a saved file.
Selection, focus and menus
Ctrl+Z undoes; Ctrl+Shift+Z and Ctrl+Y redo. The main Edit menu disables unavailable steps and disables history while recording. Piano roll also has Edit/Undo/Redo shortcuts. Undo preserves currently selected track/clip, mixer strip, graph node and piano-roll clip/note when their stable IDs still exist (notes must remain visible under the current filter). Clip selection follows its restored track. A removed selection clears, with track fallback for a removed clip. A visible piano roll stays open when its clip survives; missing clips hide it. Historical selections/layouts are not saved in snapshots; undo does not resurrect a selection already cleared by a deletion. Open/New retain their reset behavior.
Editor-local shortcuts act on focused editors. Text inputs retain Space and text undo/editing keys; numeric mixer inputs do not invoke fader shortcuts. Timeline Ctrl+D duplicates, F2 renames, Alt+Up/Down reorders tracks, S splits audio at the cursor, A opens audio properties and Delete removes the selection. Graph Left/Right chooses a node, L arranges by flow, I inserts unity gain in the selected wire, Delete removes the selected node/wire and Escape cancels gestures. Piano-roll commands are listed in its Notes menu. Escape cancels gestures; ordinary deletion is reversible and does not add a confirmation dialog.
Graph right-click now selects the node/wire under the pointer before showing Inspect/Arrange/Insert/Delete; empty space clears a stale selection. Piano-roll right-click selects the clicked note and offers Properties/Quantize/Repeat/Delete. It no longer immediately deletes the note. Timeline context menus retain their selection and duplicate/rename/delete/audio commands. Dismissing a context menu does not edit the document. Context menus and widgets remain C++/FLTK UI code.
Open/import/plugin load/replacement paint a busy cursor and operation status before synchronous work, flushing drawing without dispatching new input or reentering commands. This is feedback, not background loading or cancellable progress. Failure messages distinguish capacity, memory, target/value, format/ optional-support and audio errors. Unsaved New/Open/Quit and Save As overwrite keep their existing confirmations; undoable deletes and external-media reference edits do not request extra approval. No operation here deletes source media.
Evidence and manual procedure
make all test gui test-gui-plugins test-reliability analyze test-workflow test-gui-jack covers core commands, grouped gestures/batches, backend rollback,
selection retention, take undo/redo and real software JACK state restoration.
The GUI fixture injects history-reservation failure before graph/map/import
publication and verifies unchanged document, submission count and redo. The
allocation-interposed reliability suite fails each reservation allocation, then
fails all allocations while capturing a reserved edit; redo branching and
snapshot restoration still succeed. ASan/UBSan cover the same ownership paths.
For native manual inspection: right-click different graph nodes/wires and empty space, dismiss the menu, and confirm only selection changes. Right-click a note, dismiss, then explicitly Delete and undo; confirm the note returns. Type spaces and use text undo in a name/input dialog, and edit numeric mixer fields without triggering fader keys. Record a short take, stop, undo once and confirm its WAV still exists; redo restores the reference. Watch operation feedback on a large import/Open. These manual popup/focus/large-file and physical-device listening checks remain pending; automation does not claim broad accessibility or human usability qualification. Existing native-plugin failure limits still apply.