summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaolán McNamara <caolanm@redhat.com>2021-05-31 12:26:18 +0100
committerCaolán McNamara <caolanm@redhat.com>2021-05-31 15:06:40 +0200
commitbf571610d34111198b98bae30042fb23a4c0f4a4 (patch)
tree526642a8b2e0c9db19abc39c41ee5afb2aa1a0bf
parentUpdate git submodules (diff)
downloadcore-bf571610d34111198b98bae30042fb23a4c0f4a4.tar.gz
core-bf571610d34111198b98bae30042fb23a4c0f4a4.zip
gtk4: enable cut and paste into other applications
Change-Id: I9924a9af10532254dc368e274e4a20c6706e4239 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/116445 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r--compilerplugins/clang/reservedid.cxx2
-rw-r--r--vcl/inc/unx/gtk/gtkinst.hxx15
-rw-r--r--vcl/unx/gtk3/gtkinst.cxx264
3 files changed, 255 insertions, 26 deletions
diff --git a/compilerplugins/clang/reservedid.cxx b/compilerplugins/clang/reservedid.cxx
index 169942f9c392..0792c96b2da6 100644
--- a/compilerplugins/clang/reservedid.cxx
+++ b/compilerplugins/clang/reservedid.cxx
@@ -197,6 +197,8 @@ bool ReservedId::VisitNamedDecl(NamedDecl const * decl) {
// connectivity/source/inc/ado/Awrapadox.hxx, MS SDK adoctint.h
&& s != "_ADOUser"
// connectivity/source/inc/ado/Awrapadox.hxx, MS SDK adoctint.h
+ && s != "_ClipboardContent" // vcl/unx/gtk3/gtkinst.cxx
+ && s != "_ClipboardContentClass" // vcl/unx/gtk3/gtkinst.cxx
&& s != "_FcPattern" // vcl/inc/unx/fc_fontoptions.hxx
&& s != "_GdkDisplay"
// vcl/unx/gtk/xid_fullscreen_on_all_monitors.c
diff --git a/vcl/inc/unx/gtk/gtkinst.hxx b/vcl/inc/unx/gtk/gtkinst.hxx
index f389c6433b9c..3aaa03d1ddb6 100644
--- a/vcl/inc/unx/gtk/gtkinst.hxx
+++ b/vcl/inc/unx/gtk/gtkinst.hxx
@@ -65,12 +65,23 @@ struct VclToGtkHelper
#else
std::vector<GtkTargetEntry> FormatsToGtk(const css::uno::Sequence<css::datatransfer::DataFlavor> &rFormats);
#endif
-#if !GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
+ void setSelectionData(const css::uno::Reference<css::datatransfer::XTransferable> &rTrans,
+ GdkContentProvider* provider,
+ const char* mime_type,
+ GOutputStream* stream,
+ int io_priority,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+#else
void setSelectionData(const css::uno::Reference<css::datatransfer::XTransferable> &rTrans,
GtkSelectionData *selection_data, guint info);
#endif
private:
-#if !GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
+ OString makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor);
+#else
GtkTargetEntry makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor);
#endif
};
diff --git a/vcl/unx/gtk3/gtkinst.cxx b/vcl/unx/gtk3/gtkinst.cxx
index a60f33790505..43e419aa2305 100644
--- a/vcl/unx/gtk3/gtkinst.cxx
+++ b/vcl/unx/gtk3/gtkinst.cxx
@@ -889,7 +889,16 @@ public:
virtual void SAL_CALL removeClipboardListener(
const Reference< css::datatransfer::clipboard::XClipboardListener >& listener ) override;
-#if !GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
+ GdkContentFormats* ref_formats();
+ void write_mime_type_async(GdkContentProvider* provider,
+ const char* mime_type,
+ GOutputStream* stream,
+ int io_priority,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+#else
void ClipboardGet(GtkSelectionData *selection_data, guint info);
#endif
void OwnerPossiblyChanged(GdkClipboard *clipboard);
@@ -927,7 +936,25 @@ Reference< css::datatransfer::XTransferable > VclGtkClipboard::getContents()
return m_aContents;
}
-#if !GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
+void VclGtkClipboard::write_mime_type_async(GdkContentProvider* provider,
+ const char* mime_type,
+ GOutputStream* stream,
+ int io_priority,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ if (!m_aContents.is())
+ return;
+ // tdf#129809 take a reference in case m_aContents is replaced during this
+ // call
+ Reference<datatransfer::XTransferable> xCurrentContents(m_aContents);
+ m_aConversionHelper.setSelectionData(xCurrentContents, provider, mime_type,
+ stream, io_priority, cancellable,
+ callback, user_data);
+}
+#else
void VclGtkClipboard::ClipboardGet(GtkSelectionData *selection_data, guint info)
{
if (!m_aContents.is())
@@ -991,6 +1018,9 @@ void VclGtkClipboard::OwnerPossiblyChanged(GdkClipboard* clipboard)
if (!m_aContents.is())
return;
+#if GTK_CHECK_VERSION(4, 0, 0)
+ bool bSelf = gdk_clipboard_is_local(clipboard);
+#else
//if gdk_display_supports_selection_notification is not supported, e.g. like
//right now under wayland, then you only get owner-changed notifications at
//opportune times when the selection might have changed. So here
@@ -1029,6 +1059,7 @@ void VclGtkClipboard::OwnerPossiblyChanged(GdkClipboard* clipboard)
m_nOwnerChangedSignalId = g_signal_connect(clipboard, "owner-change",
G_CALLBACK(handle_owner_change), this);
#endif
+#endif
if (!bSelf)
{
@@ -1053,7 +1084,17 @@ void VclGtkClipboard::ClipboardClear()
m_aGtkTargets.clear();
}
-#if !GTK_CHECK_VERSION(4, 0, 0)
+#if GTK_CHECK_VERSION(4, 0, 0)
+OString VclToGtkHelper::makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor)
+{
+ OString aEntry = OUStringToOString(rFlavor.MimeType, RTL_TEXTENCODING_UTF8);
+ auto it = std::find_if(aInfoToFlavor.begin(), aInfoToFlavor.end(),
+ DataFlavorEq(rFlavor));
+ if (it == aInfoToFlavor.end())
+ aInfoToFlavor.push_back(rFlavor);
+ return aEntry;
+}
+#else
GtkTargetEntry VclToGtkHelper::makeGtkTargetEntry(const css::datatransfer::DataFlavor& rFlavor)
{
GtkTargetEntry aEntry;
@@ -1071,8 +1112,119 @@ GtkTargetEntry VclToGtkHelper::makeGtkTargetEntry(const css::datatransfer::DataF
}
return aEntry;
}
+#endif
+
+#if GTK_CHECK_VERSION(4, 0, 0)
+
+namespace
+{
+ void write_mime_type_done(GObject* pStream, GAsyncResult* pResult, gpointer pTaskPtr)
+ {
+ GTask* pTask = static_cast<GTask*>(pTaskPtr);
+
+ GError* pError = nullptr;
+ if (!g_output_stream_write_all_finish(G_OUTPUT_STREAM(pStream),
+ pResult, nullptr, &pError))
+ {
+ g_task_return_error(pTask, pError);
+ }
+ else
+ {
+ g_task_return_boolean(pTask, true);
+ }
+
+ g_object_unref(pTask);
+ }
+
+ class MimeTypeEq
+ {
+ private:
+ const OUString& m_rMimeType;
+ public:
+ explicit MimeTypeEq(const OUString& rMimeType) : m_rMimeType(rMimeType) {}
+ bool operator() (const css::datatransfer::DataFlavor& rData) const
+ {
+ return rData.MimeType == m_rMimeType;
+ }
+ };
+}
void VclToGtkHelper::setSelectionData(const Reference<css::datatransfer::XTransferable> &rTrans,
+ GdkContentProvider* provider,
+ const char* mime_type,
+ GOutputStream* stream,
+ int io_priority,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task = g_task_new(provider, cancellable, callback, user_data);
+ g_task_set_priority(task, io_priority);
+
+ OUString sMimeType(mime_type, strlen(mime_type), RTL_TEXTENCODING_UTF8);
+
+ auto it = std::find_if(aInfoToFlavor.begin(), aInfoToFlavor.end(),
+ MimeTypeEq(sMimeType));
+ if (it == aInfoToFlavor.end())
+ {
+ SAL_WARN( "vcl.gtk", "unknown mime-type request from clipboard");
+ g_task_return_new_error(task, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ "unknown mime-type “%s” request from clipboard", mime_type);
+ g_object_unref(task);
+ return;
+ }
+
+ css::datatransfer::DataFlavor aFlavor(*it);
+ if (aFlavor.MimeType == "UTF8_STRING" || aFlavor.MimeType == "STRING")
+ aFlavor.MimeType = "text/plain;charset=utf-8";
+
+ Sequence<sal_Int8> aData;
+ Any aValue;
+
+ try
+ {
+ aValue = rTrans->getTransferData(aFlavor);
+ }
+ catch (...)
+ {
+ }
+
+ if (aValue.getValueTypeClass() == TypeClass_STRING)
+ {
+ OUString aString;
+ aValue >>= aString;
+ aData = Sequence< sal_Int8 >( reinterpret_cast<sal_Int8 const *>(aString.getStr()), aString.getLength() * sizeof( sal_Unicode ) );
+ }
+ else if (aValue.getValueType() == cppu::UnoType<Sequence< sal_Int8 >>::get())
+ {
+ aValue >>= aData;
+ }
+ else if (aFlavor.MimeType == "text/plain;charset=utf-8")
+ {
+ //didn't have utf-8, try utf-16 and convert
+ aFlavor.MimeType = "text/plain;charset=utf-16";
+ aFlavor.DataType = cppu::UnoType<OUString>::get();
+ try
+ {
+ aValue = rTrans->getTransferData(aFlavor);
+ }
+ catch (...)
+ {
+ }
+ OUString aString;
+ aValue >>= aString;
+ OString aUTF8String(OUStringToOString(aString, RTL_TEXTENCODING_UTF8));
+
+ g_output_stream_write_all_async(stream, aUTF8String.getStr(), aUTF8String.getLength(),
+ io_priority, cancellable, write_mime_type_done, task);
+ return;
+ }
+
+ g_output_stream_write_all_async(stream, aData.getArray(), aData.getLength(),
+ io_priority, cancellable, write_mime_type_done, task);
+}
+#else
+void VclToGtkHelper::setSelectionData(const Reference<css::datatransfer::XTransferable> &rTrans,
GtkSelectionData *selection_data, guint info)
{
GdkAtom type(gdk_atom_intern(OUStringToOString(aInfoToFlavor[info].MimeType,
@@ -1203,38 +1355,22 @@ std::vector<GtkTargetEntry> VclToGtkHelper::FormatsToGtk(const css::uno::Sequenc
bHaveUTF8 = true;
}
}
-#if GTK_CHECK_VERSION(4, 0, 0)
- aGtkTargets.push_back(OUStringToOString(rFlavor.MimeType, RTL_TEXTENCODING_UTF8));
-#else
- GtkTargetEntry aEntry(makeGtkTargetEntry(rFlavor));
- aGtkTargets.push_back(aEntry);
-#endif
+ aGtkTargets.push_back(makeGtkTargetEntry(rFlavor));
}
if (bHaveText)
{
-#if !GTK_CHECK_VERSION(4, 0, 0)
css::datatransfer::DataFlavor aFlavor;
aFlavor.DataType = cppu::UnoType<Sequence< sal_Int8 >>::get();
-#endif
if (!bHaveUTF8)
{
-#if GTK_CHECK_VERSION(4, 0, 0)
- aGtkTargets.push_back("text/plain;charset=utf-8");
-#else
aFlavor.MimeType = "text/plain;charset=utf-8";
aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
-#endif
}
-#if GTK_CHECK_VERSION(4, 0, 0)
- aGtkTargets.push_back("UTF8_STRING");
- aGtkTargets.push_back("STRING");
-#else
aFlavor.MimeType = "UTF8_STRING";
aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
aFlavor.MimeType = "STRING";
aGtkTargets.push_back(makeGtkTargetEntry(aFlavor));
-#endif
}
return aGtkTargets;
@@ -1258,15 +1394,95 @@ void VclGtkClipboard::SyncGtkClipboard()
}
}
-void VclGtkClipboard::SetGtkClipboard()
+#if GTK_CHECK_VERSION(4, 0, 0)
+
+G_BEGIN_DECLS
+
+#define CLIPBOARD_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), clipboard_content_get_type(), ClipboardContent))
+
+typedef struct _ClipboardContent ClipboardContent;
+typedef struct _ClipboardContentClass ClipboardContentClass;
+
+struct _ClipboardContent
{
- GdkClipboard* clipboard = clipboard_get(m_eSelection);
+ GdkContentProvider parent;
+ VclGtkClipboard* clipboard;
+};
+
+struct _ClipboardContentClass
+{
+ GdkContentProviderClass parent_class;
+};
+
+GType clipboard_content_get_type();
+
+G_DEFINE_TYPE(ClipboardContent, clipboard_content, GDK_TYPE_CONTENT_PROVIDER)
+
+static void clipboard_content_write_mime_type_async(GdkContentProvider* provider,
+ const char* mime_type,
+ GOutputStream* stream,
+ int io_priority,
+ GCancellable* cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ClipboardContent *content = CLIPBOARD_CONTENT(provider);
+ content->clipboard->write_mime_type_async(provider, mime_type, stream, io_priority,
+ cancellable, callback, user_data);
+}
+
+static gboolean clipboard_content_write_mime_type_finish(GdkContentProvider*,
+ GAsyncResult* result,
+ GError** error)
+{
+ return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+static GdkContentFormats* clipboard_content_ref_formats(GdkContentProvider *provider)
+{
+ ClipboardContent *content = CLIPBOARD_CONTENT(provider);
+ return content->clipboard->ref_formats();
+}
+
+static void clipboard_content_class_init(ClipboardContentClass* klass)
+{
+ GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS(klass);
+
+ provider_class->ref_formats = clipboard_content_ref_formats;
+ provider_class->write_mime_type_async = clipboard_content_write_mime_type_async;
+ provider_class->write_mime_type_finish = clipboard_content_write_mime_type_finish;
+}
+
+static void clipboard_content_init(ClipboardContent*)
+{
+}
+
+static GdkContentProvider* clipboard_content_new(VclGtkClipboard* pClipboard)
+{
+ ClipboardContent *content = CLIPBOARD_CONTENT(g_object_new(clipboard_content_get_type(), nullptr));
+ content->clipboard = pClipboard;
+ return GDK_CONTENT_PROVIDER(content);
+}
+
+G_END_DECLS
+
+#endif
+
#if GTK_CHECK_VERSION(4, 0, 0)
+GdkContentFormats* VclGtkClipboard::ref_formats()
+{
GdkContentFormatsBuilder* pBuilder = gdk_content_formats_builder_new();
for (const auto& rFormat : m_aGtkTargets)
gdk_content_formats_builder_add_mime_type(pBuilder, rFormat.getStr());
- GdkContentFormats* pFormats = gdk_content_formats_builder_free_to_formats(pBuilder);
- //TODO pFormats needs a place to call home
+ return gdk_content_formats_builder_free_to_formats(pBuilder);
+}
+#endif
+
+void VclGtkClipboard::SetGtkClipboard()
+{
+ GdkClipboard* clipboard = clipboard_get(m_eSelection);
+#if GTK_CHECK_VERSION(4, 0, 0)
+ gdk_clipboard_set_content(clipboard, clipboard_content_new(this));
#else
gtk_clipboard_set_with_data(clipboard, m_aGtkTargets.data(), m_aGtkTargets.size(),
ClipboardGetFunc, ClipboardClearFunc, this);