diff options
author | Caolán McNamara <caolanm@redhat.com> | 2016-01-23 21:10:03 +0000 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2016-01-28 10:41:46 +0000 |
commit | 237ebf91c17f8c20304ebd4f7cf8bd058bc6f7c7 (patch) | |
tree | f11c37c9129318949e346269557111d63100e981 | |
parent | b0b8b419b249860617b12bf3e7dd233ba4f71e04 (diff) |
Resolves: tdf#93054 gtk3: implement drag and drop
Change-Id: Ib644ea36b8a9e68e023e465ef159b9a4890e5d37
(cherry picked from commit a5b4f6e456bfb735385e8d3d6945ea8f3be1ba94)
-rw-r--r-- | vcl/inc/unx/gtk/gtkframe.hxx | 57 | ||||
-rw-r--r-- | vcl/inc/unx/gtk/gtkinst.hxx | 128 | ||||
-rw-r--r-- | vcl/source/window/mouse.cxx | 1 | ||||
-rw-r--r-- | vcl/unx/gtk/gtkobject.cxx | 2 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkframe.cxx | 454 | ||||
-rw-r--r-- | vcl/unx/gtk3/gtk3gtkinst.cxx | 559 |
6 files changed, 1030 insertions, 171 deletions
diff --git a/vcl/inc/unx/gtk/gtkframe.hxx b/vcl/inc/unx/gtk/gtkframe.hxx index 972b241752d8..da629b7ef047 100644 --- a/vcl/inc/unx/gtk/gtkframe.hxx +++ b/vcl/inc/unx/gtk/gtkframe.hxx @@ -42,6 +42,9 @@ #include <basebmp/bitmapdevice.hxx> #include <basebmp/scanlineformats.hxx> #include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/datatransfer/dnd/XDragSource.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> #include <list> #include <vector> @@ -54,6 +57,9 @@ 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 GtkDragSource; +class GtkDnDTransferable; #endif #if !(GLIB_MAJOR_VERSION > 2 || GLIB_MINOR_VERSION >= 26) @@ -205,6 +211,10 @@ class GtkSalFrame : public SalFrame, public X11WindowProvider long m_nWidthRequest; long m_nHeightRequest; cairo_region_t* m_pRegion; + GtkDropTarget* m_pDropTarget; + GtkDragSource* m_pDragSource; + bool m_bInDrag; + GtkDnDTransferable* m_pFormatConversionRequest; #else GdkRegion* m_pRegion; #endif @@ -236,6 +246,20 @@ class GtkSalFrame : public SalFrame, public X11WindowProvider 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); + + static gboolean signalDragFailed(GtkWidget *widget, GdkDragContext *context, GtkDragResult result, gpointer frame); + static void signalDragDelete(GtkWidget *widget, GdkDragContext *context, gpointer frame); + static void signalDragEnd(GtkWidget *widget, GdkDragContext *context, gpointer frame); + static void signalDragDataGet(GtkWidget* widget, GdkDragContext* context, GtkSelectionData *data, guint info, + 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); @@ -357,6 +381,39 @@ public: // only for gtk3 ... cairo_t* getCairoContext() const; void damaged (const basegfx::B2IBox& rDamageRect); + + void registerDropTarget(GtkDropTarget* pDropTarget) + { + assert(!m_pDropTarget); + m_pDropTarget = pDropTarget; + } + + void deregisterDropTarget(GtkDropTarget* pDropTarget) + { + assert(m_pDropTarget == pDropTarget); (void)pDropTarget; + m_pDropTarget = nullptr; + } + + void registerDragSource(GtkDragSource* pDragSource) + { + assert(!m_pDragSource); + m_pDragSource = pDragSource; + } + + void deregisterDragSource(GtkDragSource* pDragSource) + { + assert(m_pDragSource == pDragSource); (void)pDragSource; + m_pDragSource = nullptr; + } + + void SetFormatConversionRequest(GtkDnDTransferable *pRequest) + { + m_pFormatConversionRequest = pRequest; + } + + void startDrag(gint nButton, gint nDragOriginX, gint nDragOriginY, + GdkDragAction sourceActions, GtkTargetList* pTargetList); + #endif virtual ~GtkSalFrame(); diff --git a/vcl/inc/unx/gtk/gtkinst.hxx b/vcl/inc/unx/gtk/gtkinst.hxx index a7c489b7bf5d..b91ec5248416 100644 --- a/vcl/inc/unx/gtk/gtkinst.hxx +++ b/vcl/inc/unx/gtk/gtkinst.hxx @@ -23,6 +23,12 @@ #include <unx/salinst.h> #include <generic/gensys.h> #include <headless/svpinst.hxx> +#include <com/sun/star/datatransfer/DataFlavor.hpp> +#include <com/sun/star/datatransfer/dnd/XDragSource.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 +50,126 @@ 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); + void setSelectionData(const css::uno::Reference<css::datatransfer::XTransferable> &rTrans, + GtkSelectionData *selection_data, guint info); +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); +}; + +class GtkDragSource : public cppu::WeakComponentImplHelper<css::datatransfer::dnd::XDragSource, + css::lang::XInitialization, + css::lang::XServiceInfo> +{ + osl::Mutex m_aMutex; + GtkSalFrame* m_pFrame; + css::uno::Reference<css::datatransfer::dnd::XDragSourceListener> m_xListener; + css::uno::Reference<css::datatransfer::XTransferable> m_xTrans; + VclToGtkHelper m_aConversionHelper; +public: + GtkDragSource() : WeakComponentImplHelper( m_aMutex ) {} + virtual ~GtkDragSource(); + + // XDragSource + virtual sal_Bool SAL_CALL isDragImageSupported() throw(std::exception) override; + virtual sal_Int32 SAL_CALL getDefaultCursor(sal_Int8 dragAction) throw(std::exception) override; + virtual void SAL_CALL startDrag( + const css::datatransfer::dnd::DragGestureEvent& trigger, sal_Int8 sourceActions, sal_Int32 cursor, sal_Int32 image, + const css::uno::Reference< css::datatransfer::XTransferable >& transferable, + const css::uno::Reference< css::datatransfer::dnd::XDragSourceListener >& listener) throw(std::exception) override; + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence<css::uno::Any >& rArguments) + throw (css::uno::Exception, std::exception) override; + void deinitialize(); + + 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 dragFailed(); + void dragDelete(); + void dragEnd(GdkDragContext* context); + void dragDataGet(GtkSelectionData *data, guint info); +}; + +#endif + class GtkSalTimer; #if GTK_CHECK_VERSION(3,0,0) class GtkInstance : public SvpSalInstance @@ -94,6 +220,8 @@ public: #if GTK_CHECK_VERSION(3,0,0) virtual css::uno::Reference< css::uno::XInterface > CreateClipboard( const css::uno::Sequence< css::uno::Any >& i_rArguments ) override; + virtual css::uno::Reference< css::uno::XInterface > CreateDragSource() override; + virtual css::uno::Reference< css::uno::XInterface > CreateDropTarget() override; #endif virtual const cairo_font_options_t* GetCairoFontOptions() override; diff --git a/vcl/source/window/mouse.cxx b/vcl/source/window/mouse.cxx index 198e4cde7460..b55e7a01dc72 100644 --- a/vcl/source/window/mouse.cxx +++ b/vcl/source/window/mouse.cxx @@ -750,6 +750,7 @@ Reference< css::datatransfer::dnd::XDragSource > Window::GetDragSource() aDropTargetSN = "com.sun.star.datatransfer.dnd.X11DropTarget"; aDragSourceAL[ 0 ] = makeAny( Application::GetDisplayConnection() ); + aDragSourceAL[ 1 ] = makeAny( (sal_Size)(pEnvData->aShellWindow) ); aDropTargetAL[ 0 ] = makeAny( Application::GetDisplayConnection() ); aDropTargetAL[ 1 ] = makeAny( (sal_Size)(pEnvData->aShellWindow) ); #endif 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 cc664287ea27..af284ca8c3aa 100644 --- a/vcl/unx/gtk3/gtk3gtkframe.cxx +++ b/vcl/unx/gtk3/gtk3gtkframe.cxx @@ -74,6 +74,8 @@ #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/awt/MouseButton.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> @@ -801,6 +803,18 @@ void GtkSalFrame::InvalidateGraphics() GtkSalFrame::~GtkSalFrame() { + if (m_pDropTarget) + { + m_pDropTarget->deinitialize(); + m_pDropTarget = nullptr; + } + + if (m_pDragSource) + { + m_pDragSource->deinitialize(); + m_pDragSource= nullptr; + } + InvalidateGraphics(); if( m_pParent ) @@ -985,6 +999,21 @@ 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 )); + + //Drop Target Stuff + 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 )); + + //Drag Source Stuff + m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-end", G_CALLBACK(signalDragEnd), this )); + m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-failed", G_CALLBACK(signalDragFailed), this )); + m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-delete", G_CALLBACK(signalDragDelete), this )); + m_aMouseSignalIds.push_back(g_signal_connect( G_OBJECT(pEventWidget), "drag-data-get", G_CALLBACK(signalDragDataGet), 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) @@ -1028,6 +1057,10 @@ void GtkSalFrame::InitCommon() m_hBackgroundPixmap = None; m_nExtStyle = 0; m_pRegion = nullptr; + m_pDropTarget = nullptr; + m_pDragSource = nullptr; + m_bInDrag = false; + m_pFormatConversionRequest = nullptr; m_ePointerStyle = static_cast<PointerStyle>(0xffff); m_bSetFocusOnMap = false; m_pSalMenu = nullptr; @@ -1054,8 +1087,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(); @@ -3033,6 +3066,288 @@ 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 + { + GdkDragAction eAct(static_cast<GdkDragAction>(0)); + + if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_MOVE) + eAct = GDK_ACTION_MOVE; + else if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_COPY) + eAct = GDK_ACTION_COPY; + else if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_LINK) + eAct = GDK_ACTION_LINK; + + gdk_drag_status(m_pContext, eAct, 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(nullptr, 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() override + { + 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_selected_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 + { + GdkDragAction eAct(static_cast<GdkDragAction>(0)); + + if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_MOVE) + eAct = GDK_ACTION_MOVE; + else if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_COPY) + eAct = GDK_ACTION_COPY; + else if (dragOperation & css::datatransfer::dnd::DNDConstants::ACTION_LINK) + eAct = GDK_ACTION_LINK; + + gdk_drag_status(m_pContext, eAct, 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); + GtkDropTargetDragContext* pContext = new GtkDropTargetDragContext(context, time); + //preliminary accept the Drag and select the preferred action, the fire_* will + //inform the original caller of our choice and the callsite can decide + //to overrule this choice. i.e. typically here we default to ACTION_MOVE + pContext->acceptDrag(GdkToVcl(gdk_drag_context_get_actions(context))); + aEvent.Context = pContext; + aEvent.LocationX = x; + aEvent.LocationY = y; + aEvent.DropAction = GdkToVcl(gdk_drag_context_get_selected_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); @@ -3618,4 +3933,141 @@ Window GtkSalFrame::GetX11Window() return widget_get_xid(m_pWindow); } +void GtkDragSource::startDrag(const datatransfer::dnd::DragGestureEvent& rEvent, + sal_Int8 sourceActions, sal_Int32 /*cursor*/, sal_Int32 /*image*/, + const css::uno::Reference<css::datatransfer::XTransferable>& rTrans, + const css::uno::Reference<css::datatransfer::dnd::XDragSourceListener>& rListener) throw(std::exception) +{ + m_xListener = rListener; + m_xTrans = rTrans; + + if (m_pFrame) + { + css::uno::Sequence<css::datatransfer::DataFlavor> aFormats = rTrans->getTransferDataFlavors(); + std::vector<GtkTargetEntry> aGtkTargets(m_aConversionHelper.FormatsToGtk(aFormats)); + GtkTargetList *pTargetList = gtk_target_list_new(aGtkTargets.data(), aGtkTargets.size()); + + gint nDragButton = 1; // default to left button + css::awt::MouseEvent aEvent; + if (rEvent.Event >>= aEvent) + { + if (aEvent.Buttons & css::awt::MouseButton::LEFT ) + nDragButton = 1; + else if (aEvent.Buttons & css::awt::MouseButton::RIGHT) + nDragButton = 3; + else if (aEvent.Buttons & css::awt::MouseButton::MIDDLE) + nDragButton = 2; + } + + m_pFrame->startDrag(nDragButton, rEvent.DragOriginX, rEvent.DragOriginY, + VclToGdk(sourceActions), pTargetList); + gtk_target_list_unref(pTargetList); + for (auto &a : aGtkTargets) + g_free(a.target); + } + else + dragFailed(); +} + +void GtkSalFrame::startDrag(gint nButton, gint nDragOriginX, gint nDragOriginY, + GdkDragAction sourceActions, GtkTargetList* pTargetList) +{ + SolarMutexGuard aGuard; + + assert(m_pDragSource); + + GdkEvent aFakeEvent; + memset(&aFakeEvent, 0, sizeof(GdkEvent)); + aFakeEvent.type = GDK_BUTTON_PRESS; + aFakeEvent.button.window = widget_get_window(getMouseEventWidget()); + aFakeEvent.button.time = GDK_CURRENT_TIME; + GdkDeviceManager* pDeviceManager = gdk_display_get_device_manager(getGdkDisplay()); + aFakeEvent.button.device = gdk_device_manager_get_client_pointer(pDeviceManager); + +#if GTK_CHECK_VERSION(3,10,0) + GdkDragContext *pContext = gtk_drag_begin_with_coordinates(getMouseEventWidget(), + pTargetList, + sourceActions, + nButton, + &aFakeEvent, + nDragOriginX, + nDragOriginY); +#else + GdkDragContext *pContext = gtk_drag_begin(getMouseEventWidget(), + pTargetList, + sourceActions, + nButton, + &aFakeEvent); + (void)nDragOriginX; + (void)nDragOriginY; +#endif + + if (!pContext) + m_pDragSource->dragFailed(); +} + +void GtkDragSource::dragFailed() +{ + datatransfer::dnd::DragSourceDropEvent aEv; + aEv.DropAction = datatransfer::dnd::DNDConstants::ACTION_NONE; + aEv.DropSuccess = false; + m_xListener->dragDropEnd(aEv); +} + +gboolean GtkSalFrame::signalDragFailed(GtkWidget* /*widget*/, GdkDragContext* /*context*/, GtkDragResult /*result*/, gpointer frame) +{ + GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); + if (!pThis->m_pDragSource) + return false; + pThis->m_pDragSource->dragFailed(); + return false; +} + +void GtkDragSource::dragDelete() +{ + datatransfer::dnd::DragSourceDropEvent aEv; + aEv.DropAction = datatransfer::dnd::DNDConstants::ACTION_MOVE; + aEv.DropSuccess = true; + m_xListener->dragDropEnd(aEv); +} + +void GtkSalFrame::signalDragDelete(GtkWidget* /*widget*/, GdkDragContext* /*context*/, gpointer frame) +{ + GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); + if (!pThis->m_pDragSource) + return; + pThis->m_pDragSource->dragDelete(); +} + +void GtkDragSource::dragEnd(GdkDragContext* context) +{ + datatransfer::dnd::DragSourceDropEvent aEv; + aEv.DropAction = GdkToVcl(gdk_drag_context_get_selected_action(context)); + aEv.DropSuccess = gdk_drag_drop_succeeded(context); + m_xListener->dragDropEnd(aEv); +} + +void GtkSalFrame::signalDragEnd(GtkWidget* /*widget*/, GdkDragContext* context, gpointer frame) +{ + GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); + if (!pThis->m_pDragSource) + return; + pThis->m_pDragSource->dragEnd(context); +} + +void GtkDragSource::dragDataGet(GtkSelectionData *data, guint info) +{ + m_aConversionHelper.setSelectionData(m_xTrans, data, info); +} + +void GtkSalFrame::signalDragDataGet(GtkWidget* /*widget*/, GdkDragContext* /*context*/, GtkSelectionData *data, guint info, + guint /*time*/, gpointer frame) +{ + GtkSalFrame* pThis = static_cast<GtkSalFrame*>(frame); + if (!pThis->m_pDragSource) + return; + pThis->m_pDragSource->dragDataGet(data, info); +} + + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/unx/gtk3/gtk3gtkinst.cxx b/vcl/unx/gtk3/gtk3gtkinst.cxx index a3f30683cbdb..f914e4546f6a 100644 --- a/vcl/unx/gtk3/gtk3gtkinst.cxx +++ b/vcl/unx/gtk3/gtk3gtkinst.cxx @@ -20,8 +20,6 @@ #include "com/sun/star/datatransfer/clipboard/XClipboardListener.hpp" #include "com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp" #include "com/sun/star/datatransfer/clipboard/XSystemClipboard.hpp" -#include "com/sun/star/datatransfer/dnd/XDragSource.hpp" -#include "com/sun/star/datatransfer/dnd/XDropTarget.hpp" #include "com/sun/star/datatransfer/dnd/DNDConstants.hpp" #include <comphelper/processfactory.hxx> #include <comphelper/sequence.hxx> @@ -93,15 +91,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) { } @@ -146,6 +216,7 @@ public: } std::vector<css::datatransfer::DataFlavor> getTransferDataFlavorsAsVector() + override { std::vector<css::datatransfer::DataFlavor> aVector; @@ -155,77 +226,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 +292,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 +307,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 +349,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 +374,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; } @@ -393,12 +384,53 @@ void VclGtkClipboard::ClipboardGet(GtkClipboard* /*clipboard*/, GtkSelectionData { if (!m_aContents.is()) return; + m_aConversionHelper.setSelectionData(m_aContents, selection_data, info); +} - GdkAtom type(gdk_atom_intern(OUStringToOString(m_aInfoToFlavor[info].MimeType, +void VclGtkClipboard::OwnerChanged(GtkClipboard* clipboard, GdkEvent* /*event*/) +{ + if (G_OBJECT(m_pOwner) != gtk_clipboard_get_owner(clipboard)) + { + //null out m_aContents to return control to the system-one which + //will be retrieved if getContents is called again + setContents(Reference<css::datatransfer::XTransferable>(), + Reference<css::datatransfer::clipboard::XClipboardOwner>()); + } +} + +void VclGtkClipboard::ClipboardClear(GtkClipboard * /*clipboard*/) +{ + for (auto &a : m_aGtkTargets) + g_free(a.target); + m_aGtkTargets.clear(); +} + +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(aInfoToFlavor.begin(), aInfoToFlavor.end(), + DataFlavorEq(rFlavor)); + if (it != aInfoToFlavor.end()) + aEntry.info = std::distance(aInfoToFlavor.begin(), it); + else + { + aEntry.info = aInfoToFlavor.size(); + aInfoToFlavor.push_back(rFlavor); + } + return aEntry; +} + +void VclToGtkHelper::setSelectionData(const Reference<css::datatransfer::XTransferable> &rTrans, + GtkSelectionData *selection_data, guint info) +{ + GdkAtom type(gdk_atom_intern(OUStringToOString(aInfoToFlavor[info].MimeType, RTL_TEXTENCODING_UTF8).getStr(), false)); - css::datatransfer::DataFlavor aFlavor(m_aInfoToFlavor[info]); + css::datatransfer::DataFlavor aFlavor(aInfoToFlavor[info]); if (aFlavor.MimeType == "UTF8_STRING" || aFlavor.MimeType == "STRING") aFlavor.MimeType = "text/plain;charset=utf-8"; @@ -407,9 +439,9 @@ void VclGtkClipboard::ClipboardGet(GtkClipboard* /*clipboard*/, GtkSelectionData try { - aValue = m_aContents->getTransferData(aFlavor); + aValue = rTrans->getTransferData(aFlavor); } - catch(...) + catch (...) { } @@ -430,9 +462,9 @@ void VclGtkClipboard::ClipboardGet(GtkClipboard* /*clipboard*/, GtkSelectionData aFlavor.DataType = cppu::UnoType<OUString>::get(); try { - aValue = m_aContents->getTransferData(aFlavor); + aValue = rTrans->getTransferData(aFlavor); } - catch(...) + catch (...) { } OUString aString; @@ -449,42 +481,6 @@ void VclGtkClipboard::ClipboardGet(GtkClipboard* /*clipboard*/, GtkSelectionData aData.getLength()); } -void VclGtkClipboard::OwnerChanged(GtkClipboard* clipboard, GdkEvent* /*event*/) -{ - if (G_OBJECT(m_pOwner) != gtk_clipboard_get_owner(clipboard)) - { - //null out m_aContents to return control to the system-one which - //will be retrieved if getContents is called again - setContents(Reference<css::datatransfer::XTransferable>(), - Reference<css::datatransfer::clipboard::XClipboardOwner>()); - } -} - -void VclGtkClipboard::ClipboardClear(GtkClipboard * /*clipboard*/) -{ - for (auto &a : m_aGtkTargets) - g_free(a.target); - m_aGtkTargets.clear(); -} - -GtkTargetEntry VclGtkClipboard::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(), - DataFlavorEq(rFlavor)); - if (it != m_aInfoToFlavor.end()) - aEntry.info = std::distance(m_aInfoToFlavor.begin(), it); - else - { - aEntry.info = m_aInfoToFlavor.size(); - m_aInfoToFlavor.push_back(rFlavor); - } - return aEntry; -} - namespace { void ClipboardGetFunc(GtkClipboard *clipboard, GtkSelectionData *selection_data, @@ -539,6 +535,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 +593,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 @@ -604,6 +607,10 @@ void VclGtkClipboard::setContents( ClipboardGetFunc, ClipboardClearFunc, G_OBJECT(m_pOwner)); gtk_clipboard_set_can_store(clipboard, aGtkTargets.data(), aGtkTargets.size()); } + + for (auto &a : m_aGtkTargets) + g_free(a.target); + m_aGtkTargets = aGtkTargets; } @@ -662,4 +669,218 @@ 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 > GtkInstance::CreateDropTarget() +{ + return Reference< XInterface >( static_cast<cppu::OWeakObject *>(new GtkDropTarget()) ); +} + +GtkDragSource::~GtkDragSource() +{ + if (m_pFrame) + m_pFrame->deregisterDragSource(this); +} + +void GtkDragSource::deinitialize() +{ + m_pFrame = nullptr; +} + +sal_Bool GtkDragSource::isDragImageSupported() throw(std::exception) +{ + return true; +} + +sal_Int32 GtkDragSource::getDefaultCursor( sal_Int8 ) throw(std::exception) +{ + return 0; +} + +void GtkDragSource::initialize(const css::uno::Sequence<css::uno::Any >& rArguments) throw(Exception, std::exception) +{ + if (rArguments.getLength() < 2) + { + throw RuntimeException("DragSource::initialize: Cannot install window event handler", + static_cast<OWeakObject*>(this)); + } + + sal_Size nFrame = 0; + rArguments.getConstArray()[1] >>= nFrame; + + if (!nFrame) + { + throw RuntimeException("DragSource::initialize: missing SalFrame", + static_cast<OWeakObject*>(this)); + } + + m_pFrame = reinterpret_cast<GtkSalFrame*>(nFrame); + m_pFrame->registerDragSource(this); +} + +OUString SAL_CALL GtkDragSource::getImplementationName() + throw (css::uno::RuntimeException, std::exception) +{ + return OUString("com.sun.star.datatransfer.dnd.VclGtkDragSource"); +} + +sal_Bool SAL_CALL GtkDragSource::supportsService(OUString const & ServiceName) + throw (css::uno::RuntimeException, std::exception) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence<OUString> SAL_CALL GtkDragSource::getSupportedServiceNames() + throw (css::uno::RuntimeException, std::exception) +{ + Sequence<OUString> aRet { "com.sun.star.datatransfer.dnd.GtkDragSource" }; + return aRet; +} + +Reference< XInterface > GtkInstance::CreateDragSource() +{ + return Reference< XInterface >( static_cast<cppu::OWeakObject *>(new GtkDragSource()) ); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |