1 | /* GDK - The GIMP Drawing Kit |
2 | * |
3 | * Copyright (C) 2017 Benjamin Otte <otte@gnome.org> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
17 | */ |
18 | |
19 | #include "config.h" |
20 | |
21 | #include "gdkclipboardprivate.h" |
22 | |
23 | #include "gdkcontentdeserializer.h" |
24 | #include "gdkcontentformats.h" |
25 | #include "gdkcontentproviderimpl.h" |
26 | #include "gdkcontentproviderprivate.h" |
27 | #include "gdkcontentserializer.h" |
28 | #include "gdkdisplay.h" |
29 | #include "gdkintl.h" |
30 | #include "gdkpipeiostreamprivate.h" |
31 | #include "gdktexture.h" |
32 | |
33 | #include <gobject/gvaluecollector.h> |
34 | |
35 | /** |
36 | * GdkClipboard: |
37 | * |
38 | * The `GdkClipboard` object represents data shared between applications or |
39 | * inside an application. |
40 | * |
41 | * To get a `GdkClipboard` object, use [method@Gdk.Display.get_clipboard] or |
42 | * [method@Gdk.Display.get_primary_clipboard]. You can find out about the data |
43 | * that is currently available in a clipboard using |
44 | * [method@Gdk.Clipboard.get_formats]. |
45 | * |
46 | * To make text or image data available in a clipboard, use |
47 | * [method@Gdk.Clipboard.set_text] or [method@Gdk.Clipboard.set_texture]. |
48 | * For other data, you can use [method@Gdk.Clipboard.set_content], which |
49 | * takes a [class@Gdk.ContentProvider] object. |
50 | * |
51 | * To read textual or image data from a clipboard, use |
52 | * [method@Gdk.Clipboard.read_text_async] or |
53 | * [method@Gdk.Clipboard.read_texture_async]. For other data, use |
54 | * [method@Gdk.Clipboard.read_async], which provides a `GInputStream` object. |
55 | */ |
56 | |
57 | typedef struct _GdkClipboardPrivate GdkClipboardPrivate; |
58 | |
59 | struct _GdkClipboardPrivate |
60 | { |
61 | GdkDisplay *display; |
62 | GdkContentFormats *formats; |
63 | GdkContentProvider *content; |
64 | |
65 | guint local : 1; |
66 | }; |
67 | |
68 | enum { |
69 | PROP_0, |
70 | PROP_DISPLAY, |
71 | PROP_FORMATS, |
72 | PROP_LOCAL, |
73 | PROP_CONTENT, |
74 | N_PROPERTIES |
75 | }; |
76 | |
77 | enum { |
78 | CHANGED, |
79 | N_SIGNALS |
80 | }; |
81 | |
82 | static GParamSpec *properties[N_PROPERTIES] = { NULL, }; |
83 | static guint signals[N_SIGNALS] = { 0 }; |
84 | |
85 | G_DEFINE_TYPE_WITH_PRIVATE (GdkClipboard, gdk_clipboard, G_TYPE_OBJECT) |
86 | |
87 | static void |
88 | gdk_clipboard_set_property (GObject *gobject, |
89 | guint prop_id, |
90 | const GValue *value, |
91 | GParamSpec *pspec) |
92 | { |
93 | GdkClipboard *clipboard = GDK_CLIPBOARD (gobject); |
94 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
95 | |
96 | switch (prop_id) |
97 | { |
98 | case PROP_DISPLAY: |
99 | priv->display = g_value_get_object (value); |
100 | g_assert (priv->display != NULL); |
101 | break; |
102 | |
103 | default: |
104 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
105 | break; |
106 | } |
107 | } |
108 | |
109 | static void |
110 | gdk_clipboard_get_property (GObject *gobject, |
111 | guint prop_id, |
112 | GValue *value, |
113 | GParamSpec *pspec) |
114 | { |
115 | GdkClipboard *clipboard = GDK_CLIPBOARD (gobject); |
116 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
117 | |
118 | switch (prop_id) |
119 | { |
120 | case PROP_DISPLAY: |
121 | g_value_set_object (value, v_object: priv->display); |
122 | break; |
123 | |
124 | case PROP_FORMATS: |
125 | g_value_set_boxed (value, v_boxed: priv->formats); |
126 | break; |
127 | |
128 | case PROP_CONTENT: |
129 | g_value_set_object (value, v_object: priv->content); |
130 | break; |
131 | |
132 | case PROP_LOCAL: |
133 | g_value_set_boolean (value, v_boolean: priv->local); |
134 | break; |
135 | |
136 | default: |
137 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
138 | break; |
139 | } |
140 | } |
141 | |
142 | static void |
143 | gdk_clipboard_finalize (GObject *object) |
144 | { |
145 | GdkClipboard *clipboard = GDK_CLIPBOARD (object); |
146 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
147 | |
148 | g_clear_pointer (&priv->formats, gdk_content_formats_unref); |
149 | |
150 | G_OBJECT_CLASS (gdk_clipboard_parent_class)->finalize (object); |
151 | } |
152 | |
153 | static void |
154 | gdk_clipboard_content_changed_cb (GdkContentProvider *provider, |
155 | GdkClipboard *clipboard); |
156 | |
157 | static gboolean |
158 | gdk_clipboard_real_claim (GdkClipboard *clipboard, |
159 | GdkContentFormats *formats, |
160 | gboolean local, |
161 | GdkContentProvider *content) |
162 | { |
163 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
164 | |
165 | g_object_freeze_notify (G_OBJECT (clipboard)); |
166 | |
167 | gdk_content_formats_unref (formats: priv->formats); |
168 | gdk_content_formats_ref (formats); |
169 | formats = gdk_content_formats_union_deserialize_gtypes (formats); |
170 | priv->formats = formats; |
171 | g_object_notify_by_pspec (G_OBJECT (clipboard), pspec: properties[PROP_FORMATS]); |
172 | if (priv->local != local) |
173 | { |
174 | priv->local = local; |
175 | g_object_notify_by_pspec (G_OBJECT (clipboard), pspec: properties[PROP_LOCAL]); |
176 | } |
177 | |
178 | if (priv->content != content) |
179 | { |
180 | GdkContentProvider *old_content = priv->content; |
181 | |
182 | if (content) |
183 | priv->content = g_object_ref (content); |
184 | else |
185 | priv->content = NULL; |
186 | |
187 | if (old_content) |
188 | { |
189 | g_signal_handlers_disconnect_by_func (old_content, |
190 | gdk_clipboard_content_changed_cb, |
191 | clipboard); |
192 | gdk_content_provider_detach_clipboard (provider: old_content, clipboard); |
193 | g_object_unref (object: old_content); |
194 | } |
195 | if (content) |
196 | { |
197 | gdk_content_provider_attach_clipboard (provider: content, clipboard); |
198 | g_signal_connect (content, |
199 | "content-changed" , |
200 | G_CALLBACK (gdk_clipboard_content_changed_cb), |
201 | clipboard); |
202 | } |
203 | |
204 | g_object_notify_by_pspec (G_OBJECT (clipboard), pspec: properties[PROP_CONTENT]); |
205 | } |
206 | |
207 | g_object_thaw_notify (G_OBJECT (clipboard)); |
208 | |
209 | g_signal_emit (instance: clipboard, signal_id: signals[CHANGED], detail: 0); |
210 | |
211 | return TRUE; |
212 | } |
213 | |
214 | static void |
215 | gdk_clipboard_store_default_async (GdkClipboard *clipboard, |
216 | int io_priority, |
217 | GCancellable *cancellable, |
218 | GAsyncReadyCallback callback, |
219 | gpointer user_data) |
220 | { |
221 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
222 | GTask *task; |
223 | |
224 | task = g_task_new (source_object: clipboard, cancellable, callback, callback_data: user_data); |
225 | g_task_set_priority (task, priority: io_priority); |
226 | g_task_set_source_tag (task, gdk_clipboard_store_default_async); |
227 | |
228 | if (priv->local) |
229 | { |
230 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED, |
231 | _("This clipboard cannot store data." )); |
232 | } |
233 | else |
234 | { |
235 | g_task_return_boolean (task, TRUE); |
236 | } |
237 | |
238 | g_object_unref (object: task); |
239 | } |
240 | |
241 | static gboolean |
242 | gdk_clipboard_store_default_finish (GdkClipboard *clipboard, |
243 | GAsyncResult *result, |
244 | GError **error) |
245 | { |
246 | g_return_val_if_fail (g_task_is_valid (result, clipboard), FALSE); |
247 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_store_default_async, FALSE); |
248 | |
249 | return g_task_propagate_boolean (G_TASK (result), error); |
250 | } |
251 | |
252 | static void |
253 | gdk_clipboard_read_local_write_done (GObject *clipboard, |
254 | GAsyncResult *result, |
255 | gpointer stream) |
256 | { |
257 | /* we don't care about the error, we just want to clean up */ |
258 | gdk_clipboard_write_finish (GDK_CLIPBOARD (clipboard), result, NULL); |
259 | |
260 | /* XXX: Do we need to close_async() here? */ |
261 | g_output_stream_close (stream, NULL, NULL); |
262 | |
263 | g_object_unref (object: stream); |
264 | } |
265 | |
266 | static void |
267 | gdk_clipboard_read_local_async (GdkClipboard *clipboard, |
268 | GdkContentFormats *formats, |
269 | int io_priority, |
270 | GCancellable *cancellable, |
271 | GAsyncReadyCallback callback, |
272 | gpointer user_data) |
273 | { |
274 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
275 | GdkContentFormats *content_formats; |
276 | const char *mime_type; |
277 | GTask *task; |
278 | |
279 | task = g_task_new (source_object: clipboard, cancellable, callback, callback_data: user_data); |
280 | g_task_set_priority (task, priority: io_priority); |
281 | g_task_set_source_tag (task, gdk_clipboard_read_local_async); |
282 | |
283 | if (priv->content == NULL) |
284 | { |
285 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_FOUND, |
286 | _("Cannot read from empty clipboard." )); |
287 | g_object_unref (object: task); |
288 | return; |
289 | } |
290 | |
291 | content_formats = gdk_content_provider_ref_formats (provider: priv->content); |
292 | content_formats = gdk_content_formats_union_serialize_mime_types (formats: content_formats); |
293 | mime_type = gdk_content_formats_match_mime_type (first: content_formats, second: formats); |
294 | |
295 | if (mime_type != NULL) |
296 | { |
297 | GOutputStream *output_stream; |
298 | GIOStream *stream; |
299 | |
300 | stream = gdk_pipe_io_stream_new (); |
301 | output_stream = g_io_stream_get_output_stream (stream); |
302 | gdk_clipboard_write_async (clipboard, |
303 | mime_type, |
304 | stream: output_stream, |
305 | io_priority, |
306 | cancellable, |
307 | callback: gdk_clipboard_read_local_write_done, |
308 | g_object_ref (output_stream)); |
309 | g_task_set_task_data (task, task_data: (gpointer) mime_type, NULL); |
310 | g_task_return_pointer (task, g_object_ref (g_io_stream_get_input_stream (stream)), result_destroy: g_object_unref); |
311 | |
312 | g_object_unref (object: stream); |
313 | } |
314 | else |
315 | { |
316 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED, |
317 | _("No compatible formats to transfer clipboard contents." )); |
318 | } |
319 | |
320 | gdk_content_formats_unref (formats: content_formats); |
321 | g_object_unref (object: task); |
322 | } |
323 | |
324 | static GInputStream * |
325 | gdk_clipboard_read_local_finish (GdkClipboard *clipboard, |
326 | GAsyncResult *result, |
327 | const char **out_mime_type, |
328 | GError **error) |
329 | { |
330 | g_return_val_if_fail (g_task_is_valid (result, clipboard), NULL); |
331 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_read_local_async, NULL); |
332 | |
333 | if (out_mime_type) |
334 | *out_mime_type = g_task_get_task_data (G_TASK (result)); |
335 | |
336 | return g_task_propagate_pointer (G_TASK (result), error); |
337 | } |
338 | |
339 | static void |
340 | gdk_clipboard_class_init (GdkClipboardClass *class) |
341 | { |
342 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
343 | |
344 | object_class->get_property = gdk_clipboard_get_property; |
345 | object_class->set_property = gdk_clipboard_set_property; |
346 | object_class->finalize = gdk_clipboard_finalize; |
347 | |
348 | class->claim = gdk_clipboard_real_claim; |
349 | class->store_async = gdk_clipboard_store_default_async; |
350 | class->store_finish = gdk_clipboard_store_default_finish; |
351 | class->read_async = gdk_clipboard_read_local_async; |
352 | class->read_finish = gdk_clipboard_read_local_finish; |
353 | |
354 | /** |
355 | * GdkClipboard:display: (attributes org.gtk.Property.get=gdk_clipboard_get_display) |
356 | * |
357 | * The `GdkDisplay` that the clipboard belongs to. |
358 | */ |
359 | properties[PROP_DISPLAY] = |
360 | g_param_spec_object (name: "display" , |
361 | nick: "Display" , |
362 | blurb: "Display owning this clipboard" , |
363 | GDK_TYPE_DISPLAY, |
364 | flags: G_PARAM_READWRITE | |
365 | G_PARAM_CONSTRUCT_ONLY | |
366 | G_PARAM_STATIC_STRINGS | |
367 | G_PARAM_EXPLICIT_NOTIFY); |
368 | |
369 | /** |
370 | * GdkClipboard:formats: (attributes org.gtk.Property.get=gdk_clipboard_get_formats) |
371 | * |
372 | * The possible formats that the clipboard can provide its data in. |
373 | */ |
374 | properties[PROP_FORMATS] = |
375 | g_param_spec_boxed (name: "formats" , |
376 | nick: "Formats" , |
377 | blurb: "The possible formats for data" , |
378 | GDK_TYPE_CONTENT_FORMATS, |
379 | flags: G_PARAM_READABLE | |
380 | G_PARAM_STATIC_STRINGS | |
381 | G_PARAM_EXPLICIT_NOTIFY); |
382 | |
383 | /** |
384 | * GdkClipboard:local: (attributes org.gtk.Property.get=gdk_clipboard_is_local) |
385 | * |
386 | * %TRUE if the contents of the clipboard are owned by this process. |
387 | */ |
388 | properties[PROP_LOCAL] = |
389 | g_param_spec_boolean (name: "local" , |
390 | nick: "Local" , |
391 | blurb: "If the contents are owned by this process" , |
392 | TRUE, |
393 | flags: G_PARAM_READABLE | |
394 | G_PARAM_STATIC_STRINGS | |
395 | G_PARAM_EXPLICIT_NOTIFY); |
396 | |
397 | /** |
398 | * GdkClipboard:content: (attributes org.gtk.Property.get=gdk_clipboard_get_content) |
399 | * |
400 | * The `GdkContentProvider` or %NULL if the clipboard is empty or contents are |
401 | * provided otherwise. |
402 | */ |
403 | properties[PROP_CONTENT] = |
404 | g_param_spec_object (name: "content" , |
405 | nick: "Content" , |
406 | blurb: "Provider of the clipboard's content" , |
407 | GDK_TYPE_CONTENT_PROVIDER, |
408 | flags: G_PARAM_READABLE | |
409 | G_PARAM_STATIC_STRINGS | |
410 | G_PARAM_EXPLICIT_NOTIFY); |
411 | |
412 | /** |
413 | * GdkClipboard::changed: |
414 | * @clipboard: the object on which the signal was emitted |
415 | * |
416 | * Emitted when the clipboard changes ownership. |
417 | */ |
418 | signals[CHANGED] = |
419 | g_signal_new (signal_name: "changed" , |
420 | G_TYPE_FROM_CLASS (class), |
421 | signal_flags: G_SIGNAL_RUN_LAST, |
422 | G_STRUCT_OFFSET (GdkClipboardClass, changed), |
423 | NULL, NULL, NULL, |
424 | G_TYPE_NONE, n_params: 0); |
425 | |
426 | g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPERTIES, pspecs: properties); |
427 | } |
428 | |
429 | static void |
430 | gdk_clipboard_init (GdkClipboard *clipboard) |
431 | { |
432 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
433 | |
434 | priv->formats = gdk_content_formats_new (NULL, n_mime_types: 0); |
435 | priv->local = TRUE; |
436 | } |
437 | |
438 | /** |
439 | * gdk_clipboard_get_display: (attributes org.gtk.Method.get_property=display) |
440 | * @clipboard: a `GdkClipboard` |
441 | * |
442 | * Gets the `GdkDisplay` that the clipboard was created for. |
443 | * |
444 | * Returns: (transfer none): a `GdkDisplay` |
445 | */ |
446 | GdkDisplay * |
447 | gdk_clipboard_get_display (GdkClipboard *clipboard) |
448 | { |
449 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
450 | |
451 | g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), NULL); |
452 | |
453 | return priv->display; |
454 | } |
455 | |
456 | /** |
457 | * gdk_clipboard_get_formats: (attributes org.gtk.Method.get_property=formats) |
458 | * @clipboard: a `GdkClipboard` |
459 | * |
460 | * Gets the formats that the clipboard can provide its current contents in. |
461 | * |
462 | * Returns: (transfer none): The formats of the clipboard |
463 | */ |
464 | GdkContentFormats * |
465 | gdk_clipboard_get_formats (GdkClipboard *clipboard) |
466 | { |
467 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
468 | |
469 | g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), NULL); |
470 | |
471 | return priv->formats; |
472 | } |
473 | |
474 | /** |
475 | * gdk_clipboard_is_local: (attributes org.gtk.Method.get_property=local) |
476 | * @clipboard: a `GdkClipboard` |
477 | * |
478 | * Returns if the clipboard is local. |
479 | * |
480 | * A clipboard is considered local if it was last claimed |
481 | * by the running application. |
482 | * |
483 | * Note that [method@Gdk.Clipboard.get_content] may return %NULL |
484 | * even on a local clipboard. In this case the clipboard is empty. |
485 | * |
486 | * Returns: %TRUE if the clipboard is local |
487 | */ |
488 | gboolean |
489 | gdk_clipboard_is_local (GdkClipboard *clipboard) |
490 | { |
491 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
492 | |
493 | g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), FALSE); |
494 | |
495 | return priv->local; |
496 | } |
497 | |
498 | /** |
499 | * gdk_clipboard_get_content: (attributes org.gtk.Method.get_property=content) |
500 | * @clipboard: a `GdkClipboard` |
501 | * |
502 | * Returns the `GdkContentProvider` currently set on @clipboard. |
503 | * |
504 | * If the @clipboard is empty or its contents are not owned by the |
505 | * current process, %NULL will be returned. |
506 | * |
507 | * Returns: (transfer none) (nullable): The content of a clipboard |
508 | * if the clipboard does not maintain any content |
509 | */ |
510 | GdkContentProvider * |
511 | gdk_clipboard_get_content (GdkClipboard *clipboard) |
512 | { |
513 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
514 | |
515 | g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), NULL); |
516 | |
517 | return priv->content; |
518 | } |
519 | |
520 | /** |
521 | * gdk_clipboard_store_async: |
522 | * @clipboard: a `GdkClipboard` |
523 | * @io_priority: the I/O priority of the request |
524 | * @cancellable: (nullable): optional `GCancellable` object |
525 | * @callback: (scope async): callback to call when the request is satisfied |
526 | * @user_data: (closure): the data to pass to callback function |
527 | * |
528 | * Asynchronously instructs the @clipboard to store its contents remotely. |
529 | * |
530 | * If the clipboard is not local, this function does nothing but report success. |
531 | * |
532 | * The @callback must call [method@Gdk.Clipboard.store_finish]. |
533 | * |
534 | * The purpose of this call is to preserve clipboard contents beyond the |
535 | * lifetime of an application, so this function is typically called on |
536 | * exit. Depending on the platform, the functionality may not be available |
537 | * unless a "clipboard manager" is running. |
538 | * |
539 | * This function is called automatically when a [class@Gtk.Application] is |
540 | * shut down, so you likely don't need to call it. |
541 | */ |
542 | void |
543 | gdk_clipboard_store_async (GdkClipboard *clipboard, |
544 | int io_priority, |
545 | GCancellable *cancellable, |
546 | GAsyncReadyCallback callback, |
547 | gpointer user_data) |
548 | { |
549 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
550 | |
551 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
552 | g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
553 | g_return_if_fail (callback != NULL); |
554 | |
555 | if (priv->local) |
556 | { |
557 | GDK_CLIPBOARD_GET_CLASS (clipboard)->store_async (clipboard, |
558 | io_priority, |
559 | cancellable, |
560 | callback, |
561 | user_data); |
562 | } |
563 | else |
564 | { |
565 | gdk_clipboard_store_default_async (clipboard, |
566 | io_priority, |
567 | cancellable, |
568 | callback, |
569 | user_data); |
570 | } |
571 | } |
572 | |
573 | /** |
574 | * gdk_clipboard_store_finish: |
575 | * @clipboard: a `GdkClipboard` |
576 | * @result: a `GAsyncResult` |
577 | * @error: a `GError` location to store the error occurring |
578 | * |
579 | * Finishes an asynchronous clipboard store. |
580 | * |
581 | * See [method@Gdk.Clipboard.store_async]. |
582 | * |
583 | * Returns: %TRUE if storing was successful. |
584 | */ |
585 | gboolean |
586 | gdk_clipboard_store_finish (GdkClipboard *clipboard, |
587 | GAsyncResult *result, |
588 | GError **error) |
589 | { |
590 | g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), FALSE); |
591 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
592 | |
593 | /* don't check priv->local here because it might have changed while the |
594 | * read was ongoing */ |
595 | if (g_async_result_is_tagged (res: result, source_tag: gdk_clipboard_store_default_async)) |
596 | { |
597 | return gdk_clipboard_store_default_finish (clipboard, result, error); |
598 | } |
599 | else |
600 | { |
601 | return GDK_CLIPBOARD_GET_CLASS (clipboard)->store_finish (clipboard, result, error); |
602 | } |
603 | } |
604 | |
605 | static void |
606 | gdk_clipboard_read_internal (GdkClipboard *clipboard, |
607 | GdkContentFormats *formats, |
608 | int io_priority, |
609 | GCancellable *cancellable, |
610 | GAsyncReadyCallback callback, |
611 | gpointer user_data) |
612 | { |
613 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
614 | |
615 | if (priv->local) |
616 | { |
617 | gdk_clipboard_read_local_async (clipboard, |
618 | formats, |
619 | io_priority, |
620 | cancellable, |
621 | callback, |
622 | user_data); |
623 | } |
624 | else |
625 | { |
626 | GDK_CLIPBOARD_GET_CLASS (clipboard)->read_async (clipboard, |
627 | formats, |
628 | io_priority, |
629 | cancellable, |
630 | callback, |
631 | user_data); |
632 | } |
633 | } |
634 | |
635 | /** |
636 | * gdk_clipboard_read_async: |
637 | * @clipboard: a `GdkClipboard` |
638 | * @mime_types: (array zero-terminated=1): a %NULL-terminated array of mime types to choose from |
639 | * @io_priority: the I/O priority of the request |
640 | * @cancellable: (nullable): optional `GCancellable` object |
641 | * @callback: (scope async): callback to call when the request is satisfied |
642 | * @user_data: (closure): the data to pass to callback function |
643 | * |
644 | * Asynchronously requests an input stream to read the @clipboard's |
645 | * contents from. |
646 | * |
647 | * When the operation is finished @callback will be called. You must then |
648 | * call [method@Gdk.Clipboard.read_finish] to get the result of the operation. |
649 | * |
650 | * The clipboard will choose the most suitable mime type from the given list |
651 | * to fulfill the request, preferring the ones listed first. |
652 | */ |
653 | void |
654 | gdk_clipboard_read_async (GdkClipboard *clipboard, |
655 | const char **mime_types, |
656 | int io_priority, |
657 | GCancellable *cancellable, |
658 | GAsyncReadyCallback callback, |
659 | gpointer user_data) |
660 | { |
661 | GdkContentFormats *formats; |
662 | |
663 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
664 | g_return_if_fail (mime_types != NULL && mime_types[0] != NULL); |
665 | g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
666 | g_return_if_fail (callback != NULL); |
667 | |
668 | formats = gdk_content_formats_new (mime_types, n_mime_types: g_strv_length (str_array: (char **) mime_types)); |
669 | |
670 | gdk_clipboard_read_internal (clipboard, formats, io_priority, cancellable, callback, user_data); |
671 | |
672 | gdk_content_formats_unref (formats); |
673 | } |
674 | |
675 | /** |
676 | * gdk_clipboard_read_finish: |
677 | * @clipboard: a `GdkClipboard` |
678 | * @result: a `GAsyncResult` |
679 | * @out_mime_type: (out) (optional) (transfer none): location to store |
680 | * the chosen mime type |
681 | * @error: a `GError` location to store the error occurring |
682 | * |
683 | * Finishes an asynchronous clipboard read. |
684 | * |
685 | * See [method@Gdk.Clipboard.read_async]. |
686 | * |
687 | * Returns: (transfer full) (nullable): a `GInputStream` |
688 | */ |
689 | GInputStream * |
690 | gdk_clipboard_read_finish (GdkClipboard *clipboard, |
691 | GAsyncResult *result, |
692 | const char **out_mime_type, |
693 | GError **error) |
694 | { |
695 | g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), NULL); |
696 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
697 | |
698 | /* don't check priv->local here because it might have changed while the |
699 | * read was ongoing */ |
700 | if (g_async_result_is_tagged (res: result, source_tag: gdk_clipboard_read_local_async)) |
701 | { |
702 | return gdk_clipboard_read_local_finish (clipboard, result, out_mime_type, error); |
703 | } |
704 | else |
705 | { |
706 | return GDK_CLIPBOARD_GET_CLASS (clipboard)->read_finish (clipboard, result, out_mime_type, error); |
707 | } |
708 | } |
709 | |
710 | static void |
711 | gdk_clipboard_read_value_done (GObject *source, |
712 | GAsyncResult *result, |
713 | gpointer data) |
714 | { |
715 | GTask *task = data; |
716 | GError *error = NULL; |
717 | GValue *value; |
718 | |
719 | value = g_task_get_task_data (task); |
720 | |
721 | if (!gdk_content_deserialize_finish (result, value, error: &error)) |
722 | g_task_return_error (task, error); |
723 | else |
724 | g_task_return_pointer (task, result: value, NULL); |
725 | |
726 | g_object_unref (object: task); |
727 | } |
728 | |
729 | static void |
730 | gdk_clipboard_read_value_got_stream (GObject *source, |
731 | GAsyncResult *result, |
732 | gpointer data) |
733 | { |
734 | GInputStream *stream; |
735 | GError *error = NULL; |
736 | GTask *task = data; |
737 | const char *mime_type; |
738 | |
739 | stream = gdk_clipboard_read_finish (GDK_CLIPBOARD (source), result, out_mime_type: &mime_type, error: &error); |
740 | if (stream == NULL) |
741 | { |
742 | g_task_return_error (task, error); |
743 | return; |
744 | } |
745 | |
746 | gdk_content_deserialize_async (stream, |
747 | mime_type, |
748 | G_VALUE_TYPE (g_task_get_task_data (task)), |
749 | io_priority: g_task_get_priority (task), |
750 | cancellable: g_task_get_cancellable (task), |
751 | callback: gdk_clipboard_read_value_done, |
752 | user_data: task); |
753 | g_object_unref (object: stream); |
754 | } |
755 | |
756 | static void |
757 | free_value (gpointer value) |
758 | { |
759 | g_value_unset (value); |
760 | g_slice_free (GValue, value); |
761 | } |
762 | |
763 | static void |
764 | gdk_clipboard_read_value_internal (GdkClipboard *clipboard, |
765 | GType type, |
766 | gpointer source_tag, |
767 | int io_priority, |
768 | GCancellable *cancellable, |
769 | GAsyncReadyCallback callback, |
770 | gpointer user_data) |
771 | { |
772 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
773 | GdkContentFormatsBuilder *builder; |
774 | GdkContentFormats *formats; |
775 | GValue *value; |
776 | GTask *task; |
777 | |
778 | task = g_task_new (source_object: clipboard, cancellable, callback, callback_data: user_data); |
779 | g_task_set_priority (task, priority: io_priority); |
780 | g_task_set_source_tag (task, source_tag); |
781 | value = g_slice_new0 (GValue); |
782 | g_value_init (value, g_type: type); |
783 | g_task_set_task_data (task, task_data: value, task_data_destroy: free_value); |
784 | |
785 | if (priv->local) |
786 | { |
787 | GError *error = NULL; |
788 | |
789 | if (priv->content == NULL) |
790 | { |
791 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_FOUND, |
792 | _("Cannot read from empty clipboard." )); |
793 | g_object_unref (object: task); |
794 | return; |
795 | } |
796 | |
797 | if (gdk_content_provider_get_value (provider: priv->content, value, error: &error)) |
798 | { |
799 | g_task_return_pointer (task, result: value, NULL); |
800 | g_object_unref (object: task); |
801 | return; |
802 | } |
803 | else if (!g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED)) |
804 | { |
805 | g_task_return_error (task, error); |
806 | g_object_unref (object: task); |
807 | return; |
808 | } |
809 | else |
810 | { |
811 | /* fall through to regular stream transfer */ |
812 | g_clear_error (err: &error); |
813 | } |
814 | } |
815 | |
816 | builder = gdk_content_formats_builder_new (); |
817 | gdk_content_formats_builder_add_gtype (builder, type); |
818 | formats = gdk_content_formats_builder_free_to_formats (builder); |
819 | formats = gdk_content_formats_union_deserialize_mime_types (formats); |
820 | |
821 | gdk_clipboard_read_internal (clipboard, |
822 | formats, |
823 | io_priority, |
824 | cancellable, |
825 | callback: gdk_clipboard_read_value_got_stream, |
826 | user_data: task); |
827 | |
828 | gdk_content_formats_unref (formats); |
829 | } |
830 | |
831 | /** |
832 | * gdk_clipboard_read_value_async: |
833 | * @clipboard: a `GdkClipboard` |
834 | * @type: a `GType` to read |
835 | * @io_priority: the I/O priority of the request |
836 | * @cancellable: (nullable): optional `GCancellable` object |
837 | * @callback: (scope async): callback to call when the request is satisfied |
838 | * @user_data: (closure): the data to pass to callback function |
839 | * |
840 | * Asynchronously request the @clipboard contents converted to the given |
841 | * @type. |
842 | * |
843 | * When the operation is finished @callback will be called. You must then call |
844 | * [method@Gdk.Clipboard.read_value_finish] to get the resulting `GValue`. |
845 | * |
846 | * For local clipboard contents that are available in the given `GType`, |
847 | * the value will be copied directly. Otherwise, GDK will try to use |
848 | * [func@content_deserialize_async] to convert the clipboard's data. |
849 | */ |
850 | void |
851 | gdk_clipboard_read_value_async (GdkClipboard *clipboard, |
852 | GType type, |
853 | int io_priority, |
854 | GCancellable *cancellable, |
855 | GAsyncReadyCallback callback, |
856 | gpointer user_data) |
857 | { |
858 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
859 | g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
860 | g_return_if_fail (callback != NULL); |
861 | |
862 | gdk_clipboard_read_value_internal (clipboard, |
863 | type, |
864 | source_tag: gdk_clipboard_read_value_async, |
865 | io_priority, |
866 | cancellable, |
867 | callback, |
868 | user_data); |
869 | } |
870 | |
871 | /** |
872 | * gdk_clipboard_read_value_finish: |
873 | * @clipboard: a `GdkClipboard` |
874 | * @result: a `GAsyncResult` |
875 | * @error: a GError` location to store the error occurring |
876 | * |
877 | * Finishes an asynchronous clipboard read. |
878 | * |
879 | * See [method@Gdk.Clipboard.read_value_async]. |
880 | * |
881 | * Returns: (transfer none): a `GValue` containing the result. |
882 | */ |
883 | const GValue * |
884 | gdk_clipboard_read_value_finish (GdkClipboard *clipboard, |
885 | GAsyncResult *result, |
886 | GError **error) |
887 | { |
888 | g_return_val_if_fail (g_task_is_valid (result, clipboard), NULL); |
889 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_read_value_async, NULL); |
890 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
891 | |
892 | return g_task_propagate_pointer (G_TASK (result), error); |
893 | } |
894 | |
895 | /** |
896 | * gdk_clipboard_read_texture_async: |
897 | * @clipboard: a `GdkClipboard` |
898 | * @cancellable: (nullable): optional `GCancellable` object, %NULL to ignore. |
899 | * @callback: (scope async): callback to call when the request is satisfied |
900 | * @user_data: (closure): the data to pass to callback function |
901 | * |
902 | * Asynchronously request the @clipboard contents converted to a `GdkPixbuf`. |
903 | * |
904 | * When the operation is finished @callback will be called. You must then |
905 | * call [method@Gdk.Clipboard.read_texture_finish] to get the result. |
906 | * |
907 | * This is a simple wrapper around [method@Gdk.Clipboard.read_value_async]. |
908 | * Use that function or [method@Gdk.Clipboard.read_async] directly if you |
909 | * need more control over the operation. |
910 | */ |
911 | void |
912 | gdk_clipboard_read_texture_async (GdkClipboard *clipboard, |
913 | GCancellable *cancellable, |
914 | GAsyncReadyCallback callback, |
915 | gpointer user_data) |
916 | { |
917 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
918 | g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
919 | g_return_if_fail (callback != NULL); |
920 | |
921 | gdk_clipboard_read_value_internal (clipboard, |
922 | GDK_TYPE_TEXTURE, |
923 | source_tag: gdk_clipboard_read_texture_async, |
924 | G_PRIORITY_DEFAULT, |
925 | cancellable, |
926 | callback, |
927 | user_data); |
928 | } |
929 | |
930 | /** |
931 | * gdk_clipboard_read_texture_finish: |
932 | * @clipboard: a `GdkClipboard` |
933 | * @result: a `GAsyncResult` |
934 | * @error: a `GError` location to store the error occurring |
935 | * |
936 | * Finishes an asynchronous clipboard read. |
937 | * |
938 | * See [method@Gdk.Clipboard.read_texture_async]. |
939 | * |
940 | * Returns: (transfer full) (nullable): a new `GdkTexture` |
941 | */ |
942 | GdkTexture * |
943 | gdk_clipboard_read_texture_finish (GdkClipboard *clipboard, |
944 | GAsyncResult *result, |
945 | GError **error) |
946 | { |
947 | const GValue *value; |
948 | |
949 | g_return_val_if_fail (g_task_is_valid (result, clipboard), NULL); |
950 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_read_texture_async, NULL); |
951 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
952 | |
953 | value = g_task_propagate_pointer (G_TASK (result), error); |
954 | if (!value) |
955 | return NULL; |
956 | |
957 | return g_value_dup_object (value); |
958 | } |
959 | |
960 | /** |
961 | * gdk_clipboard_read_text_async: |
962 | * @clipboard: a `GdkClipboard` |
963 | * @cancellable: (nullable): optional `GCancellable` object |
964 | * @callback: (scope async): callback to call when the request is satisfied |
965 | * @user_data: (closure): the data to pass to callback function |
966 | * |
967 | * Asynchronously request the @clipboard contents converted to a string. |
968 | * |
969 | * When the operation is finished @callback will be called. You must then |
970 | * call [method@Gdk.Clipboard.read_text_finish] to get the result. |
971 | * |
972 | * This is a simple wrapper around [method@Gdk.Clipboard.read_value_async]. |
973 | * Use that function or [method@Gdk.Clipboard.read_async] directly if you |
974 | * need more control over the operation. |
975 | */ |
976 | void |
977 | gdk_clipboard_read_text_async (GdkClipboard *clipboard, |
978 | GCancellable *cancellable, |
979 | GAsyncReadyCallback callback, |
980 | gpointer user_data) |
981 | { |
982 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
983 | g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
984 | g_return_if_fail (callback != NULL); |
985 | |
986 | gdk_clipboard_read_value_internal (clipboard, |
987 | G_TYPE_STRING, |
988 | source_tag: gdk_clipboard_read_text_async, |
989 | G_PRIORITY_DEFAULT, |
990 | cancellable, |
991 | callback, |
992 | user_data); |
993 | } |
994 | |
995 | /** |
996 | * gdk_clipboard_read_text_finish: |
997 | * @clipboard: a `GdkClipboard` |
998 | * @result: a `GAsyncResult` |
999 | * @error: a `GError` location to store the error occurring |
1000 | * |
1001 | * Finishes an asynchronous clipboard read. |
1002 | * |
1003 | * See [method@Gdk.Clipboard.read_text_async]. |
1004 | * |
1005 | * Returns: (transfer full) (nullable): a new string |
1006 | */ |
1007 | char * |
1008 | gdk_clipboard_read_text_finish (GdkClipboard *clipboard, |
1009 | GAsyncResult *result, |
1010 | GError **error) |
1011 | { |
1012 | const GValue *value; |
1013 | |
1014 | g_return_val_if_fail (g_task_is_valid (result, clipboard), NULL); |
1015 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_read_text_async, NULL); |
1016 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
1017 | |
1018 | value = g_task_propagate_pointer (G_TASK (result), error); |
1019 | if (!value) |
1020 | return NULL; |
1021 | |
1022 | return g_value_dup_string (value); |
1023 | } |
1024 | |
1025 | GdkClipboard * |
1026 | gdk_clipboard_new (GdkDisplay *display) |
1027 | { |
1028 | return g_object_new (GDK_TYPE_CLIPBOARD, |
1029 | first_property_name: "display" , display, |
1030 | NULL); |
1031 | } |
1032 | |
1033 | static void |
1034 | gdk_clipboard_write_done (GObject *content, |
1035 | GAsyncResult *result, |
1036 | gpointer task) |
1037 | { |
1038 | GError *error = NULL; |
1039 | |
1040 | if (gdk_content_provider_write_mime_type_finish (GDK_CONTENT_PROVIDER (content), result, error: &error)) |
1041 | g_task_return_boolean (task, TRUE); |
1042 | else |
1043 | g_task_return_error (task, error); |
1044 | |
1045 | g_object_unref (object: task); |
1046 | } |
1047 | |
1048 | static void |
1049 | gdk_clipboard_write_serialize_done (GObject *content, |
1050 | GAsyncResult *result, |
1051 | gpointer task) |
1052 | { |
1053 | GError *error = NULL; |
1054 | |
1055 | if (gdk_content_serialize_finish (result, error: &error)) |
1056 | g_task_return_boolean (task, TRUE); |
1057 | else |
1058 | g_task_return_error (task, error); |
1059 | |
1060 | g_object_unref (object: task); |
1061 | } |
1062 | |
1063 | void |
1064 | gdk_clipboard_write_async (GdkClipboard *clipboard, |
1065 | const char *mime_type, |
1066 | GOutputStream *stream, |
1067 | int io_priority, |
1068 | GCancellable *cancellable, |
1069 | GAsyncReadyCallback callback, |
1070 | gpointer user_data) |
1071 | { |
1072 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
1073 | GdkContentFormats *formats, *mime_formats; |
1074 | GTask *task; |
1075 | GType gtype; |
1076 | |
1077 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
1078 | g_return_if_fail (priv->local); |
1079 | g_return_if_fail (mime_type != NULL); |
1080 | g_return_if_fail (mime_type == g_intern_string (mime_type)); |
1081 | g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); |
1082 | g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
1083 | g_return_if_fail (callback != NULL); |
1084 | |
1085 | task = g_task_new (source_object: clipboard, cancellable, callback, callback_data: user_data); |
1086 | g_task_set_priority (task, priority: io_priority); |
1087 | g_task_set_source_tag (task, gdk_clipboard_write_async); |
1088 | |
1089 | if (priv->content == NULL) |
1090 | { |
1091 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_FOUND, |
1092 | _("Cannot read from empty clipboard." )); |
1093 | g_object_unref (object: task); |
1094 | return; |
1095 | } |
1096 | |
1097 | formats = gdk_content_provider_ref_formats (provider: priv->content); |
1098 | if (gdk_content_formats_contain_mime_type (formats, mime_type)) |
1099 | { |
1100 | gdk_content_provider_write_mime_type_async (provider: priv->content, |
1101 | mime_type, |
1102 | stream, |
1103 | io_priority, |
1104 | cancellable, |
1105 | callback: gdk_clipboard_write_done, |
1106 | user_data: task); |
1107 | gdk_content_formats_unref (formats); |
1108 | return; |
1109 | } |
1110 | |
1111 | mime_formats = gdk_content_formats_new (mime_types: (const char *[2]) { mime_type, NULL }, n_mime_types: 1); |
1112 | mime_formats = gdk_content_formats_union_serialize_gtypes (formats: mime_formats); |
1113 | gtype = gdk_content_formats_match_gtype (first: formats, second: mime_formats); |
1114 | if (gtype != G_TYPE_INVALID) |
1115 | { |
1116 | GValue value = G_VALUE_INIT; |
1117 | GError *error = NULL; |
1118 | |
1119 | g_assert (gtype != G_TYPE_INVALID); |
1120 | |
1121 | g_value_init (value: &value, g_type: gtype); |
1122 | if (gdk_content_provider_get_value (provider: priv->content, value: &value, error: &error)) |
1123 | { |
1124 | gdk_content_serialize_async (stream, |
1125 | mime_type, |
1126 | value: &value, |
1127 | io_priority, |
1128 | cancellable, |
1129 | callback: gdk_clipboard_write_serialize_done, |
1130 | g_object_ref (task)); |
1131 | } |
1132 | else |
1133 | { |
1134 | g_task_return_error (task, error); |
1135 | } |
1136 | |
1137 | g_value_unset (value: &value); |
1138 | } |
1139 | else |
1140 | { |
1141 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED, |
1142 | _("No compatible formats to transfer clipboard contents." )); |
1143 | } |
1144 | |
1145 | gdk_content_formats_unref (formats: mime_formats); |
1146 | gdk_content_formats_unref (formats); |
1147 | g_object_unref (object: task); |
1148 | } |
1149 | |
1150 | gboolean |
1151 | gdk_clipboard_write_finish (GdkClipboard *clipboard, |
1152 | GAsyncResult *result, |
1153 | GError **error) |
1154 | { |
1155 | g_return_val_if_fail (g_task_is_valid (result, clipboard), FALSE); |
1156 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_clipboard_write_async, FALSE); |
1157 | |
1158 | return g_task_propagate_boolean (G_TASK (result), error); |
1159 | } |
1160 | |
1161 | static gboolean |
1162 | gdk_clipboard_claim (GdkClipboard *clipboard, |
1163 | GdkContentFormats *formats, |
1164 | gboolean local, |
1165 | GdkContentProvider *content) |
1166 | { |
1167 | return GDK_CLIPBOARD_GET_CLASS (clipboard)->claim (clipboard, formats, local, content); |
1168 | } |
1169 | |
1170 | static void |
1171 | gdk_clipboard_content_changed_cb (GdkContentProvider *provider, |
1172 | GdkClipboard *clipboard) |
1173 | { |
1174 | GdkContentFormats *formats; |
1175 | |
1176 | formats = gdk_content_provider_ref_formats (provider); |
1177 | formats = gdk_content_formats_union_serialize_mime_types (formats); |
1178 | |
1179 | gdk_clipboard_claim (clipboard, formats, TRUE, content: provider); |
1180 | |
1181 | gdk_content_formats_unref (formats); |
1182 | } |
1183 | |
1184 | void |
1185 | gdk_clipboard_claim_remote (GdkClipboard *clipboard, |
1186 | GdkContentFormats *formats) |
1187 | { |
1188 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
1189 | g_return_if_fail (formats != NULL); |
1190 | |
1191 | gdk_clipboard_claim (clipboard, formats, FALSE, NULL); |
1192 | } |
1193 | |
1194 | /** |
1195 | * gdk_clipboard_set_content: |
1196 | * @clipboard: a `GdkClipboard` |
1197 | * @provider: (transfer none) (nullable): the new contents of @clipboard |
1198 | * or %NULL to clear the clipboard |
1199 | * |
1200 | * Sets a new content provider on @clipboard. |
1201 | * |
1202 | * The clipboard will claim the `GdkDisplay`'s resources and advertise |
1203 | * these new contents to other applications. |
1204 | * |
1205 | * In the rare case of a failure, this function will return %FALSE. The |
1206 | * clipboard will then continue reporting its old contents and ignore |
1207 | * @provider. |
1208 | * |
1209 | * If the contents are read by either an external application or the |
1210 | * @clipboard's read functions, @clipboard will select the best format to |
1211 | * transfer the contents and then request that format from @provider. |
1212 | * |
1213 | * Returns: %TRUE if setting the clipboard succeeded |
1214 | */ |
1215 | gboolean |
1216 | gdk_clipboard_set_content (GdkClipboard *clipboard, |
1217 | GdkContentProvider *provider) |
1218 | { |
1219 | GdkClipboardPrivate *priv = gdk_clipboard_get_instance_private (self: clipboard); |
1220 | GdkContentFormats *formats; |
1221 | gboolean result; |
1222 | |
1223 | g_return_val_if_fail (GDK_IS_CLIPBOARD (clipboard), FALSE); |
1224 | g_return_val_if_fail (provider == NULL || GDK_IS_CONTENT_PROVIDER (provider), FALSE); |
1225 | |
1226 | if (provider) |
1227 | { |
1228 | if (priv->content == provider) |
1229 | return TRUE; |
1230 | |
1231 | formats = gdk_content_provider_ref_formats (provider); |
1232 | formats = gdk_content_formats_union_serialize_mime_types (formats); |
1233 | } |
1234 | else |
1235 | { |
1236 | if (priv->content == NULL && priv->local) |
1237 | return TRUE; |
1238 | |
1239 | formats = gdk_content_formats_new (NULL, n_mime_types: 0); |
1240 | } |
1241 | |
1242 | result = gdk_clipboard_claim (clipboard, formats, TRUE, content: provider); |
1243 | |
1244 | gdk_content_formats_unref (formats); |
1245 | |
1246 | return result; |
1247 | } |
1248 | |
1249 | /** |
1250 | * gdk_clipboard_set: |
1251 | * @clipboard: a `GdkClipboard` |
1252 | * @type: type of value to set |
1253 | * @...: value contents conforming to @type |
1254 | * |
1255 | * Sets the clipboard to contain the value collected from the given varargs. |
1256 | * |
1257 | * Values should be passed the same way they are passed to other value |
1258 | * collecting APIs, such as [`method@GObject.Object.set`] or |
1259 | * [`func@GObject.signal_emit`]. |
1260 | * |
1261 | * ```c |
1262 | * gdk_clipboard_set (clipboard, GTK_TYPE_STRING, "Hello World"); |
1263 | * |
1264 | * gdk_clipboard_set (clipboard, GDK_TYPE_TEXTURE, some_texture); |
1265 | * ``` |
1266 | */ |
1267 | void |
1268 | gdk_clipboard_set (GdkClipboard *clipboard, |
1269 | GType type, |
1270 | ...) |
1271 | { |
1272 | va_list args; |
1273 | |
1274 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
1275 | |
1276 | va_start (args, type); |
1277 | gdk_clipboard_set_valist (clipboard, type, args); |
1278 | va_end (args); |
1279 | } |
1280 | |
1281 | /** |
1282 | * gdk_clipboard_set_valist: (skip) |
1283 | * @clipboard: a `GdkClipboard` |
1284 | * @type: type of value to set |
1285 | * @args: varargs containing the value of @type |
1286 | * |
1287 | * Sets the clipboard to contain the value collected from the given @args. |
1288 | */ |
1289 | void |
1290 | gdk_clipboard_set_valist (GdkClipboard *clipboard, |
1291 | GType type, |
1292 | va_list args) |
1293 | { |
1294 | GValue value = G_VALUE_INIT; |
1295 | char *error; |
1296 | |
1297 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
1298 | |
1299 | G_VALUE_COLLECT_INIT (&value, type, |
1300 | args, G_VALUE_NOCOPY_CONTENTS, |
1301 | &error); |
1302 | if (error) |
1303 | { |
1304 | g_warning ("%s: %s" , G_STRLOC, error); |
1305 | g_free (mem: error); |
1306 | /* we purposely leak the value here, it might not be |
1307 | * in a sane state if an error condition occurred |
1308 | */ |
1309 | return; |
1310 | } |
1311 | |
1312 | gdk_clipboard_set_value (clipboard, value: &value); |
1313 | g_value_unset (value: &value); |
1314 | } |
1315 | |
1316 | /** |
1317 | * gdk_clipboard_set_value: (rename-to gdk_clipboard_set) |
1318 | * @clipboard: a `GdkClipboard` |
1319 | * @value: a `GValue` to set |
1320 | * |
1321 | * Sets the @clipboard to contain the given @value. |
1322 | */ |
1323 | void |
1324 | gdk_clipboard_set_value (GdkClipboard *clipboard, |
1325 | const GValue *value) |
1326 | { |
1327 | GdkContentProvider *provider; |
1328 | |
1329 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
1330 | g_return_if_fail (G_IS_VALUE (value)); |
1331 | |
1332 | provider = gdk_content_provider_new_for_value (value); |
1333 | |
1334 | gdk_clipboard_set_content (clipboard, provider); |
1335 | g_object_unref (object: provider); |
1336 | } |
1337 | |
1338 | /** |
1339 | * gdk_clipboard_set_text: (skip) |
1340 | * @clipboard: a `GdkClipboard` |
1341 | * @text: Text to put into the clipboard |
1342 | * |
1343 | * Puts the given @text into the clipboard. |
1344 | */ |
1345 | void |
1346 | gdk_clipboard_set_text (GdkClipboard *clipboard, |
1347 | const char *text) |
1348 | { |
1349 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
1350 | |
1351 | gdk_clipboard_set (clipboard, G_TYPE_STRING, text); |
1352 | } |
1353 | |
1354 | /** |
1355 | * gdk_clipboard_set_texture: (skip) |
1356 | * @clipboard: a `GdkClipboard` |
1357 | * @texture: a `GdkTexture` to put into the clipboard |
1358 | * |
1359 | * Puts the given @texture into the clipboard. |
1360 | */ |
1361 | void |
1362 | gdk_clipboard_set_texture (GdkClipboard *clipboard, |
1363 | GdkTexture *texture) |
1364 | { |
1365 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
1366 | g_return_if_fail (GDK_IS_TEXTURE (texture)); |
1367 | |
1368 | gdk_clipboard_set (clipboard, GDK_TYPE_TEXTURE, texture); |
1369 | } |
1370 | |