file_document.h
A document that lives in a file: open, save, save as, and the questions that go around them.
#include <sonore/file_document.h>- 1type
- Guides: Testing your plugin
On this page
Design notes
What it is for
Every application that edits a file has the same dozen paths, and the bugs are in the paths nobody tries: closing a changed document and pressing Cancel; Save on a document that was never saved; Save As over a file that exists; a save that fails half way and must leave the document pointing at the file it CAME from, still marked changed. This class owns those paths. A subclass supplies four things -- the title, how to load, how to save, and (optionally) where the last document was -- exactly as the reference asks.
A PLUGIN does not use this: its state is saved into the host's session and its dirty flag reported to the host (clap_host_state::mark_dirty), which is the format's job. This is for the standalone and for applications.
The questions are hooks
"Save changes?", "Overwrite?", the open and save choosers and the failure message are virtual functions that answer through a callback. The defaults are the native ones -- a message box (MessageBoxW, NSAlert, a GTK dialog), and FileDialog for the choosers -- which answer before they return. An application whose questions are drawn inside its own window overrides them; gfx/alert.h's AlertOverlay is exactly that shape:
void askToSaveChanges(std::function<void(SaveChoice)> reply) override {
alert.show("Closing document...", "Save the changes to \"" +
getDocumentTitle() + "\"?", {"Save", "Discard", "Cancel"},
[reply](int i) { reply(i == 0 ? SaveChoice::save
: i == 1 ? SaveChoice::discard
: SaveChoice::cancel); });
}
Every operation has an Async form that finishes when the hooks have answered, however late. The plain forms return the result, and are for hooks that answer at once: if a hook has NOT answered by the time it returns, the plain form reports userCancelledSave (or a failed Result) and the late answer is then ignored -- a save the caller was told did not happen must not happen behind its back. A document deleted before a hook answers is never called back either.
Recent files
setLastDocumentOpened() is called after every successful load and save, and getLastDocumentOpened() is where the choosers start. By default they keep the last path and, if one is attached, feed a RecentlyOpenedFilesList (recent_files.h) -- which is the hook an "Open Recent" menu needs.
Types 1
class FileBasedDocument : public ChangeBroadcaster
A document that lives in one file, with every open, save and save-as path and the questions around them. Mirrors the reference's class of the same name; the file comment covers the hooks, the Async forms and recent files.
A subclass implements getDocumentTitle, loadDocument and saveDocument, and may override the question hooks and the last-document pair; it calls changed() on every edit. Changes reach listeners through the ChangeBroadcaster base, which its owner dispatches. MAIN THREAD: the default hooks are native modal dialogs. For the standalone and for applications, never a plugin, whose state belongs to the host.