allow closing welded dialogs to be cancelled
from both directions of window manager close button and esc
Change-Id: I3c7bd6f67e3100f5dd3ab04456ad9aa5100c472c
Reviewed-on: https://gerrit.libreoffice.org/72319
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Tested-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/vcl/source/window/dialog.cxx b/vcl/source/window/dialog.cxx
index 2195799..5348e20 100644
--- a/vcl/source/window/dialog.cxx
+++ b/vcl/source/window/dialog.cxx
@@ -835,8 +835,26 @@ bool Dialog::Close()
if ( mpWindowImpl->mxWindowPeer.is() && IsCreatedWithToolkit() && !IsInExecute() )
return false;
// If there's a cancel button with a custom handler, then always give it a chance to
// handle Dialog::Close
PushButton* pCustomCancelButton;
PushButton* pCancelButton = dynamic_cast<PushButton*>(get_widget_for_response(RET_CANCEL));
if (!mbInClose && pCancelButton && pCancelButton->GetClickHdl().IsSet())
pCustomCancelButton = pCancelButton;
else
pCustomCancelButton = nullptr;
mbInClose = true;
if (pCustomCancelButton)
{
pCustomCancelButton->Click();
if (xWindow->IsDisposed())
return true;
mbInClose = false;
return false;
}
if ( !(GetStyle() & WB_CLOSEABLE) )
{
bool bRet = true;
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index dac1bcc..0d60969 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -2995,6 +2995,8 @@ namespace
}
}
class GtkInstanceButton;
class GtkInstanceDialog : public GtkInstanceWindow, public virtual weld::Dialog
{
private:
@@ -3006,6 +3008,7 @@ private:
std::function<void(sal_Int32)> m_aFunc;
gulong m_nCloseSignalId;
gulong m_nResponseSignalId;
gulong m_nSignalDeleteId;
// for calc ref dialog that shrink to range selection widgets and resize back
GtkWidget* m_pRefEdit;
@@ -3014,10 +3017,12 @@ private:
int m_nOldEditWidthReq; // Original width request of the input field
int m_nOldBorderWidth; // border width for expanded dialog
void signal_close();
static void signalClose(GtkWidget*, gpointer widget)
{
GtkInstanceDialog* pThis = static_cast<GtkInstanceDialog*>(widget);
pThis->response(RET_CANCEL);
pThis->signal_close();
}
static void signalAsyncResponse(GtkWidget*, gint ret, gpointer widget)
@@ -3026,6 +3031,11 @@ private:
pThis->asyncresponse(ret);
}
static gboolean signalAsyncDelete(GtkDialog*, GdkEventAny*, gpointer)
{
return true; /* Do not destroy */
}
static int GtkToVcl(int ret)
{
if (ret == GTK_RESPONSE_OK)
@@ -3043,24 +3053,7 @@ private:
return ret;
}
void asyncresponse(gint ret)
{
if (ret == GTK_RESPONSE_HELP)
{
help();
return;
}
else if (has_click_handler(ret))
return;
hide();
m_aFunc(GtkToVcl(ret));
m_aFunc = nullptr;
// move the self pointer, otherwise it might be de-allocated by time we try to reset it
std::shared_ptr<GtkInstanceDialog> me = std::move(m_xRunAsyncSelf);
m_xDialogController.reset();
me.reset();
}
void asyncresponse(gint ret);
public:
GtkInstanceDialog(GtkDialog* pDialog, GtkInstanceBuilder* pBuilder, bool bTakeOwnership)
@@ -3069,6 +3062,7 @@ public:
, m_aDialogRun(pDialog)
, m_nCloseSignalId(g_signal_connect(m_pDialog, "close", G_CALLBACK(signalClose), this))
, m_nResponseSignalId(0)
, m_nSignalDeleteId(0)
, m_pRefEdit(nullptr)
, m_nOldEditWidth(0)
, m_nOldEditWidthReq(0)
@@ -3086,6 +3080,7 @@ public:
show();
m_nResponseSignalId = g_signal_connect(m_pDialog, "response", G_CALLBACK(signalAsyncResponse), this);
m_nSignalDeleteId = g_signal_connect(m_pDialog, "delete-event", G_CALLBACK(signalAsyncDelete), this);
return true;
}
@@ -3100,32 +3095,14 @@ public:
show();
m_nResponseSignalId = g_signal_connect(m_pDialog, "response", G_CALLBACK(signalAsyncResponse), this);
m_nSignalDeleteId = g_signal_connect(m_pDialog, "delete-event", G_CALLBACK(signalAsyncDelete), this);
return true;
}
bool has_click_handler(int nResponse);
GtkInstanceButton* has_click_handler(int nResponse);
virtual int run() override
{
sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(m_pDialog)));
int ret;
while (true)
{
ret = m_aDialogRun.run();
if (ret == GTK_RESPONSE_HELP)
{
help();
continue;
}
else if (has_click_handler(ret))
continue;
break;
}
hide();
return GtkToVcl(ret);
}
virtual int run() override;
virtual void show() override
{
@@ -3295,6 +3272,8 @@ public:
g_signal_handler_disconnect(m_pDialog, m_nCloseSignalId);
if (m_nResponseSignalId)
g_signal_handler_disconnect(m_pDialog, m_nResponseSignalId);
if (m_nSignalDeleteId)
g_signal_handler_disconnect(m_pDialog, m_nSignalDeleteId);
}
};
@@ -4610,6 +4589,60 @@ public:
}
};
void GtkInstanceDialog::asyncresponse(gint ret)
{
if (ret == GTK_RESPONSE_HELP)
{
help();
return;
}
GtkInstanceButton* pClickHandler = has_click_handler(ret);
if (pClickHandler)
{
// make GTK_RESPONSE_DELETE_EVENT act as if cancel button was pressed
if (ret == GTK_RESPONSE_DELETE_EVENT)
pClickHandler->clicked();
return;
}
hide();
m_aFunc(GtkToVcl(ret));
m_aFunc = nullptr;
// move the self pointer, otherwise it might be de-allocated by time we try to reset it
std::shared_ptr<GtkInstanceDialog> me = std::move(m_xRunAsyncSelf);
m_xDialogController.reset();
me.reset();
}
int GtkInstanceDialog::run()
{
sort_native_button_order(GTK_BOX(gtk_dialog_get_action_area(m_pDialog)));
int ret;
while (true)
{
ret = m_aDialogRun.run();
if (ret == GTK_RESPONSE_HELP)
{
help();
continue;
}
GtkInstanceButton* pClickHandler = has_click_handler(ret);
if (pClickHandler)
{
// make GTK_RESPONSE_DELETE_EVENT act as if cancel button was pressed
if (ret == GTK_RESPONSE_DELETE_EVENT)
pClickHandler->clicked();
continue;
}
break;
}
hide();
return GtkToVcl(ret);
}
weld::Button* GtkInstanceDialog::get_widget_for_response(int nResponse)
{
GtkButton* pButton = GTK_BUTTON(gtk_dialog_get_widget_for_response(m_pDialog, VclToGtk(nResponse)));
@@ -4631,15 +4664,33 @@ void GtkInstanceDialog::response(int nResponse)
gtk_dialog_response(m_pDialog, VclToGtk(nResponse));
}
bool GtkInstanceDialog::has_click_handler(int nResponse)
void GtkInstanceDialog::signal_close()
{
if (GtkWidget* pWidget = gtk_dialog_get_widget_for_response(m_pDialog, VclToGtk(nResponse)))
GtkInstanceButton* pClickHandler = has_click_handler(GTK_RESPONSE_CANCEL);
if (pClickHandler)
{
g_signal_stop_emission_by_name(m_pDialog, "close");
// make esc act as if cancel button was pressed
pClickHandler->clicked();
return;
}
response(RET_CANCEL);
}
GtkInstanceButton* GtkInstanceDialog::has_click_handler(int nResponse)
{
GtkInstanceButton* pButton = nullptr;
// e.g. map GTK_RESPONSE_DELETE_EVENT to GTK_RESPONSE_CANCEL
nResponse = VclToGtk(GtkToVcl(nResponse));
if (GtkWidget* pWidget = gtk_dialog_get_widget_for_response(m_pDialog, nResponse))
{
void* pData = g_object_get_data(G_OBJECT(pWidget), "g-lo-GtkInstanceButton");
GtkInstanceButton* pButton = static_cast<GtkInstanceButton*>(pData);
return pButton && pButton->has_click_handler();
pButton = static_cast<GtkInstanceButton*>(pData);
if (pButton && !pButton->has_click_handler())
pButton = nullptr;
}
return false;
return pButton;
}
class GtkInstanceToggleButton : public GtkInstanceButton, public virtual weld::ToggleButton