summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2021-06-24 15:53:37 +0100
committerCaolán McNamara <caolanm@redhat.com>2021-06-25 16:56:49 +0200
commit0ec9a51609b78c9aa85b01a4902711923324b025 (patch)
tree2f7b9d2f394013afba34cefde41d564a089a9681
parent-Werror,-Wunused-but-set-variable (clang-cl 13 trunk) (diff)
downloadcore-0ec9a51609b78c9aa85b01a4902711923324b025.tar.gz
core-0ec9a51609b78c9aa85b01a4902711923324b025.zip
gtk4 dnd into external apps
It's always hard to bootstrap dnd because so much of it has to work before any of it works. Typically dnd and cnp are strongly related so getting cnp working first is the way to go before trying dnd. Change-Id: I645f56dc5f8cb0e3a31e0ff8bbaab610366b6312 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/117798 Tested-by: Caolán McNamara <caolanm@redhat.com> Reviewed-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r--compilerplugins/clang/reservedid.cxx4
-rw-r--r--vcl/Library_vclplug_gtk4.mk7
-rw-r--r--vcl/inc/unx/gtk/gtkframe.hxx14
-rw-r--r--vcl/inc/unx/gtk/gtkinst.hxx6
-rw-r--r--vcl/unx/gtk3/gtkframe.cxx127
-rw-r--r--vcl/unx/gtk3/gtkinst.cxx147
-rw-r--r--vcl/unx/gtk4/contentprovider.hxx20
-rw-r--r--vcl/unx/gtk4/gtkframe.cxx2
-rw-r--r--vcl/unx/gtk4/gtkinst.cxx2
-rw-r--r--vcl/unx/gtk4/transferableprovider.cxx118
-rw-r--r--vcl/unx/gtk4/transferableprovider.hxx51
11 files changed, 299 insertions, 199 deletions
diff --git a/compilerplugins/clang/reservedid.cxx b/compilerplugins/clang/reservedid.cxx
index 9927e9c50307..eac62a5b9671 100644
--- a/compilerplugins/clang/reservedid.cxx
+++ b/compilerplugins/clang/reservedid.cxx
@@ -197,8 +197,6 @@ bool ReservedId::VisitNamedDecl(NamedDecl const * decl) {
// connectivity/source/inc/ado/Awrapadox.hxx, MS SDK adoctint.h
&& s != "_ADOUser"
// connectivity/source/inc/ado/Awrapadox.hxx, MS SDK adoctint.h
- && s != "_ClipboardContent" // vcl/unx/gtk3/gtkinst.cxx
- && s != "_ClipboardContentClass" // vcl/unx/gtk3/gtkinst.cxx
&& s != "_FcPattern" // vcl/inc/unx/fc_fontoptions.hxx
&& s != "_GdkDisplay"
// vcl/unx/gtk/xid_fullscreen_on_all_monitors.c
@@ -212,6 +210,8 @@ bool ReservedId::VisitNamedDecl(NamedDecl const * decl) {
&& s != "_NotifyingLayout" // vcl/unx/gtk4/notifyinglayout.cxx
&& s != "_SurfacePaintable" // vcl/unx/gtk3/gtkinst.cxx
&& s != "_SurfacePaintableClass" // vcl/unx/gtk3/gtkinst.cxx
+ && s != "_TransferableContent" // vcl/unx/gtk4/transferableprovider.cxx
+ && s != "_TransferableContentClass" // vcl/unx/gtk4/transferableprovider.cxx
&& s != "_XRegion" // vcl/unx/generic/gdi/x11cairotextrender.cxx
&& s != "_XTrap") // vcl/unx/generic/gdi/xrender_peer.hxx
{
diff --git a/vcl/Library_vclplug_gtk4.mk b/vcl/Library_vclplug_gtk4.mk
index 98349413a1d3..8b2d55d9d944 100644
--- a/vcl/Library_vclplug_gtk4.mk
+++ b/vcl/Library_vclplug_gtk4.mk
@@ -92,12 +92,13 @@ $(eval $(call gb_Library_add_exception_objects,vclplug_gtk4,\
vcl/unx/gtk4/salnativewidgets-gtk \
vcl/unx/gtk4/gtkframe \
vcl/unx/gtk4/gtkobject \
- vcl/unx/gtk4/gtksalmenu \
- vcl/unx/gtk4/glomenu \
- vcl/unx/gtk4/gloactiongroup \
+ vcl/unx/gtk4/gtksalmenu \
+ vcl/unx/gtk4/glomenu \
+ vcl/unx/gtk4/gloactiongroup \
vcl/unx/gtk4/hudawareness \
vcl/unx/gtk4/notifyinglayout \
vcl/unx/gtk4/surfacepaintable \
+ vcl/unx/gtk4/transferableprovider \
))
ifeq ($(OS),LINUX)
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index 57005e89a64e..7dcb722968bc 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -61,6 +61,8 @@ class GtkDnDTransferable;
class GtkSalMenu;
+struct VclToGtkHelper;
+
class GtkSalFrame final : public SalFrame
{
struct IMHandler
@@ -264,6 +266,10 @@ class GtkSalFrame final : public SalFrame
static GdkDragAction signalDragMotion(GtkDropTargetAsync *dest, GdkDrop *drop, double x, double y, gpointer frame);
static void signalDragLeave(GtkDropTargetAsync *dest, GdkDrop *drop, gpointer frame);
static gboolean signalDragDrop(GtkDropTargetAsync* context, GdkDrop* drop, double x, double y, gpointer frame);
+
+ static void signalDragFailed(GdkDrag* drag, GdkDragCancelReason reason, gpointer frame);
+ static void signalDragDelete(GdkDrag* drag, gpointer frame);
+ static void signalDragEnd(GdkDrag* drag, gpointer frame);
#else
static gboolean signalDragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
guint time, gpointer frame);
@@ -460,10 +466,10 @@ public:
m_pDragSource = nullptr;
}
-#if !GTK_CHECK_VERSION(4, 0, 0)
- void startDrag(gint nButton, gint nDragOriginX, gint nDragOriginY,
- GdkDragAction sourceActions, GtkTargetList* pTargetList);
-#endif
+ void startDrag(const css::datatransfer::dnd::DragGestureEvent& rEvent,
+ const css::uno::Reference<css::datatransfer::XTransferable>& rTrans,
+ VclToGtkHelper& rConversionHelper,
+ GdkDragAction sourceActions);
void closePopup();
diff --git a/vcl/inc/unx/gtk/gtkinst.hxx b/vcl/inc/unx/gtk/gtkinst.hxx
index 56da7a6885f3..a293662791ae 100644
--- a/vcl/inc/unx/gtk/gtkinst.hxx
+++ b/vcl/inc/unx/gtk/gtkinst.hxx
@@ -191,9 +191,7 @@ class GtkInstDragSource final : public cppu::WeakComponentImplHelper<css::datatr
GtkSalFrame* m_pFrame;
css::uno::Reference<css::datatransfer::dnd::XDragSourceListener> m_xListener;
css::uno::Reference<css::datatransfer::XTransferable> m_xTrans;
-#if !GTK_CHECK_VERSION(4, 0, 0)
VclToGtkHelper m_aConversionHelper;
-#endif
public:
GtkInstDragSource()
: WeakComponentImplHelper(m_aMutex)
@@ -232,7 +230,9 @@ public:
void dragFailed();
void dragDelete();
-#if !GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
+ void dragEnd(GdkDrag* drag);
+#else
void dragEnd(GdkDragContext* context);
void dragDataGet(GtkSelectionData *data, guint info);
#endif
diff --git a/vcl/unx/gtk3/gtkframe.cxx b/vcl/unx/gtk3/gtkframe.cxx
index 02455ed0edb5..2b133e0868e3 100644
--- a/vcl/unx/gtk3/gtkframe.cxx
+++ b/vcl/unx/gtk3/gtkframe.cxx
@@ -4186,7 +4186,6 @@ void GtkSalFrame::signalWindowState(GdkToplevel* pSurface, GParamSpec*, gpointer
namespace
{
-#if !GTK_CHECK_VERSION(4,0,0)
GdkDragAction VclToGdk(sal_Int8 dragOperation)
{
GdkDragAction eRet(static_cast<GdkDragAction>(0));
@@ -4198,7 +4197,6 @@ namespace
eRet = static_cast<GdkDragAction>(eRet | GDK_ACTION_LINK);
return eRet;
}
-#endif
sal_Int8 GdkToVcl(GdkDragAction dragOperation)
{
@@ -5520,58 +5518,63 @@ std::vector<GtkTargetEntry> GtkInstDragSource::FormatsToGtk(const css::uno::Sequ
#endif
void GtkInstDragSource::startDrag(const datatransfer::dnd::DragGestureEvent& rEvent,
- sal_Int8 sourceActions, sal_Int32 /*cursor*/, sal_Int32 /*image*/,
- const css::uno::Reference<css::datatransfer::XTransferable>& rTrans,
- const css::uno::Reference<css::datatransfer::dnd::XDragSourceListener>& rListener)
+ sal_Int8 sourceActions, sal_Int32 /*cursor*/, sal_Int32 /*image*/,
+ const css::uno::Reference<css::datatransfer::XTransferable>& rTrans,
+ const css::uno::Reference<css::datatransfer::dnd::XDragSourceListener>& rListener)
{
-#if !GTK_CHECK_VERSION(4, 0, 0)
set_datatransfer(rTrans, rListener);
if (m_pFrame)
{
- auto aFormats = m_xTrans->getTransferDataFlavors();
- std::vector<GtkTargetEntry> aGtkTargets(FormatsToGtk(aFormats));
- GtkTargetList *pTargetList = gtk_target_list_new(aGtkTargets.data(), aGtkTargets.size());
-
- gint nDragButton = 1; // default to left button
- css::awt::MouseEvent aEvent;
- if (rEvent.Event >>= aEvent)
- {
- if (aEvent.Buttons & css::awt::MouseButton::LEFT )
- nDragButton = 1;
- else if (aEvent.Buttons & css::awt::MouseButton::RIGHT)
- nDragButton = 3;
- else if (aEvent.Buttons & css::awt::MouseButton::MIDDLE)
- nDragButton = 2;
- }
-
setActiveDragSource();
- m_pFrame->startDrag(nDragButton, rEvent.DragOriginX, rEvent.DragOriginY,
- VclToGdk(sourceActions), pTargetList);
-
- gtk_target_list_unref(pTargetList);
- for (auto &a : aGtkTargets)
- g_free(a.target);
+ m_pFrame->startDrag(rEvent, rTrans, m_aConversionHelper ,VclToGdk(sourceActions));
}
else
dragFailed();
-#else
- (void)rEvent;
- (void)sourceActions;
- (void)rTrans;
- (void)rListener;
-#endif
}
-#if !GTK_CHECK_VERSION(4, 0, 0)
-void GtkSalFrame::startDrag(gint nButton, gint nDragOriginX, gint nDragOriginY,
- GdkDragAction sourceActions, GtkTargetList* pTargetList)
+void GtkSalFrame::startDrag(const css::datatransfer::dnd::DragGestureEvent& rEvent,
+ const css::uno::Reference<css::datatransfer::XTransferable>& rTrans,
+ VclToGtkHelper& rConversionHelper,
+ GdkDragAction sourceActions)
{
SolarMutexGuard aGuard;
assert(m_pDragSource);
+#if GTK_CHECK_VERSION(4, 0, 0)
+
+ GdkSeat *pSeat = gdk_display_get_default_seat(getGdkDisplay());
+ GdkDrag* pDrag = gdk_drag_begin(widget_get_surface(getMouseEventWidget()),
+ gdk_seat_get_pointer(pSeat),
+ transerable_content_new(&rConversionHelper, rTrans.get()),
+ sourceActions,
+ rEvent.DragOriginX, rEvent.DragOriginY);
+
+ g_signal_connect(G_OBJECT(pDrag), "drop-performed", G_CALLBACK(signalDragEnd), this);
+ g_signal_connect(G_OBJECT(pDrag), "cancel", G_CALLBACK(signalDragFailed), this);
+ g_signal_connect(G_OBJECT(pDrag), "dnd-finished", G_CALLBACK(signalDragDelete), this);
+
+#else
+
+ auto aFormats = rTrans->getTransferDataFlavors();
+ auto aGtkTargets = rConversionHelper.FormatsToGtk(aFormats);
+
+ GtkTargetList *pTargetList = gtk_target_list_new(aGtkTargets.data(), aGtkTargets.size());
+
+ gint nDragButton = 1; // default to left button
+ css::awt::MouseEvent aEvent;
+ if (rEvent.Event >>= aEvent)
+ {
+ if (aEvent.Buttons & css::awt::MouseButton::LEFT )
+ nDragButton = 1;
+ else if (aEvent.Buttons & css::awt::MouseButton::RIGHT)
+ nDragButton = 3;
+ else if (aEvent.Buttons & css::awt::MouseButton::MIDDLE)
+ nDragButton = 2;
+ }
+
GdkEvent aFakeEvent;
memset(&aFakeEvent, 0, sizeof(GdkEvent));
aFakeEvent.type = GDK_BUTTON_PRESS;
@@ -5580,15 +5583,21 @@ void GtkSalFrame::startDrag(gint nButton, gint nDragOriginX, gint nDragOriginY,
GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay());
aFakeEvent.button.device = gdk_device_manager_get_client_pointer(pDeviceManager);
- GdkDragContext *pContext = gtk_drag_begin_with_coordinates(getMouseEventWidget(),
- pTargetList,
- sourceActions,
- nButton,
- &aFakeEvent,
- nDragOriginX,
- nDragOriginY);
+ GdkDragContext *pDrag = gtk_drag_begin_with_coordinates(getMouseEventWidget(),
+ pTargetList,
+ sourceActions,
+ nDragButton,
+ &aFakeEvent,
+ rEvent.DragOriginX,
+ rEvent.DragOriginY);
+
+ gtk_target_list_unref(pTargetList);
- if (!pContext)
+ for (auto &a : aGtkTargets)
+ g_free(a.target);
+#endif
+
+ if (!pDrag)
m_pDragSource->dragFailed();
}
@@ -5605,13 +5614,18 @@ void GtkInstDragSource::dragFailed()
}
}
+#if GTK_CHECK_VERSION(4, 0, 0)
+void GtkSalFrame::signalDragFailed(GdkDrag* /*drag*/, GdkDragCancelReason /*reason*/, gpointer frame)
+#else
gboolean GtkSalFrame::signalDragFailed(GtkWidget* /*widget*/, GdkDragContext* /*context*/, GtkDragResult /*result*/, gpointer frame)
+#endif
{
GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
- if (!pThis->m_pDragSource)
- return false;
- pThis->m_pDragSource->dragFailed();
+ if (pThis->m_pDragSource)
+ pThis->m_pDragSource->dragFailed();
+#if !GTK_CHECK_VERSION(4, 0, 0)
return false;
+#endif
}
void GtkInstDragSource::dragDelete()
@@ -5627,7 +5641,11 @@ void GtkInstDragSource::dragDelete()
}
}
+#if GTK_CHECK_VERSION(4, 0, 0)
+void GtkSalFrame::signalDragDelete(GdkDrag* /*context*/, gpointer frame)
+#else
void GtkSalFrame::signalDragDelete(GtkWidget* /*widget*/, GdkDragContext* /*context*/, gpointer frame)
+#endif
{
GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
if (!pThis->m_pDragSource)
@@ -5635,12 +5653,20 @@ void GtkSalFrame::signalDragDelete(GtkWidget* /*widget*/, GdkDragContext* /*cont
pThis->m_pDragSource->dragDelete();
}
+#if GTK_CHECK_VERSION(4, 0, 0)
+void GtkInstDragSource::dragEnd(GdkDrag* context)
+#else
void GtkInstDragSource::dragEnd(GdkDragContext* context)
+#endif
{
if (m_xListener.is())
{
datatransfer::dnd::DragSourceDropEvent aEv;
+#if GTK_CHECK_VERSION(4, 0, 0)
+ aEv.DropAction = GdkToVcl(gdk_drag_get_selected_action(context));
+#else
aEv.DropAction = GdkToVcl(gdk_drag_context_get_selected_action(context));
+#endif
// an internal drop can accept the drop but fail with dropComplete( false )
// this is different than the GTK API
if (g_DropSuccessSet)
@@ -5654,7 +5680,11 @@ void GtkInstDragSource::dragEnd(GdkDragContext* context)
g_ActiveDragSource = nullptr;
}
+#if GTK_CHECK_VERSION(4, 0, 0)
+void GtkSalFrame::signalDragEnd(GdkDrag* context, gpointer frame)
+#else
void GtkSalFrame::signalDragEnd(GtkWidget* /*widget*/, GdkDragContext* context, gpointer frame)
+#endif
{
GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
if (!pThis->m_pDragSource)
@@ -5662,6 +5692,7 @@ void GtkSalFrame::signalDragEnd(GtkWidget* /*widget*/, GdkDragContext* context,
pThis->m_pDragSource->dragEnd(context);
}
+#if !GTK_CHECK_VERSION(4, 0, 0)
void GtkInstDragSource::dragDataGet(GtkSelectionData *data, guint info)
{
m_aConversionHelper.setSelectionData(m_xTrans, data, info);
diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index 88e099fe93ec..ebe2eb7e635c 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -869,12 +869,18 @@ class VclGtkClipboard :
std::vector< Reference<css::datatransfer::clipboard::XClipboardListener> > m_aListeners;
#if GTK_CHECK_VERSION(4, 0, 0)
std::vector<OString> m_aGtkTargets;
+ TransferableContent* m_pClipboardContent;
#else
std::vector<GtkTargetEntry> m_aGtkTargets;
#endif
VclToGtkHelper m_aConversionHelper;
DECL_LINK(AsyncSetGtkClipboard, void*, void);
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+ DECL_LINK(DetachClipboard, void*, void);
+#endif
+
public:
explicit VclGtkClipboard(SelectionType eSelection);
@@ -920,16 +926,7 @@ public:
virtual void SAL_CALL removeClipboardListener(
const Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override;
-#if GTK_CHECK_VERSION(4, 0, 0)
- GdkContentFormats* ref_formats();
- void write_mime_type_async(GdkContentProvider* provider,
- const char* mime_type,
- GOutputStream* stream,
- int io_priority,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-#else
+#if !GTK_CHECK_VERSION(4, 0, 0)
void ClipboardGet(GtkSelectionData *selection_data, guint info);
#endif
void OwnerPossiblyChanged(GdkClipboard *clipboard);
@@ -963,29 +960,15 @@ Reference< css::datatransfer::XTransferable > VclGtkClipboard::getContents()
//tdf#93887 This is the system clipboard/selection. We fetch it when we are not
//the owner of the clipboard and have not already fetched it.
m_aContents = new GtkClipboardTransferable(m_eSelection);
+#if GTK_CHECK_VERSION(4, 0, 0)
+ if (m_pClipboardContent)
+ transerable_content_set_transferable(m_pClipboardContent, m_aContents.get());
+#endif
}
return m_aContents;
}
-#if GTK_CHECK_VERSION(4, 0, 0)
-void VclGtkClipboard::write_mime_type_async(GdkContentProvider* provider,
- const char* mime_type,
- GOutputStream* stream,
- int io_priority,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- if (!m_aContents.is())
- return;
- // tdf#129809 take a reference in case m_aContents is replaced during this
- // call
- Reference<datatransfer::XTransferable> xCurrentContents(m_aContents);
- m_aConversionHelper.setSelectionData(xCurrentContents, provider, mime_type,
- stream, io_priority, cancellable,
- callback, user_data);
-}
-#else
+#if !GTK_CHECK_VERSION(4, 0, 0)
void VclGtkClipboard::ClipboardGet(GtkSelectionData *selection_data, guint info)
{
if (!m_aContents.is())
@@ -995,9 +978,7 @@ void VclGtkClipboard::ClipboardGet(GtkSelectionData *selection_data, guint info)
Reference<datatransfer::XTransferable> xCurrentContents(m_aContents);
m_aConversionHelper.setSelectionData(xCurrentContents, selection_data, info);
}
-#endif
-#if !GTK_CHECK_VERSION(4, 0, 0)
namespace
{
const OString& getPID()
@@ -1114,6 +1095,11 @@ void VclGtkClipboard::ClipboardClear()
}
#if GTK_CHECK_VERSION(4, 0, 0)
+IMPL_LINK_NOARG(VclGtkClipboard, DetachClipboard, void*, void)
+{
+ ClipboardClear();
+}
+
OString VclToGtkHelper::makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor)
{
OString aEntry = OUStringToOString(rFlavor.MimeType, RTL_TEXTENCODING_UTF8);
@@ -1318,6 +1304,9 @@ VclGtkClipboard::VclGtkClipboard(SelectionType eSelection)
(m_aMutex)
, m_eSelection(eSelection)
, m_pSetClipboardEvent(nullptr)
+#if GTK_CHECK_VERSION(4, 0, 0)
+ , m_pClipboardContent(nullptr)
+#endif
{
GdkClipboard* clipboard = clipboard_get(m_eSelection);
#if GTK_CHECK_VERSION(4, 0, 0)
@@ -1350,6 +1339,7 @@ VclGtkClipboard::~VclGtkClipboard()
{
#if GTK_CHECK_VERSION(4, 0, 0)
gdk_clipboard_set_content(clipboard, nullptr);
+ m_pClipboardContent = nullptr;
#else
gtk_clipboard_clear(clipboard);
#endif
@@ -1423,97 +1413,13 @@ void VclGtkClipboard::SyncGtkClipboard()
}
}
-#if GTK_CHECK_VERSION(4, 0, 0)
-
-G_BEGIN_DECLS
-
-#define CLIPBOARD_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), clipboard_content_get_type(), ClipboardContent))
-
-struct _ClipboardContent
-{
- GdkContentProvider parent;
- VclGtkClipboard* clipboard;
-};
-
-struct _ClipboardContentClass
-{
- GdkContentProviderClass parent_class;
-};
-
-G_DEFINE_TYPE(ClipboardContent, clipboard_content, GDK_TYPE_CONTENT_PROVIDER)
-
-static void clipboard_content_write_mime_type_async(GdkContentProvider* provider,
- const char* mime_type,
- GOutputStream* stream,
- int io_priority,
- GCancellable* cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- ClipboardContent *content = CLIPBOARD_CONTENT(provider);
- content->clipboard->write_mime_type_async(provider, mime_type, stream, io_priority,
- cancellable, callback, user_data);
-}
-
-static gboolean clipboard_content_write_mime_type_finish(GdkContentProvider*,
- GAsyncResult* result,
- GError** error)
-{
- return g_task_propagate_boolean(G_TASK(result), error);
-}
-
-static GdkContentFormats* clipboard_content_ref_formats(GdkContentProvider *provider)
-{
- ClipboardContent *content = CLIPBOARD_CONTENT(provider);
- return content->clipboard->ref_formats();
-}
-
-static void clipboard_content_detach_clipboard(GdkContentProvider *provider, GdkClipboard*)
-{
- ClipboardContent *content = CLIPBOARD_CONTENT(provider);
- return content->clipboard->ClipboardClear();
-}
-
-static void clipboard_content_class_init(ClipboardContentClass* klass)
-{
- GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS(klass);
-
- provider_class->ref_formats = clipboard_content_ref_formats;
- provider_class->detach_clipboard = clipboard_content_detach_clipboard;
- provider_class->write_mime_type_async = clipboard_content_write_mime_type_async;
- provider_class->write_mime_type_finish = clipboard_content_write_mime_type_finish;
-}
-
-static void clipboard_content_init(ClipboardContent*)
-{
-}
-
-static GdkContentProvider* clipboard_content_new(VclGtkClipboard* pClipboard)
-{
- ClipboardContent *content = CLIPBOARD_CONTENT(g_object_new(clipboard_content_get_type(), nullptr));
- content->clipboard = pClipboard;
- return GDK_CONTENT_PROVIDER(content);
-}
-
-G_END_DECLS
-
-#endif
-
-#if GTK_CHECK_VERSION(4, 0, 0)
-GdkContentFormats* VclGtkClipboard::ref_formats()
-{
- GdkContentFormatsBuilder* pBuilder = gdk_content_formats_builder_new();
- for (const auto& rFormat : m_aGtkTargets)
- gdk_content_formats_builder_add_mime_type(pBuilder, rFormat.getStr());
- return gdk_content_formats_builder_free_to_formats(pBuilder);
-}
-#endif
-
void VclGtkClipboard::SetGtkClipboard()
{
GdkClipboard* clipboard = clipboard_get(m_eSelection);
#if GTK_CHECK_VERSION(4, 0, 0)
- gdk_clipboard_set_content(clipboard, clipboard_content_new(this));
+ m_pClipboardContent = TRANSFERABLE_CONTENT(transerable_content_new(&m_aConversionHelper, m_aContents.get()));
+ transerable_content_set_detach_clipboard_link(m_pClipboardContent, LINK(this, VclGtkClipboard, DetachClipboard));
+ gdk_clipboard_set_content(clipboard, GDK_CONTENT_PROVIDER(m_pClipboardContent));
#else
gtk_clipboard_set_with_data(clipboard, m_aGtkTargets.data(), m_aGtkTargets.size(),
ClipboardGetFunc, ClipboardClearFunc, this);
@@ -1535,6 +1441,10 @@ void VclGtkClipboard::setContents(
Reference< datatransfer::clipboard::XClipboardOwner > xOldOwner( m_aOwner );
Reference< datatransfer::XTransferable > xOldContents( m_aContents );
m_aContents = xTrans;
+#if GTK_CHECK_VERSION(4, 0, 0)
+ if (m_pClipboardContent)
+ transerable_content_set_transferable(m_pClipboardContent, m_aContents.get());
+#endif
m_aOwner = xClipboardOwner;
std::vector< Reference< datatransfer::clipboard::XClipboardListener > > aListeners( m_aListeners );
@@ -1545,6 +1455,7 @@ void VclGtkClipboard::setContents(
{
#if GTK_CHECK_VERSION(4, 0, 0)
gdk_clipboard_set_content(clipboard, nullptr);
+ m_pClipboardContent = nullptr;
#else
gtk_clipboard_clear(clipboard);
#endif
diff --git a/vcl/unx/gtk4/contentprovider.hxx b/vcl/unx/gtk4/contentprovider.hxx
deleted file mode 100644
index ffb7b99f0ffb..000000000000
--- a/vcl/unx/gtk4/contentprovider.hxx
+++ /dev/null
@@ -1,20 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#pragma once
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-G_DECLARE_FINAL_TYPE(ClipboardContent, clipboard_content, CLIPBOARD, CONTENT, GdkContentProvider)
-
-G_END_DECLS
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk4/gtkframe.cxx b/vcl/unx/gtk4/gtkframe.cxx
index 9133b06ad6b3..e446ba8a55d4 100644
--- a/vcl/unx/gtk4/gtkframe.cxx
+++ b/vcl/unx/gtk4/gtkframe.cxx
@@ -7,6 +7,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+#include "transferableprovider.hxx"
+
#include "../gtk3/gtkframe.cxx"
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk4/gtkinst.cxx b/vcl/unx/gtk4/gtkinst.cxx
index c1f3e310b8e3..502eed2aa246 100644
--- a/vcl/unx/gtk4/gtkinst.cxx
+++ b/vcl/unx/gtk4/gtkinst.cxx
@@ -10,10 +10,10 @@
// make gtk4 plug advertise correctly as gtk4
#define GTK_TOOLKIT_NAME "gtk4"
-#include "contentprovider.hxx"
#include "convert3to4.hxx"
#include "notifyinglayout.hxx"
#include "surfacepaintable.hxx"
+#include "transferableprovider.hxx"
#include "../gtk3/gtkinst.cxx"
diff --git a/vcl/unx/gtk4/transferableprovider.cxx b/vcl/unx/gtk4/transferableprovider.cxx
new file mode 100644
index 000000000000..554b80c0d4a6
--- /dev/null
+++ b/vcl/unx/gtk4/transferableprovider.cxx
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <com/sun/star/datatransfer/XTransferable.hpp>
+#include <unx/gtk/gtkinst.hxx>
+#include "transferableprovider.hxx"
+
+#define TRANSFERABLE_CONTENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), transerable_content_get_type(), TransferableContent))
+
+struct _TransferableContent
+{
+ GdkContentProvider parent;
+ VclToGtkHelper* m_pConversionHelper;
+ css::datatransfer::XTransferable* m_pContents;
+ Link<void*, void> m_aDetachClipboardLink;
+};
+
+namespace
+{
+struct _TransferableContentClass : public GdkContentProviderClass
+{
+};
+}
+
+G_DEFINE_TYPE(TransferableContent, transerable_content, GDK_TYPE_CONTENT_PROVIDER)
+
+static void transerable_content_write_mime_type_async(GdkContentProvider* provider,
+ const char* mime_type, GOutputStream* stream,
+ int io_priority, GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ TransferableContent* self = TRANSFERABLE_CONTENT(provider);
+ if (!self->m_pContents)
+ return;
+ // tdf#129809 take a reference in case m_aContents is replaced during this
+ // call
+ css::uno::Reference<css::datatransfer::XTransferable> xCurrentContents(self->m_pContents);
+ self->m_pConversionHelper->setSelectionData(xCurrentContents, provider, mime_type, stream,
+ io_priority, cancellable, callback, user_data);
+}
+
+static gboolean transerable_content_write_mime_type_finish(GdkContentProvider*,
+ GAsyncResult* result, GError** error)
+{
+ return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+static GdkContentFormats* transerable_content_ref_formats(GdkContentProvider* provider)
+{
+ TransferableContent* self = TRANSFERABLE_CONTENT(provider);
+ css::uno::Reference<css::datatransfer::XTransferable> xCurrentContents(self->m_pContents);
+ if (!xCurrentContents)
+ return nullptr;
+
+ auto aFormats = xCurrentContents->getTransferDataFlavors();
+ std::vector<OString> aGtkTargets(self->m_pConversionHelper->FormatsToGtk(aFormats));
+
+ GdkContentFormatsBuilder* pBuilder = gdk_content_formats_builder_new();
+ for (const auto& rFormat : aGtkTargets)
+ gdk_content_formats_builder_add_mime_type(pBuilder, rFormat.getStr());
+ return gdk_content_formats_builder_free_to_formats(pBuilder);
+}
+
+static void transerable_content_detach_clipboard(GdkContentProvider* provider, GdkClipboard*)
+{
+ TransferableContent* self = TRANSFERABLE_CONTENT(provider);
+ self->m_aDetachClipboardLink.Call(nullptr);
+}
+
+static void transerable_content_class_init(TransferableContentClass* klass)
+{
+ GdkContentProviderClass* provider_class = GDK_CONTENT_PROVIDER_CLASS(klass);
+
+ provider_class->ref_formats = transerable_content_ref_formats;
+ provider_class->detach_clipboard = transerable_content_detach_clipboard;
+ provider_class->write_mime_type_async = transerable_content_write_mime_type_async;
+ provider_class->write_mime_type_finish = transerable_content_write_mime_type_finish;
+}
+
+static void transerable_content_init(TransferableContent* self)
+{
+ self->m_pConversionHelper = nullptr;
+ self->m_pContents = nullptr;
+ // prevent loplugin:unreffun firing on macro generated function
+ (void)transerable_content_get_instance_private(self);
+}
+
+void transerable_content_set_transferable(TransferableContent* pContent,
+ css::datatransfer::XTransferable* pTransferable)
+{
+ pContent->m_pContents = pTransferable;
+}
+
+void transerable_content_set_detach_clipboard_link(TransferableContent* pContent,
+ const Link<void*, void>& rDetachClipboardLink)
+{
+ pContent->m_aDetachClipboardLink = rDetachClipboardLink;
+}
+
+GdkContentProvider* transerable_content_new(VclToGtkHelper* pConversionHelper,
+ css::datatransfer::XTransferable* pTransferable)
+{
+ TransferableContent* content
+ = TRANSFERABLE_CONTENT(g_object_new(transerable_content_get_type(), nullptr));
+ content->m_pConversionHelper = pConversionHelper;
+ content->m_pContents = pTransferable;
+ return GDK_CONTENT_PROVIDER(content);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/gtk4/transferableprovider.hxx b/vcl/unx/gtk4/transferableprovider.hxx
new file mode 100644
index 000000000000..df4d643d9f0f
--- /dev/null
+++ b/vcl/unx/gtk4/transferableprovider.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <tools/link.hxx>
+
+#include <gtk/gtk.h>
+
+struct VclToGtkHelper;
+
+namespace com::sun::star::datatransfer
+{
+class XTransferable;
+}
+
+G_BEGIN_DECLS
+
+/*
+ Provide a mechanism to provide data from a LibreOffice XTransferable via a
+ GdkContentProvider for gtk clipboard or dnd
+*/
+
+G_DECLARE_FINAL_TYPE(TransferableContent, transerable_content, TRANSFERABLE, CONTENT,
+ GdkContentProvider)
+
+GdkContentProvider* transerable_content_new(VclToGtkHelper* pConversionHelper,
+ css::datatransfer::XTransferable* pTransferable);
+
+/*
+ Change to a new XTransferable
+ */
+void transerable_content_set_transferable(TransferableContent* pContent,
+ css::datatransfer::XTransferable* pTransferable);
+
+/*
+ If the GdkContentProvider is used by a clipboard call rDetachClipboardLink on losing
+ ownership of the clipboard
+*/
+void transerable_content_set_detach_clipboard_link(TransferableContent* pContent,
+ const Link<void*, void>& rDetachClipboardLink);
+
+G_END_DECLS
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */