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 <caol...@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: */
_______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/libreoffice-commits