summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2019-05-14 21:50:36 +0100
committerCaolán McNamara <caolanm@redhat.com>2019-05-17 10:59:44 +0200
commit644ca26af744aec1e66c8dd4199d1228e0f780be (patch)
tree2a364e77b595a65b9f29ce076b5b9352c9ccdbea
parentdc17f247daeb281dda531ff335873d563db5b653 (diff)
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>
-rw-r--r--vcl/source/window/dialog.cxx18
-rw-r--r--vcl/unx/gtk3/gtk3gtkinst.cxx141
2 files changed, 114 insertions, 45 deletions
diff --git a/vcl/source/window/dialog.cxx b/vcl/source/window/dialog.cxx
index 21957995644e..5348e206f2c0 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 dac1bccae151..0d60969bff8d 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