Improve behaviour of "force closing" wxDocuments

When the document was forced to close, OnSaveModified() was still called
and allowed the user to cancel closing the document -- but it was still
closed after OnSaveModified() returned.

Be more upfront about it and tell the user that the document will be
closed anyhow, but still propose them to save it if necessary.

An alternative solution might be to just deprecate "force closing"
entirely, as this seems very user-unfriendly.
This commit is contained in:
Vadim Zeitlin 2022-07-15 00:36:45 +01:00
parent ee6d58abe2
commit dcee1cd025
5 changed files with 85 additions and 7 deletions

View file

@ -117,6 +117,10 @@ public:
// modified to false)
virtual bool OnSaveModified();
// Similar to OnSaveModified() but doesn't allow the user to prevent the
// document from closing as it will be closed unconditionally.
virtual void OnSaveBeforeForceClose();
// if you override, remember to call the default
// implementation (wxDocument::OnChangeFilename)
virtual void OnChangeFilename(bool notifyViews);

View file

@ -1544,6 +1544,19 @@ public:
*/
virtual bool OnSaveModified();
/**
This function is called when a document is forced to close.
The default implementation asks the user whether to save the changes
but, unlike OnSaveModified(), does not allow to cancel closing.
The document is force closed when wxDocManager::CloseDocument() is
called with its @c force argument set to @true.
@since 3.3.0
*/
virtual void OnSaveBeforeForceClose();
/**
Removes the view from the document's list of views.

View file

@ -75,6 +75,7 @@ wxIMPLEMENT_APP(MyApp);
wxBEGIN_EVENT_TABLE(MyApp, wxApp)
EVT_MENU(wxID_ABOUT, MyApp::OnAbout)
EVT_MENU(wxID_CLEAR, MyApp::OnForceCloseAll)
wxEND_EVENT_TABLE()
MyApp::MyApp()
@ -315,6 +316,7 @@ void MyApp::AppendDocumentFileCommands(wxMenu *menu, bool supportsPrinting)
menu->Append(wxID_SAVE);
menu->Append(wxID_SAVEAS);
menu->Append(wxID_REVERT, _("Re&vert..."));
menu->Append(wxID_CLEAR, "&Force close all");
if ( supportsPrinting )
{
@ -437,6 +439,13 @@ wxFrame *MyApp::CreateChildFrame(wxView *view, bool isCanvas)
return subframe;
}
void MyApp::OnForceCloseAll(wxCommandEvent& WXUNUSED(event))
{
// Pass "true" here to force closing just for testing this functionality,
// there is no real reason to force the issue here.
wxDocManager::GetDocumentManager()->CloseDocuments(true);
}
void MyApp::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxString modeName;

View file

@ -71,6 +71,9 @@ private:
void CreateMenuBarForFrame(wxFrame *frame, wxMenu *file, wxMenu *edit);
// force close all windows
void OnForceCloseAll(wxCommandEvent& event);
// show the about box: as we can have different frames it's more
// convenient, even if somewhat less usual, to handle this in the
// application object itself

View file

@ -554,6 +554,50 @@ bool wxDocument::OnSaveModified()
return true;
}
void wxDocument::OnSaveBeforeForceClose()
{
if ( !IsModified() )
return;
wxMessageDialog dialogSave
(
GetDocumentWindow(),
wxString::Format
(
_("Do you want to save changes to %s before closing it?"),
GetUserReadableName()
),
wxTheApp->GetAppDisplayName(),
wxYES_NO | wxICON_QUESTION | wxCENTRE
);
dialogSave.SetExtendedMessage(_("The document must be closed."));
dialogSave.SetYesNoLabels(_("&Save"), _("&Discard changes"));
if ( dialogSave.ShowModal() == wxID_YES )
{
while ( !Save() )
{
wxMessageDialog dialogRetry
(
GetDocumentWindow(),
wxString::Format
(
_("Saving %s failed, would you like to retry?"),
GetUserReadableName()
),
wxTheApp->GetAppDisplayName(),
wxYES_NO | wxICON_ERROR | wxCENTRE
);
dialogRetry.SetYesNoLabels(_("Retry"), _("Discard changes"));
if ( dialogRetry.ShowModal() != wxID_YES )
break;
}
}
Modify(false);
}
bool wxDocument::Draw(wxDC& WXUNUSED(context))
{
return true;
@ -1004,14 +1048,19 @@ wxDocManager::~wxDocManager()
// closes the specified document
bool wxDocManager::CloseDocument(wxDocument* doc, bool force)
{
if ( !doc->CanClose() && !force )
return false;
if ( force )
{
// We need to close, but at least ask the user if the document should
// be saved before doing it.
doc->OnSaveBeforeForceClose();
}
else // Allow the user to cancel closing too.
{
if ( !doc->CanClose() )
return false;
}
// To really force the document to close, we must ensure that it isn't
// modified, otherwise it would ask the user about whether it should be
// destroyed (again, it had been already done by CanClose() above) and might
// not destroy it at all, while we must do it here.
doc->Modify(false);
// Note that by now the document is certain not to be modified any longer.
// Implicitly deletes the document when
// the last view is deleted