[Libreoffice-commits] core.git: vcl/inc vcl/unx

Caolán McNamara caolanm at redhat.com
Tue Jan 26 05:25:53 PST 2016


 vcl/inc/unx/gtk/gtkframe.hxx  |   30 +++
 vcl/inc/unx/gtk/gtkinst.hxx   |   81 ++++++++
 vcl/unx/gtk/gtkobject.cxx     |    2 
 vcl/unx/gtk3/gtk3gtkframe.cxx |  277 ++++++++++++++++++++++++++++
 vcl/unx/gtk3/gtk3gtkinst.cxx  |  413 ++++++++++++++++++++++++++++--------------
 5 files changed, 668 insertions(+), 135 deletions(-)

New commits:
commit 0b354d18ccfc05e7c2582f851d9201e2aa353d7d
Author: Caolán McNamara <caolanm at redhat.com>
Date:   Sat Jan 23 21:10:03 2016 +0000

    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

diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx
index fbf84e0..584a2ab 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 adcb142..a2f8f78 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 1d510a4..35084aa 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 738e06e..c94c5d7 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 a3f3068..030f7c5 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: */


More information about the Libreoffice-commits mailing list