summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2016-01-23 21:10:03 +0000
committerCaolán McNamara <caolanm@redhat.com>2016-01-26 13:25:16 +0000
commit0b354d18ccfc05e7c2582f851d9201e2aa353d7d (patch)
treef69493f977b915f8cd14f693b7052ae3e02b3120
parent93fdb8dc67bf04c7a1e22c8dd15152212799c4f2 (diff)
Related: tdf#93054 gtk3: implement enough dnd to be dragged into...
from another application, e.g. text from gedit or a standalone image (view image) from firefox Change-Id: I68b82217eb2513cedc096f5ff653fb7c75b48052
-rw-r--r--vcl/inc/unx/gtk/gtkframe.hxx30
-rw-r--r--vcl/inc/unx/gtk/gtkinst.hxx81
-rw-r--r--vcl/unx/gtk/gtkobject.cxx2
-rw-r--r--vcl/unx/gtk3/gtk3gtkframe.cxx277
-rw-r--r--vcl/unx/gtk3/gtk3gtkinst.cxx413
5 files changed, 668 insertions, 135 deletions
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index fbf84e053147..584a2ab96922 100644
--- a/vcl/inc/unx/gtk/gtkframe.hxx
+++ b/vcl/inc/unx/gtk/gtkframe.hxx
@@ -54,6 +54,8 @@ typedef ::Window GdkNativeWindow;
#define GDK_WINDOW_XWINDOW(o) GDK_WINDOW_XID(o)
#define gdk_set_sm_client_id(i) gdk_x11_set_sm_client_id(i)
#define gdk_window_foreign_new_for_display(a,b) gdk_x11_window_foreign_new_for_display(a,b)
+class GtkDropTarget;
+class GtkDnDTransferable;
#endif
#if !(GLIB_MAJOR_VERSION > 2 || GLIB_MINOR_VERSION >= 26)
@@ -206,6 +208,9 @@ class GtkSalFrame : public SalFrame
long m_nWidthRequest;
long m_nHeightRequest;
cairo_region_t* m_pRegion;
+ GtkDropTarget* m_pDropTarget;
+ bool m_bInDrag;
+ GtkDnDTransferable* m_pFormatConversionRequest;
#else
GdkRegion* m_pRegion;
#endif
@@ -237,6 +242,13 @@ class GtkSalFrame : public SalFrame
static gboolean signalTooltipQuery(GtkWidget*, gint x, gint y,
gboolean keyboard_mode, GtkTooltip *tooltip,
gpointer frame);
+ static gboolean signalDragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+ guint time, gpointer frame);
+ static gboolean signalDragDrop(GtkWidget* widget, GdkDragContext *context, gint x, gint y,
+ guint time, gpointer frame);
+ static void signalDragDropReceived(GtkWidget *widget, GdkDragContext *context, gint x, gint y,
+ GtkSelectionData *data, guint ttype, guint time, gpointer frame);
+ static void signalDragLeave(GtkWidget *widget, GdkDragContext *context, guint time, gpointer frame);
#if GTK_CHECK_VERSION(3,14,0)
static void gestureSwipe(GtkGestureSwipe* gesture, gdouble velocity_x, gdouble velocity_y, gpointer frame);
static void gestureLongPress(GtkGestureLongPress* gesture, gpointer frame);
@@ -360,6 +372,24 @@ public:
cairo_t* getCairoContext() const;
void damaged(sal_Int32 nExtentsLeft, sal_Int32 nExtentsTop,
sal_Int32 nExtentsRight, sal_Int32 nExtentsBottom) const;
+
+ void registerDropTarget(GtkDropTarget* pDropTarget)
+ {
+ assert(!m_pDropTarget);
+ m_pDropTarget = pDropTarget;
+ }
+
+ void deregisterDropTarget(GtkDropTarget* pDropTarget)
+ {
+ assert(m_pDropTarget == pDropTarget); (void)pDropTarget;
+ m_pDropTarget = nullptr;
+ }
+
+ void SetFormatConversionRequest(GtkDnDTransferable *pRequest)
+ {
+ m_pFormatConversionRequest = pRequest;
+ }
+
#endif
virtual ~GtkSalFrame();
diff --git a/vcl/inc/unx/gtk/gtkinst.hxx b/vcl/inc/unx/gtk/gtkinst.hxx
index adcb142e21ac..a2f8f78943dc 100644
--- a/vcl/inc/unx/gtk/gtkinst.hxx
+++ b/vcl/inc/unx/gtk/gtkinst.hxx
@@ -23,6 +23,11 @@
#include <unx/salinst.h>
#include <unx/gensys.h>
#include <headless/svpinst.hxx>
+#include <com/sun/star/datatransfer/DataFlavor.hpp>
+#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/compbase.hxx>
#include <gtk/gtk.h>
namespace vcl
@@ -44,6 +49,82 @@ public:
void ThreadsLeave();
};
+#if GTK_CHECK_VERSION(3,0,0)
+class GtkSalFrame;
+
+struct VclToGtkHelper
+{
+ std::vector<css::datatransfer::DataFlavor> aInfoToFlavor;
+ std::vector<GtkTargetEntry> FormatsToGtk(const css::uno::Sequence<css::datatransfer::DataFlavor> &rFormats);
+private:
+ GtkTargetEntry makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor);
+};
+
+class GtkTransferable : public cppu::WeakImplHelper<css::datatransfer::XTransferable>
+{
+protected:
+ std::map<OUString, GdkAtom> m_aMimeTypeToAtom;
+
+ std::vector<css::datatransfer::DataFlavor> getTransferDataFlavorsAsVector(GdkAtom *targets, gint n_targets);
+public:
+
+ virtual css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor)
+ throw(css::datatransfer::UnsupportedFlavorException,
+ css::io::IOException,
+ css::uno::RuntimeException, std::exception) override = 0;
+
+ virtual std::vector<css::datatransfer::DataFlavor> getTransferDataFlavorsAsVector() = 0;
+
+ virtual css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors()
+ throw(css::uno::RuntimeException, std::exception) override;
+ virtual sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
+ throw(css::uno::RuntimeException, std::exception) override;
+};
+
+class GtkDropTarget : public cppu::WeakComponentImplHelper<css::datatransfer::dnd::XDropTarget,
+ css::lang::XInitialization,
+ css::lang::XServiceInfo>
+{
+ osl::Mutex m_aMutex;
+ GtkSalFrame* m_pFrame;
+ bool m_bActive;
+ sal_Int8 m_nDefaultActions;
+ std::list<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> m_aListeners;
+public:
+ GtkDropTarget();
+ virtual ~GtkDropTarget();
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any>& rArgs)
+ throw (css::uno::Exception, std::exception) override;
+ void deinitialize();
+
+ // XDropTarget
+ virtual void SAL_CALL addDropTargetListener(const css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>&)
+ throw (std::exception) override;
+ virtual void SAL_CALL removeDropTargetListener(const css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>&)
+ throw (std::exception) override;
+ virtual sal_Bool SAL_CALL isActive() throw(std::exception) override;
+ virtual void SAL_CALL setActive(sal_Bool active) throw(std::exception) override;
+ virtual sal_Int8 SAL_CALL getDefaultActions() throw(std::exception) override;
+ virtual void SAL_CALL setDefaultActions(sal_Int8 actions) throw(std::exception) override;
+
+ OUString SAL_CALL getImplementationName()
+ throw (css::uno::RuntimeException, std::exception) override;
+
+ sal_Bool SAL_CALL supportsService(OUString const & ServiceName)
+ throw (css::uno::RuntimeException, std::exception) override;
+
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames()
+ throw (css::uno::RuntimeException, std::exception) override;
+
+ void fire_dragEnter(const css::datatransfer::dnd::DropTargetDragEnterEvent& dtdee);
+ void fire_dragOver(const css::datatransfer::dnd::DropTargetDragEvent& dtde);
+ void fire_drop(const css::datatransfer::dnd::DropTargetDropEvent& dtde);
+ void fire_dragExit(const css::datatransfer::dnd::DropTargetEvent& dte);
+};
+#endif
+
class GtkSalTimer;
#if GTK_CHECK_VERSION(3,0,0)
class GtkInstance : public SvpSalInstance
diff --git a/vcl/unx/gtk/gtkobject.cxx b/vcl/unx/gtk/gtkobject.cxx
index 1d510a45c610..35084aa4ef4e 100644
--- a/vcl/unx/gtk/gtkobject.cxx
+++ b/vcl/unx/gtk/gtkobject.cxx
@@ -62,8 +62,8 @@ GtkSalObject::GtkSalObject( GtkSalFrame* pParent, bool bShow )
#else
static int nWindow = 0;
m_aSystemData.aWindow = nWindow;
- m_aSystemData.aShellWindow = pParent->GetSystemData()->aWindow;
++nWindow;
+ m_aSystemData.aShellWindow = reinterpret_cast<long>(this);
#endif
m_aSystemData.pSalFrame = nullptr;
m_aSystemData.pWidget = m_pSocket;
diff --git a/vcl/unx/gtk3/gtk3gtkframe.cxx b/vcl/unx/gtk3/gtk3gtkframe.cxx
index 738e06e9b098..c94c5d75f5ad 100644
--- a/vcl/unx/gtk3/gtk3gtkframe.cxx
+++ b/vcl/unx/gtk3/gtk3gtkframe.cxx
@@ -74,6 +74,7 @@
#include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
+#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/ModuleManager.hpp>
#include <com/sun/star/frame/XFrame.hpp>
@@ -783,6 +784,12 @@ void GtkSalFrame::InvalidateGraphics()
GtkSalFrame::~GtkSalFrame()
{
+ if (m_pDropTarget)
+ {
+ m_pDropTarget->deinitialize();
+ m_pDropTarget = nullptr;
+ }
+
InvalidateGraphics();
if( m_pParent )
@@ -984,6 +991,12 @@ void GtkSalFrame::InitCommon()
m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-press-event", G_CALLBACK(signalButton), this ));
m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "motion-notify-event", G_CALLBACK(signalMotion), this ));
m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "button-release-event", G_CALLBACK(signalButton), this ));
+ gtk_drag_dest_set(GTK_WIDGET(pEventWidget), (GtkDestDefaults)0, nullptr, 0, (GdkDragAction)0);
+ gtk_drag_dest_set_track_motion(GTK_WIDGET(pEventWidget), true);
+ m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-motion", G_CALLBACK(signalDragMotion), this ));
+ m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-drop", G_CALLBACK(signalDragDrop), this ));
+ m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-received", G_CALLBACK(signalDragDropReceived), this ));
+ m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-leave", G_CALLBACK(signalDragLeave), this ));
g_signal_connect( G_OBJECT(m_pFixedContainer), "draw", G_CALLBACK(signalDraw), this );
g_signal_connect( G_OBJECT(m_pFixedContainer), "size-allocate", G_CALLBACK(sizeAllocated), this );
#if GTK_CHECK_VERSION(3,14,0)
@@ -1027,6 +1040,9 @@ void GtkSalFrame::InitCommon()
m_hBackgroundPixmap = None;
m_nExtStyle = 0;
m_pRegion = nullptr;
+ m_pDropTarget = nullptr;
+ m_bInDrag = false;
+ m_pFormatConversionRequest = nullptr;
m_ePointerStyle = static_cast<PointerStyle>(0xffff);
m_bSetFocusOnMap = false;
m_pSalMenu = nullptr;
@@ -1053,8 +1069,8 @@ void GtkSalFrame::InitCommon()
m_aSystemData.nSize = sizeof( SystemEnvData );
static int nWindow = 0;
m_aSystemData.aWindow = nWindow;
- m_aSystemData.aShellWindow = nWindow;
++nWindow;
+ m_aSystemData.aShellWindow = reinterpret_cast<long>(this);
m_aSystemData.pSalFrame = this;
m_aSystemData.pWidget = m_pWindow;
m_aSystemData.nScreen = m_nXScreen.getXScreen();
@@ -3011,6 +3027,265 @@ gboolean GtkSalFrame::signalVisibility( GtkWidget*, GdkEventVisibility* pEvent,
return true;
}
+namespace
+{
+ GdkDragAction VclToGdk(sal_Int8 dragOperation)
+ {
+ GdkDragAction eRet(static_cast<GdkDragAction>(0));
+ if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_COPY)
+ eRet = static_cast<GdkDragAction>(eRet | GDK_ACTION_COPY);
+ if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_MOVE)
+ eRet = static_cast<GdkDragAction>(eRet | GDK_ACTION_MOVE);
+ if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_LINK)
+ eRet = static_cast<GdkDragAction>(eRet | GDK_ACTION_LINK);
+ return eRet;
+ }
+
+ sal_Int8 GdkToVcl(GdkDragAction dragOperation)
+ {
+ sal_Int8 nRet(0);
+ if (dragOperation & GDK_ACTION_COPY)
+ nRet |= css::datatransfer::dnd::DNDConstants::ACTION_COPY;
+ if (dragOperation & GDK_ACTION_MOVE)
+ nRet |= css::datatransfer::dnd::DNDConstants::ACTION_MOVE;
+ if (dragOperation & GDK_ACTION_LINK)
+ nRet |= css::datatransfer::dnd::DNDConstants::ACTION_LINK;
+ return nRet;
+ }
+}
+
+class GtkDropTargetDropContext : public cppu::WeakImplHelper<css::datatransfer::dnd::XDropTargetDropContext>
+{
+ GdkDragContext *m_pContext;
+ guint m_nTime;
+public:
+ GtkDropTargetDropContext(GdkDragContext *pContext, guint nTime)
+ : m_pContext(pContext)
+ , m_nTime(nTime)
+ {
+ }
+
+ // XDropTargetDropContext
+ virtual void SAL_CALL acceptDrop(sal_Int8 dragOperation) throw(std::exception) override
+ {
+ gdk_drag_status(m_pContext, VclToGdk(dragOperation), m_nTime);
+ }
+
+ virtual void SAL_CALL rejectDrop() throw(std::exception) override
+ {
+ gdk_drag_status(m_pContext, static_cast<GdkDragAction>(0), m_nTime);
+ }
+
+ virtual void SAL_CALL dropComplete(sal_Bool bSuccess) throw(std::exception) override
+ {
+ gtk_drag_finish(m_pContext, bSuccess, false, m_nTime);
+ }
+};
+
+class GtkDnDTransferable : public GtkTransferable
+{
+ GdkDragContext *m_pContext;
+ guint m_nTime;
+ GtkWidget *m_pWidget;
+ GtkSalFrame *m_pFrame;
+ GMainLoop *m_pLoop;
+ GtkSelectionData *m_pData;
+public:
+ GtkDnDTransferable(GdkDragContext *pContext, guint nTime, GtkWidget *pWidget, GtkSalFrame *pFrame)
+ : m_pContext(pContext)
+ , m_nTime(nTime)
+ , m_pWidget(pWidget)
+ , m_pFrame(pFrame)
+ , m_pLoop(nullptr)
+ , m_pData(nullptr)
+ {
+ }
+
+ virtual css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor)
+ throw(css::datatransfer::UnsupportedFlavorException,
+ css::io::IOException,
+ css::uno::RuntimeException, std::exception) override
+ {
+ css::datatransfer::DataFlavor aFlavor(rFlavor);
+ if (aFlavor.MimeType == "text/plain;charset=utf-16")
+ aFlavor.MimeType = "text/plain;charset=utf-8";
+
+ auto it = m_aMimeTypeToAtom.find(aFlavor.MimeType);
+ if (it == m_aMimeTypeToAtom.end())
+ return css::uno::Any();
+
+ /* like gtk_clipboard_wait_for_contents run a sub loop
+ * waiting for drag-data-received triggered from
+ * gtk_drag_get_data
+ */
+ {
+ m_pLoop = g_main_loop_new(NULL, true);
+ m_pFrame->SetFormatConversionRequest(this);
+
+ gtk_drag_get_data(m_pWidget, m_pContext, it->second, m_nTime);
+
+ if (g_main_loop_is_running(m_pLoop))
+ {
+ gdk_threads_leave();
+ g_main_loop_run(m_pLoop);
+ gdk_threads_enter();
+ }
+
+ g_main_loop_unref(m_pLoop);
+ m_pLoop = nullptr;
+ m_pFrame->SetFormatConversionRequest(nullptr);
+ }
+
+ css::uno::Any aRet;
+
+ if (aFlavor.MimeType == "text/plain;charset=utf-8")
+ {
+ OUString aStr;
+ gchar *pText = reinterpret_cast<gchar*>(gtk_selection_data_get_text(m_pData));
+ if (pText)
+ aStr = OUString(pText, rtl_str_getLength(pText), RTL_TEXTENCODING_UTF8);
+ g_free(pText);
+ aRet <<= aStr.replaceAll("\r\n", "\n");
+ }
+ else
+ {
+ gint length(0);
+ const guchar *rawdata = gtk_selection_data_get_data_with_length(m_pData,
+ &length);
+ css::uno::Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(rawdata), length);
+ aRet <<= aSeq;
+ }
+
+ gtk_selection_data_free(m_pData);
+
+ return aRet;
+ }
+
+ virtual std::vector<css::datatransfer::DataFlavor> getTransferDataFlavorsAsVector()
+ {
+ std::vector<GdkAtom> targets;
+ for (GList* l = gdk_drag_context_list_targets(m_pContext); l; l = l->next)
+ targets.push_back(static_cast<GdkAtom>(l->data));
+ return GtkTransferable::getTransferDataFlavorsAsVector(targets.data(), targets.size());
+ }
+
+ void LoopEnd(GtkSelectionData *pData)
+ {
+ m_pData = pData;
+ g_main_loop_quit(m_pLoop);
+ }
+};
+
+gboolean GtkSalFrame::signalDragDrop(GtkWidget* pWidget, GdkDragContext* context, gint x, gint y, guint time, gpointer frame)
+{
+ GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
+
+ if (!pThis->m_pDropTarget)
+ return false;
+
+ css::datatransfer::dnd::DropTargetDropEvent aEvent;
+ aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(pThis->m_pDropTarget);
+ aEvent.Context = new GtkDropTargetDropContext(context, time);
+ aEvent.LocationX = x;
+ aEvent.LocationY = y;
+ aEvent.DropAction = GdkToVcl(gdk_drag_context_get_suggested_action(context));
+ aEvent.SourceActions = GdkToVcl(gdk_drag_context_get_actions(context));
+ css::uno::Reference<css::datatransfer::XTransferable> xTransferable(new GtkDnDTransferable(context, time, pWidget, pThis));
+ aEvent.Transferable = xTransferable;
+
+ pThis->m_pDropTarget->fire_drop(aEvent);
+
+ return true;
+}
+
+class GtkDropTargetDragContext : public cppu::WeakImplHelper<css::datatransfer::dnd::XDropTargetDragContext>
+{
+ GdkDragContext *m_pContext;
+ guint m_nTime;
+public:
+ GtkDropTargetDragContext(GdkDragContext *pContext, guint nTime)
+ : m_pContext(pContext)
+ , m_nTime(nTime)
+ {
+ }
+
+ virtual void SAL_CALL acceptDrag(sal_Int8 dragOperation) throw(std::exception) override
+ {
+ gdk_drag_status(m_pContext, VclToGdk(dragOperation), m_nTime);
+ }
+
+ virtual void SAL_CALL rejectDrag() throw(std::exception) override
+ {
+ gdk_drag_status(m_pContext, static_cast<GdkDragAction>(0), m_nTime);
+ }
+};
+
+void GtkSalFrame::signalDragDropReceived(GtkWidget* /*pWidget*/, GdkDragContext * /*context*/, gint /*x*/, gint /*y*/, GtkSelectionData* data, guint /*ttype*/, guint /*time*/, gpointer frame)
+{
+ GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
+
+ /*
+ * If we get a drop, then we will call like gtk_clipboard_wait_for_contents
+ * with a loop inside a loop to get the right format, so if this is the
+ * case return to the outer loop here with a copy of the desired data
+ *
+ * don't look at me like that.
+ */
+ if (!pThis->m_pFormatConversionRequest)
+ return;
+
+ pThis->m_pFormatConversionRequest->LoopEnd(gtk_selection_data_copy(data));
+}
+
+gboolean GtkSalFrame::signalDragMotion(GtkWidget *widget, GdkDragContext *context, gint x, gint y, guint time, gpointer frame)
+{
+ GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
+
+ if (!pThis->m_pDropTarget)
+ return false;
+
+ if (!pThis->m_bInDrag)
+ gtk_drag_highlight(widget);
+
+ css::datatransfer::dnd::DropTargetDragEnterEvent aEvent;
+ aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(pThis->m_pDropTarget);
+ aEvent.Context = new GtkDropTargetDragContext(context, time);
+ aEvent.LocationX = x;
+ aEvent.LocationY = y;
+ aEvent.DropAction = GdkToVcl(gdk_drag_context_get_suggested_action(context));
+ aEvent.SourceActions = GdkToVcl(gdk_drag_context_get_actions(context));
+
+ if (!pThis->m_bInDrag)
+ {
+ css::uno::Reference<css::datatransfer::XTransferable> xTrans(new GtkDnDTransferable(context, time, widget, pThis));
+ css::uno::Sequence<css::datatransfer::DataFlavor> aFormats = xTrans->getTransferDataFlavors();
+ aEvent.SupportedDataFlavors = aFormats;
+ pThis->m_pDropTarget->fire_dragEnter(aEvent);
+ pThis->m_bInDrag = true;
+ }
+ else
+ {
+ pThis->m_pDropTarget->fire_dragOver(aEvent);
+ }
+
+ return true;
+}
+
+void GtkSalFrame::signalDragLeave(GtkWidget *widget, GdkDragContext * /*context*/, guint /*time*/, gpointer frame)
+{
+ GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
+ if (!pThis->m_pDropTarget)
+ return;
+ pThis->m_bInDrag = false;
+ gtk_drag_unhighlight(widget);
+
+#if 0
+ css::datatransfer::dnd::DropTargetEvent aEvent;
+ aEvent.Source = static_cast<css::datatransfer::dnd::XDropTarget*>(pThis->m_pDropTarget);
+ pThis->m_pDropTarget->fire_dragExit(aEvent);
+#endif
+}
+
void GtkSalFrame::signalDestroy( GtkWidget* pObj, gpointer frame )
{
GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame);
diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx
index a3f30683cbdb..030f7c59d91c 100644
--- a/vcl/unx/gtk3/gtk3gtkinst.cxx
+++ b/vcl/unx/gtk3/gtk3gtkinst.cxx
@@ -93,15 +93,87 @@ namespace
};
}
-class GtkTransferable : public ::cppu::WeakImplHelper <
- css::datatransfer::XTransferable >
+std::vector<css::datatransfer::DataFlavor> GtkTransferable::getTransferDataFlavorsAsVector(GdkAtom *targets, gint n_targets)
+{
+ std::vector<css::datatransfer::DataFlavor> aVector;
+
+ bool bHaveText = false, bHaveUTF16 = false;
+
+ for (gint i = 0; i < n_targets; ++i)
+ {
+ gchar* pName = gdk_atom_name(targets[i]);
+ const char* pFinalName = pName;
+ css::datatransfer::DataFlavor aFlavor;
+
+ for (size_t j = 0; j < SAL_N_ELEMENTS(aConversionTab); ++j)
+ {
+ if (rtl_str_compare(pName, aConversionTab[j].pNativeType) == 0)
+ {
+ pFinalName = aConversionTab[j].pType;
+ break;
+ }
+ }
+
+ aFlavor.MimeType = OUString(pFinalName,
+ rtl_str_getLength(pFinalName),
+ RTL_TEXTENCODING_UTF8);
+
+ m_aMimeTypeToAtom[aFlavor.MimeType] = targets[i];
+
+ aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+
+ sal_Int32 nIndex(0);
+ if (aFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain")
+ {
+ bHaveText = true;
+ OUString aToken(aFlavor.MimeType.getToken(0, ';', nIndex));
+ if (aToken == "charset=utf-16")
+ {
+ bHaveUTF16 = true;
+ aFlavor.DataType = cppu::UnoType<OUString>::get();
+ }
+ }
+ aVector.push_back(aFlavor);
+ g_free(pName);
+ }
+
+ //If we have text, but no UTF-16 format which is basically the only
+ //text-format LibreOffice supports for cnp then claim we do and we
+ //will convert on demand
+ if (bHaveText && !bHaveUTF16)
+ {
+ css::datatransfer::DataFlavor aFlavor;
+ aFlavor.MimeType = "text/plain;charset=utf-16";
+ aFlavor.DataType = cppu::UnoType<OUString>::get();
+ aVector.push_back(aFlavor);
+ }
+
+ return aVector;
+}
+
+
+css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL GtkTransferable::getTransferDataFlavors()
+ throw(css::uno::RuntimeException, std::exception)
+{
+ return comphelper::containerToSequence(getTransferDataFlavorsAsVector());
+}
+
+sal_Bool SAL_CALL GtkTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
+ throw(css::uno::RuntimeException, std::exception)
+{
+ const std::vector<css::datatransfer::DataFlavor> aAll =
+ getTransferDataFlavorsAsVector();
+
+ return std::find_if(aAll.begin(), aAll.end(), DataFlavorEq(rFlavor)) != aAll.end();
+}
+
+class GtkClipboardTransferable : public GtkTransferable
{
private:
GdkAtom m_nSelection;
- std::map<OUString, GdkAtom> m_aMimeTypeToAtom;
public:
- explicit GtkTransferable(GdkAtom nSelection)
+ explicit GtkClipboardTransferable(GdkAtom nSelection)
: m_nSelection(nSelection)
{
}
@@ -155,77 +227,12 @@ public:
gint n_targets;
if (gtk_clipboard_wait_for_targets(clipboard, &targets, &n_targets))
{
- bool bHaveText = false, bHaveUTF16 = false;
-
- for (gint i = 0; i < n_targets; ++i)
- {
- gchar* pName = gdk_atom_name(targets[i]);
- const char* pFinalName = pName;
- css::datatransfer::DataFlavor aFlavor;
-
- for (size_t j = 0; j < SAL_N_ELEMENTS(aConversionTab); ++j)
- {
- if (rtl_str_compare(pName, aConversionTab[j].pNativeType) == 0)
- {
- pFinalName = aConversionTab[j].pType;
- break;
- }
- }
-
- aFlavor.MimeType = OUString(pFinalName,
- rtl_str_getLength(pFinalName),
- RTL_TEXTENCODING_UTF8);
-
- m_aMimeTypeToAtom[aFlavor.MimeType] = targets[i];
-
- aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
-
- sal_Int32 nIndex(0);
- if (aFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain")
- {
- bHaveText = true;
- OUString aToken(aFlavor.MimeType.getToken(0, ';', nIndex));
- if (aToken == "charset=utf-16")
- {
- bHaveUTF16 = true;
- aFlavor.DataType = cppu::UnoType<OUString>::get();
- }
- }
- aVector.push_back(aFlavor);
- g_free(pName);
- }
-
+ aVector = GtkTransferable::getTransferDataFlavorsAsVector(targets, n_targets);
g_free(targets);
-
- //If we have text, but no UTF-16 format which is basically the only
- //text-format LibreOffice supports for cnp then claim we do and we
- //will convert on demand
- if (bHaveText && !bHaveUTF16)
- {
- css::datatransfer::DataFlavor aFlavor;
- aFlavor.MimeType = "text/plain;charset=utf-16";
- aFlavor.DataType = cppu::UnoType<OUString>::get();
- aVector.push_back(aFlavor);
- }
}
return aVector;
}
-
- virtual css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors()
- throw(css::uno::RuntimeException, std::exception) override
- {
- return comphelper::containerToSequence(getTransferDataFlavorsAsVector());
- }
-
- virtual sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
- throw(css::uno::RuntimeException, std::exception) override
- {
- const std::vector<css::datatransfer::DataFlavor> aAll =
- getTransferDataFlavorsAsVector();
-
- return std::find_if(aAll.begin(), aAll.end(), DataFlavorEq(rFlavor)) != aAll.end();
- }
};
//We want to use gtk_clipboard_get_owner own owner-change to distinguish between
@@ -286,7 +293,7 @@ class VclGtkClipboard :
Reference<css::datatransfer::clipboard::XClipboardOwner> m_aOwner;
std::list< Reference<css::datatransfer::clipboard::XClipboardListener> > m_aListeners;
std::vector<GtkTargetEntry> m_aGtkTargets;
- std::vector<css::datatransfer::DataFlavor> m_aInfoToFlavor;
+ VclToGtkHelper m_aConversionHelper;
public:
@@ -301,9 +308,6 @@ public:
virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) throw( RuntimeException, std::exception ) override;
virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() throw( RuntimeException, std::exception ) override;
- static OUString getImplementationName_static();
- static Sequence< OUString > getSupportedServiceNames_static();
-
/*
* XClipboard
*/
@@ -346,29 +350,17 @@ public:
void ClipboardGet(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info);
void ClipboardClear(GtkClipboard *clipboard);
void OwnerChanged(GtkClipboard *clipboard, GdkEvent *event);
-private:
- GtkTargetEntry makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor);
};
-OUString VclGtkClipboard::getImplementationName_static()
-{
- return OUString( "com.sun.star.datatransfer.VclGtkClipboard" );
-}
-
-Sequence< OUString > VclGtkClipboard::getSupportedServiceNames_static()
-{
- Sequence< OUString > aRet { "com.sun.star.datatransfer.clipboard.SystemClipboard" };
- return aRet;
-}
-
OUString VclGtkClipboard::getImplementationName() throw( RuntimeException, std::exception )
{
- return getImplementationName_static();
+ return OUString("com.sun.star.datatransfer.VclGtkClipboard");
}
Sequence< OUString > VclGtkClipboard::getSupportedServiceNames() throw( RuntimeException, std::exception )
{
- return getSupportedServiceNames_static();
+ Sequence<OUString> aRet { "com.sun.star.datatransfer.clipboard.SystemClipboard" };
+ return aRet;
}
sal_Bool VclGtkClipboard::supportsService( const OUString& ServiceName ) throw( RuntimeException, std::exception )
@@ -383,7 +375,7 @@ Reference< css::datatransfer::XTransferable > VclGtkClipboard::getContents() thr
{
//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 GtkTransferable(m_nSelection);
+ m_aContents = new GtkClipboardTransferable(m_nSelection);
}
return m_aContents;
}
@@ -394,11 +386,11 @@ void VclGtkClipboard::ClipboardGet(GtkClipboard* /*clipboard*/, GtkSelectionData
if (!m_aContents.is())
return;
- GdkAtom type(gdk_atom_intern(OUStringToOString(m_aInfoToFlavor[info].MimeType,
+ GdkAtom type(gdk_atom_intern(OUStringToOString(m_aConversionHelper.aInfoToFlavor[info].MimeType,
RTL_TEXTENCODING_UTF8).getStr(),
false));
- css::datatransfer::DataFlavor aFlavor(m_aInfoToFlavor[info]);
+ css::datatransfer::DataFlavor aFlavor(m_aConversionHelper.aInfoToFlavor[info]);
if (aFlavor.MimeType == "UTF8_STRING" || aFlavor.MimeType == "STRING")
aFlavor.MimeType = "text/plain;charset=utf-8";
@@ -467,20 +459,20 @@ void VclGtkClipboard::ClipboardClear(GtkClipboard * /*clipboard*/)
m_aGtkTargets.clear();
}
-GtkTargetEntry VclGtkClipboard::makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor)
+GtkTargetEntry VclToGtkHelper::makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor)
{
GtkTargetEntry aEntry;
aEntry.target =
g_strdup(OUStringToOString(rFlavor.MimeType, RTL_TEXTENCODING_UTF8).getStr());
aEntry.flags = 0;
- auto it = std::find_if(m_aInfoToFlavor.begin(), m_aInfoToFlavor.end(),
+ auto it = std::find_if(aInfoToFlavor.begin(), aInfoToFlavor.end(),
DataFlavorEq(rFlavor));
- if (it != m_aInfoToFlavor.end())
- aEntry.info = std::distance(m_aInfoToFlavor.begin(), it);
+ if (it != aInfoToFlavor.end())
+ aEntry.info = std::distance(aInfoToFlavor.begin(), it);
else
{
- aEntry.info = m_aInfoToFlavor.size();
- m_aInfoToFlavor.push_back(rFlavor);
+ aEntry.info = aInfoToFlavor.size();
+ aInfoToFlavor.push_back(rFlavor);
}
return aEntry;
}
@@ -539,6 +531,47 @@ VclGtkClipboard::~VclGtkClipboard()
ClipboardClear(nullptr);
}
+std::vector<GtkTargetEntry> VclToGtkHelper::FormatsToGtk(const css::uno::Sequence<css::datatransfer::DataFlavor> &rFormats)
+{
+ std::vector<GtkTargetEntry> aGtkTargets;
+
+ bool bHaveText(false), bHaveUTF8(false);
+ for (int i = 0; i < rFormats.getLength(); ++i)
+ {
+ const css::datatransfer::DataFlavor& rFlavor = rFormats[i];
+
+ sal_Int32 nIndex(0);
+ if (rFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain")
+ {
+ bHaveText = true;
+ OUString aToken(rFlavor.MimeType.getToken(0, ';', nIndex));
+ if (aToken == "charset=utf-8")
+ {
+ bHaveUTF8 = true;
+ }
+ }
+ GtkTargetEntry aEntry(makeGtkTargetEntry(rFlavor));
+ aGtkTargets.push_back(aEntry);
+ }
+
+ if (bHaveText)
+ {
+ css::datatransfer::DataFlavor aFlavor;
+ aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
+ if (!bHaveUTF8)
+ {
+ aFlavor.MimeType = "text/plain;charset=utf-8";
+ aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
+ }
+ aFlavor.MimeType = "UTF8_STRING";
+ aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
+ aFlavor.MimeType = "STRING";
+ aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
+ }
+
+ return aGtkTargets;
+}
+
void VclGtkClipboard::setContents(
const Reference< css::datatransfer::XTransferable >& xTrans,
const Reference< css::datatransfer::clipboard::XClipboardOwner >& xClipboardOwner )
@@ -556,41 +589,7 @@ void VclGtkClipboard::setContents(
if (m_aContents.is())
{
css::uno::Sequence<css::datatransfer::DataFlavor> aFormats = xTrans->getTransferDataFlavors();
- std::vector<GtkTargetEntry> aGtkTargets;
- bool bHaveText(false), bHaveUTF8(false);
- for (int i = 0; i < aFormats.getLength(); ++i)
- {
- const css::datatransfer::DataFlavor& rFlavor = aFormats[i];
-
- sal_Int32 nIndex(0);
- if (rFlavor.MimeType.getToken(0, ';', nIndex) == "text/plain")
- {
- bHaveText = true;
- OUString aToken(rFlavor.MimeType.getToken(0, ';', nIndex));
- if (aToken == "charset=utf-8")
- {
- bHaveUTF8 = true;
- }
- }
- GtkTargetEntry aEntry(makeGtkTargetEntry(rFlavor));
- aGtkTargets.push_back(aEntry);
- }
-
- if (bHaveText)
- {
- css::datatransfer::DataFlavor aFlavor;
- aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
- if (!bHaveUTF8)
- {
- aFlavor.MimeType = "text/plain;charset=utf-8";
- aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
- }
- aFlavor.MimeType = "UTF8_STRING";
- aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
- aFlavor.MimeType = "STRING";
- aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
- }
-
+ std::vector<GtkTargetEntry> aGtkTargets(m_aConversionHelper.FormatsToGtk(aFormats));
if (!aGtkTargets.empty())
{
//if there was a previous gtk_clipboard_set_with_data call then
@@ -662,4 +661,152 @@ Reference< XInterface > GtkInstance::CreateClipboard(const Sequence< Any >& argu
return Reference< XInterface >( static_cast<cppu::OWeakObject *>(new VclGtkClipboard(nSelection)) );
}
+GtkDropTarget::GtkDropTarget()
+ : WeakComponentImplHelper(m_aMutex)
+ , m_pFrame(nullptr)
+ , m_bActive(false)
+ , m_nDefaultActions(0)
+{
+}
+
+OUString SAL_CALL GtkDropTarget::getImplementationName()
+ throw (css::uno::RuntimeException, std::exception)
+{
+ return OUString("com.sun.star.datatransfer.dnd.VclGtkDropTarget");
+}
+
+sal_Bool SAL_CALL GtkDropTarget::supportsService(OUString const & ServiceName)
+ throw (css::uno::RuntimeException, std::exception)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL GtkDropTarget::getSupportedServiceNames()
+ throw (css::uno::RuntimeException, std::exception)
+{
+ Sequence<OUString> aRet { "com.sun.star.datatransfer.dnd.GtkDropTarget" };
+ return aRet;
+}
+
+GtkDropTarget::~GtkDropTarget()
+{
+ if (m_pFrame)
+ m_pFrame->deregisterDropTarget(this);
+}
+
+void GtkDropTarget::deinitialize()
+{
+ m_pFrame = nullptr;
+ m_bActive = false;
+}
+
+void GtkDropTarget::initialize(const Sequence<Any>& rArguments) throw( Exception, std::exception )
+{
+ if (rArguments.getLength() < 2)
+ {
+ throw RuntimeException("DropTarget::initialize: Cannot install window event handler",
+ static_cast<OWeakObject*>(this));
+ }
+
+ sal_Size nFrame = 0;
+ rArguments.getConstArray()[1] >>= nFrame;
+
+ if (!nFrame)
+ {
+ throw RuntimeException("DropTarget::initialize: missing SalFrame",
+ static_cast<OWeakObject*>(this));
+ }
+
+ m_pFrame = reinterpret_cast<GtkSalFrame*>(nFrame);
+ m_pFrame->registerDropTarget(this);
+ m_bActive = true;
+}
+
+void GtkDropTarget::addDropTargetListener( const Reference< css::datatransfer::dnd::XDropTargetListener >& xListener) throw(std::exception)
+{
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+
+ m_aListeners.push_back( xListener );
+}
+
+void GtkDropTarget::removeDropTargetListener( const Reference< css::datatransfer::dnd::XDropTargetListener >& xListener) throw(std::exception)
+{
+ ::osl::Guard< ::osl::Mutex > aGuard( m_aMutex );
+
+ m_aListeners.remove( xListener );
+}
+
+void GtkDropTarget::fire_drop(const css::datatransfer::dnd::DropTargetDropEvent& dtde)
+{
+ osl::ClearableGuard<osl::Mutex> aGuard( m_aMutex );
+ std::list<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ aGuard.clear();
+
+ for (auto it = aListeners.begin(); it != aListeners.end(); ++it)
+ {
+ (*it)->drop( dtde );
+ }
+}
+
+void GtkDropTarget::fire_dragEnter(const css::datatransfer::dnd::DropTargetDragEnterEvent& dtde)
+{
+ osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex );
+ std::list<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ aGuard.clear();
+
+ for (auto it = aListeners.begin(); it != aListeners.end(); ++it)
+ {
+ (*it)->dragEnter( dtde );
+ }
+}
+
+void GtkDropTarget::fire_dragOver(const css::datatransfer::dnd::DropTargetDragEvent& dtde)
+{
+ osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex );
+ std::list<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ aGuard.clear();
+
+ for (auto it = aListeners.begin(); it != aListeners.end(); ++it)
+ {
+ (*it)->dragOver( dtde );
+ }
+}
+
+void GtkDropTarget::fire_dragExit(const css::datatransfer::dnd::DropTargetEvent& dte)
+{
+ osl::ClearableGuard< ::osl::Mutex > aGuard( m_aMutex );
+ std::list<css::uno::Reference<css::datatransfer::dnd::XDropTargetListener>> aListeners(m_aListeners);
+ aGuard.clear();
+
+ for (auto it = aListeners.begin(); it != aListeners.end(); ++it)
+ {
+ (*it)->dragExit( dte );
+ }
+}
+
+sal_Bool GtkDropTarget::isActive() throw(std::exception)
+{
+ return m_bActive;
+}
+
+void GtkDropTarget::setActive(sal_Bool bActive) throw(std::exception)
+{
+ m_bActive = bActive;
+}
+
+sal_Int8 GtkDropTarget::getDefaultActions() throw(std::exception)
+{
+ return m_nDefaultActions;
+}
+
+void GtkDropTarget::setDefaultActions(sal_Int8 nDefaultActions) throw(std::exception)
+{
+ m_nDefaultActions = nDefaultActions;
+}
+
+Reference< XInterface > SalInstance::CreateDropTarget()
+{
+ return Reference< XInterface >( static_cast<cppu::OWeakObject *>(new GtkDropTarget()) );
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */