Haiku Recent Items Lists: Correct File History in Native Menus
Build Haiku recent-file menus with BRecentFilesList, ref-based messages, filtering, bounded history, and safe handling of renamed or removed files.
Haiku’s recent-file helpers connect a familiar application feature to the operating system’s file-reference model. BRecentFilesList can build a menu of recently opened files, or expose the references one at a time so an application can integrate them into an existing menu. The helper saves application code from maintaining a parallel list of path strings, but it does not replace the application’s open workflow, file validation, or message handling.
The distinction matters in a desktop where a file can be renamed, moved, unmounted, or deleted after it was opened. A path string is a snapshot of a name. An entry_ref is a structured reference used by the Storage Kit and Tracker-aware applications. Neither makes a file immortal: consumers still need to handle a reference that no longer resolves. Recent history is a convenience index, not a transaction log or durable backup.
Select the list that matches the user action
The public Tracker header defines the common BRecentItemsList abstraction and specialized lists for files, folders, and applications. BRecentFilesList accepts a maximum item count, a flag controlling Tracker-style navigation menus for folders, and optional MIME type and application-signature filters. It also has a constructor accepting a list of MIME types. BRecentFoldersList and BRecentAppsList express different user intents; using the file list for all three creates a menu whose labels and activation semantics are confusing.
The static NewFileListMenu() helpers return a BMenu built from the recent items. Callers may supply file-open and folder-open messages, a target handler, a maximum number of items, navigation behavior, and filters. A convenience menu is useful when the application wants the standard interaction. If the application needs custom grouping, a preview column, or a different visual hierarchy, GetNextMenuItem() and GetNextRef() allow it to consume the list incrementally.
The maximum item count is a presentation and resource bound, not a promise that this exact number of usable files will appear. Entries may be filtered or become stale. Do not pad the menu with fabricated labels when fewer valid references are available. An empty or short recent list is a normal state for a new installation, a new user, a filtered document type, or an application that has not opened many files.
Route references through the normal open path
The menu items are messages, not direct calls into the document loader. The recent-list API documents that, when the caller supplies an open message, the corresponding entry_ref is attached under the refs field. Without one, the helper supplies a default B_REFS_RECEIVED message. This aligns with Haiku’s broader ref-received application workflow: the same open handler can receive a file from Tracker, a file panel, command-line startup, or a recent menu.
Keep one authoritative handler for opening a document. It should validate the incoming message, iterate every refs value the application supports, resolve the reference, report a useful error, and leave the current document intact if opening fails. Avoid a separate recent-menu callback that bypasses document validation or silently assumes the reference names an existing file.
An illustrative menu setup is:
BMessage openMessage(B_REFS_RECEIVED);
BMenu* recentMenu = BRecentFilesList::NewFileListMenu(
"Open Recent", &openMessage, NULL, this, 10, false, NULL,
"application/x-vnd.example-editor");
if (recentMenu != NULL)
fileMenu->AddItem(recentMenu);
The example assumes this is a live BHandler, fileMenu is the owning menu, and the application’s MessageReceived() implementation handles B_REFS_RECEIVED. It uses the single-MIME-type overload. For multiple document MIME types, use the overload that takes a MIME type array and its count. Confirm constructor and overload availability against the target Haiku headers when supporting an older ABI; do not copy a sample signature blindly across BeOS-era code.
The handler should check FindRef("refs", index, &ref) and the returned status_t rather than assuming the field exists. If the app accepts one document per message, it can deliberately open only the first reference and explain that behavior. If it supports multiple files, process each independently and avoid discarding later refs because the first one failed.
MIME and signature filters are useful but not authorization
The ofType filter narrows the recent list to a MIME type. An array of types permits a deliberate group of document formats. The openedByAppSig filter scopes history to an application signature. These options make menus more relevant, particularly when several applications share MIME support, but they are not access-control checks and must not be treated as proof that a file is safe or still in the same format.
MIME metadata can be missing, stale, or changed by a user. The open path still needs to inspect the actual file, handle parse errors, enforce size limits, and avoid trusting extensions or menu membership. If an application supports a set of types, document that set in the UI and in its file-handling implementation. Do not list a broad type merely to make the recent menu appear populated.
Use a stable application signature. Changing it between releases can make history appear to vanish because the filter no longer matches the previous identifier. Conversely, reusing another application’s signature risks mixing histories and confusing document ownership. Treat the signature as part of the application’s identity, not as an arbitrary label.
Custom menus require explicit iteration discipline
BRecentItemsList supports Rewind(), GetNextMenuItem(), and GetNextRef(). Iteration is stateful: after a pass, call Rewind() before a second pass. Check for the documented end condition and do not assume the list can be traversed concurrently by multiple menu builders using the same object. If each menu needs independent iteration, construct separate list instances or finish one pass before starting another.
When asking for a BMenuItem, provide the intended messages and handler target explicitly. A menu item should lead to an application-owned path with a clear result. If the app uses a custom message code, attach the reference using the API-supported route and confirm that the receiver can retrieve it. Avoid serializing native pointers or passing a BEntry* through an untyped integer field.
If the custom UI needs to display an additional state such as “offline volume” or “missing file,” resolve each reference and represent failure visibly. Do not remove a user’s history merely because the item is temporarily unavailable. On network or removable storage, the volume may return later. If the application offers a “clear recent items” command, clearly distinguish it from deleting the files themselves.
Lifetime and menu ownership
NewFileListMenu() returns a menu pointer. In the standard pattern, adding it as a submenu transfers it into the owning menu hierarchy. Follow the Interface Kit ownership contract for the exact API calls used; do not delete an object after transferring ownership. When assembling individual items, ensure they are attached to a live menu and have an appropriate target. A recent menu that outlives the window or handler it targets can deliver messages into an object that is no longer valid if the application has violated normal handler lifetime rules.
Build or refresh the menu on the UI thread at a point consistent with the application’s menu lifecycle. If rebuilding dynamically, first detach and dispose of the old submenu according to menu ownership, then insert the replacement. Avoid creating a new menu on every mouse move or drawing callback. Menu construction is UI work, not a polling strategy for file-system changes.
Validation and acceptance criteria
Test a new profile with no history, a list with one item, a full list, MIME-filtered lists, multiple supported MIME types, and two application signatures. Open a document from Tracker, then verify that the recent entry routes through the same handler and selects the same document path. Rename, move, delete, and unmount an item after adding it to history; the menu should fail safely and preserve the rest of the list.
Test all refs message cases the application claims to support: missing field, zero references, one reference, several references, and an invalid reference. Confirm the app reports a useful error rather than crashing or replacing a valid current document with an empty window. Verify target and menu ownership under repeated menu rebuilds, close/reopen cycles, and shutdown. Run with a MIME type that is absent from the history and confirm the empty-state behavior is clear.
The acceptance boundary is simple: a recent menu must offer only real references returned by the system helper, dispatch them through the normal ref-open handler, and remain safe when history is empty or stale. The helper is valuable precisely because it integrates with the platform’s file identity conventions; duplicating that behavior in an ad hoc string list creates more edge cases than it removes.
Related:
- Haiku’s BRoster and registrar: How Applications Are Found and Launched
- Haiku BFilePanel: Asynchronous Open and Save Dialogs
Sources: