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 "gdkcontentproviderprivate.h" |
22 | |
23 | #include "gdkclipboard.h" |
24 | #include "gdkcontentformats.h" |
25 | #include "gdkintl.h" |
26 | |
27 | /** |
28 | * GdkContentProvider: |
29 | * |
30 | * A `GdkContentProvider` is used to provide content for the clipboard or |
31 | * for drag-and-drop operations in a number of formats. |
32 | * |
33 | * To create a `GdkContentProvider`, use [ctor@Gdk.ContentProvider.new_for_value] |
34 | * or [ctor@Gdk.ContentProvider.new_for_bytes]. |
35 | * |
36 | * GDK knows how to handle common text and image formats out-of-the-box. See |
37 | * [class@Gdk.ContentSerializer] and [class@Gdk.ContentDeserializer] if you want |
38 | * to add support for application-specific data formats. |
39 | */ |
40 | |
41 | typedef struct _GdkContentProviderPrivate GdkContentProviderPrivate; |
42 | |
43 | struct _GdkContentProviderPrivate |
44 | { |
45 | GdkContentFormats *formats; |
46 | }; |
47 | |
48 | enum { |
49 | PROP_0, |
50 | PROP_FORMATS, |
51 | PROP_STORABLE_FORMATS, |
52 | N_PROPERTIES |
53 | }; |
54 | |
55 | enum { |
56 | CONTENT_CHANGED, |
57 | N_SIGNALS |
58 | }; |
59 | |
60 | static GParamSpec *properties[N_PROPERTIES] = { NULL, }; |
61 | static guint signals[N_SIGNALS] = { 0 }; |
62 | |
63 | G_DEFINE_TYPE_WITH_PRIVATE (GdkContentProvider, gdk_content_provider, G_TYPE_OBJECT) |
64 | |
65 | static void |
66 | gdk_content_provider_real_attach_clipboard (GdkContentProvider *provider, |
67 | GdkClipboard *clipboard) |
68 | { |
69 | } |
70 | |
71 | static void |
72 | gdk_content_provider_real_detach_clipboard (GdkContentProvider *provider, |
73 | GdkClipboard *clipboard) |
74 | { |
75 | } |
76 | |
77 | static GdkContentFormats * |
78 | gdk_content_provider_real_ref_formats (GdkContentProvider *provider) |
79 | { |
80 | return gdk_content_formats_new (NULL, n_mime_types: 0); |
81 | } |
82 | |
83 | static GdkContentFormats * |
84 | gdk_content_provider_real_ref_storable_formats (GdkContentProvider *provider) |
85 | { |
86 | return gdk_content_provider_ref_formats (provider); |
87 | } |
88 | |
89 | static void |
90 | gdk_content_provider_real_write_mime_type_async (GdkContentProvider *provider, |
91 | const char *mime_type, |
92 | GOutputStream *stream, |
93 | int io_priority, |
94 | GCancellable *cancellable, |
95 | GAsyncReadyCallback callback, |
96 | gpointer user_data) |
97 | { |
98 | GTask *task; |
99 | |
100 | task = g_task_new (source_object: provider, cancellable, callback, callback_data: user_data); |
101 | g_task_set_priority (task, priority: io_priority); |
102 | g_task_set_source_tag (task, gdk_content_provider_real_write_mime_type_async); |
103 | |
104 | g_task_return_new_error (task, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED, |
105 | _("Cannot provide contents as ā%sā" ), mime_type); |
106 | g_object_unref (object: task); |
107 | } |
108 | |
109 | static gboolean |
110 | gdk_content_provider_real_write_mime_type_finish (GdkContentProvider *provider, |
111 | GAsyncResult *result, |
112 | GError **error) |
113 | { |
114 | g_return_val_if_fail (g_task_is_valid (result, provider), FALSE); |
115 | g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == gdk_content_provider_real_write_mime_type_async, FALSE); |
116 | |
117 | return g_task_propagate_boolean (G_TASK (result), error); |
118 | } |
119 | |
120 | static gboolean |
121 | gdk_content_provider_real_get_value (GdkContentProvider *provider, |
122 | GValue *value, |
123 | GError **error) |
124 | { |
125 | g_set_error (err: error, G_IO_ERROR, code: G_IO_ERROR_NOT_SUPPORTED, |
126 | _("Cannot provide contents as %s" ), G_VALUE_TYPE_NAME (value)); |
127 | |
128 | return FALSE; |
129 | } |
130 | |
131 | static void |
132 | gdk_content_provider_get_property (GObject *gobject, |
133 | guint prop_id, |
134 | GValue *value, |
135 | GParamSpec *pspec) |
136 | { |
137 | GdkContentProvider *provider = GDK_CONTENT_PROVIDER (gobject); |
138 | |
139 | switch (prop_id) |
140 | { |
141 | case PROP_FORMATS: |
142 | g_value_take_boxed (value, v_boxed: gdk_content_provider_ref_formats (provider)); |
143 | break; |
144 | |
145 | case PROP_STORABLE_FORMATS: |
146 | g_value_take_boxed (value, v_boxed: gdk_content_provider_ref_storable_formats (provider)); |
147 | break; |
148 | |
149 | default: |
150 | G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); |
151 | break; |
152 | } |
153 | } |
154 | |
155 | static void |
156 | gdk_content_provider_class_init (GdkContentProviderClass *class) |
157 | { |
158 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
159 | |
160 | object_class->get_property = gdk_content_provider_get_property; |
161 | |
162 | class->attach_clipboard = gdk_content_provider_real_attach_clipboard; |
163 | class->detach_clipboard = gdk_content_provider_real_detach_clipboard; |
164 | class->ref_formats = gdk_content_provider_real_ref_formats; |
165 | class->ref_storable_formats = gdk_content_provider_real_ref_storable_formats; |
166 | class->write_mime_type_async = gdk_content_provider_real_write_mime_type_async; |
167 | class->write_mime_type_finish = gdk_content_provider_real_write_mime_type_finish; |
168 | class->get_value = gdk_content_provider_real_get_value; |
169 | |
170 | /** |
171 | * GdkContentProvider:formats: (attributes org.gtk.Property.get=gdk_content_provider_ref_formats) |
172 | * |
173 | * The possible formats that the provider can provide its data in. |
174 | */ |
175 | properties[PROP_FORMATS] = |
176 | g_param_spec_boxed (name: "formats" , |
177 | nick: "Formats" , |
178 | blurb: "The possible formats for data" , |
179 | GDK_TYPE_CONTENT_FORMATS, |
180 | flags: G_PARAM_READABLE | |
181 | G_PARAM_STATIC_STRINGS | |
182 | G_PARAM_EXPLICIT_NOTIFY); |
183 | |
184 | /** |
185 | * GdkContentProvider:storable-formats: (attributes org.gtk.Property.get=gdk_content_provider_ref_storable_formats) |
186 | * |
187 | * The subset of formats that clipboard managers should store this provider's data in. |
188 | */ |
189 | properties[PROP_STORABLE_FORMATS] = |
190 | g_param_spec_boxed (name: "storable-formats" , |
191 | nick: "Storable formats" , |
192 | blurb: "The formats that data should be stored in" , |
193 | GDK_TYPE_CONTENT_FORMATS, |
194 | flags: G_PARAM_READABLE | |
195 | G_PARAM_STATIC_STRINGS | |
196 | G_PARAM_EXPLICIT_NOTIFY); |
197 | |
198 | /** |
199 | * GdkContentProvider::content-changed: |
200 | * |
201 | * Emitted whenever the content provided by this provider has changed. |
202 | */ |
203 | signals[CONTENT_CHANGED] = |
204 | g_signal_new (signal_name: "content-changed" , |
205 | G_TYPE_FROM_CLASS (class), |
206 | signal_flags: G_SIGNAL_RUN_LAST, |
207 | G_STRUCT_OFFSET (GdkContentProviderClass, content_changed), |
208 | NULL, NULL, NULL, |
209 | G_TYPE_NONE, n_params: 0); |
210 | |
211 | g_object_class_install_properties (oclass: object_class, n_pspecs: N_PROPERTIES, pspecs: properties); |
212 | } |
213 | |
214 | static void |
215 | gdk_content_provider_init (GdkContentProvider *provider) |
216 | { |
217 | } |
218 | |
219 | /** |
220 | * gdk_content_provider_ref_formats: (attributes org.gtk.Method.get_property=formats) |
221 | * @provider: a `GdkContentProvider` |
222 | * |
223 | * Gets the formats that the provider can provide its current contents in. |
224 | * |
225 | * Returns: (transfer full): The formats of the provider |
226 | */ |
227 | GdkContentFormats * |
228 | gdk_content_provider_ref_formats (GdkContentProvider *provider) |
229 | { |
230 | g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (provider), NULL); |
231 | |
232 | return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->ref_formats (provider); |
233 | } |
234 | |
235 | /** |
236 | * gdk_content_provider_ref_storable_formats: (attributes org.gtk.Method.get_property=storable-formats) |
237 | * @provider: a `GdkContentProvider` |
238 | * |
239 | * Gets the formats that the provider suggests other applications to store |
240 | * the data in. |
241 | * |
242 | * An example of such an application would be a clipboard manager. |
243 | * |
244 | * This can be assumed to be a subset of [method@Gdk.ContentProvider.ref_formats]. |
245 | * |
246 | * Returns: (transfer full): The storable formats of the provider |
247 | */ |
248 | GdkContentFormats * |
249 | gdk_content_provider_ref_storable_formats (GdkContentProvider *provider) |
250 | { |
251 | g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (provider), NULL); |
252 | |
253 | return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->ref_storable_formats (provider); |
254 | } |
255 | |
256 | /** |
257 | * gdk_content_provider_content_changed: |
258 | * @provider: a `GdkContentProvider` |
259 | * |
260 | * Emits the ::content-changed signal. |
261 | */ |
262 | void |
263 | gdk_content_provider_content_changed (GdkContentProvider *provider) |
264 | { |
265 | g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); |
266 | |
267 | g_signal_emit (instance: provider, signal_id: signals[CONTENT_CHANGED], detail: 0); |
268 | |
269 | g_object_notify_by_pspec (G_OBJECT (provider), pspec: properties[PROP_FORMATS]); |
270 | } |
271 | |
272 | /** |
273 | * gdk_content_provider_write_mime_type_async: |
274 | * @provider: a `GdkContentProvider` |
275 | * @mime_type: the mime type to provide the data in |
276 | * @stream: the `GOutputStream` to write to |
277 | * @io_priority: I/O priority of the request. |
278 | * @cancellable: (nullable): optional `GCancellable` object, %NULL to ignore. |
279 | * @callback: (scope async): callback to call when the request is satisfied |
280 | * @user_data: (closure): the data to pass to callback function |
281 | * |
282 | * Asynchronously writes the contents of @provider to @stream in the given |
283 | * @mime_type. |
284 | * |
285 | * When the operation is finished @callback will be called. You must then call |
286 | * [method@Gdk.ContentProvider.write_mime_type_finish] to get the result |
287 | * of the operation. |
288 | * |
289 | * The given mime type does not need to be listed in the formats returned by |
290 | * [method@Gdk.ContentProvider.ref_formats]. However, if the given `GType` is |
291 | * not supported, `G_IO_ERROR_NOT_SUPPORTED` will be reported. |
292 | * |
293 | * The given @stream will not be closed. |
294 | */ |
295 | void |
296 | gdk_content_provider_write_mime_type_async (GdkContentProvider *provider, |
297 | const char *mime_type, |
298 | GOutputStream *stream, |
299 | int io_priority, |
300 | GCancellable *cancellable, |
301 | GAsyncReadyCallback callback, |
302 | gpointer user_data) |
303 | { |
304 | g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); |
305 | g_return_if_fail (mime_type != NULL); |
306 | g_return_if_fail (G_IS_OUTPUT_STREAM (stream)); |
307 | g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable)); |
308 | |
309 | GDK_CONTENT_PROVIDER_GET_CLASS (provider)->write_mime_type_async (provider, |
310 | g_intern_string (string: mime_type), |
311 | stream, |
312 | io_priority, |
313 | cancellable, |
314 | callback, |
315 | user_data); |
316 | } |
317 | |
318 | /** |
319 | * gdk_content_provider_write_mime_type_finish: |
320 | * @provider: a `GdkContentProvider` |
321 | * @result: a `GAsyncResult` |
322 | * @error: a `GError` location to store the error occurring |
323 | * |
324 | * Finishes an asynchronous write operation. |
325 | * |
326 | * See [method@Gdk.ContentProvider.write_mime_type_async]. |
327 | * |
328 | * Returns: %TRUE if the operation was completed successfully. Otherwise |
329 | * @error will be set to describe the failure. |
330 | */ |
331 | gboolean |
332 | gdk_content_provider_write_mime_type_finish (GdkContentProvider *provider, |
333 | GAsyncResult *result, |
334 | GError **error) |
335 | { |
336 | g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (provider), FALSE); |
337 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
338 | |
339 | return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->write_mime_type_finish (provider, result, error); |
340 | } |
341 | |
342 | /** |
343 | * gdk_content_provider_get_value: |
344 | * @provider: a `GdkContentProvider` |
345 | * @value: (out caller-allocates): the `GValue` to fill |
346 | * @error: a `GError` location to store the error occurring |
347 | * |
348 | * Gets the contents of @provider stored in @value. |
349 | * |
350 | * The @value will have been initialized to the `GType` the value should be |
351 | * provided in. This given `GType` does not need to be listed in the formats |
352 | * returned by [method@Gdk.ContentProvider.ref_formats]. However, if the |
353 | * given `GType` is not supported, this operation can fail and |
354 | * `G_IO_ERROR_NOT_SUPPORTED` will be reported. |
355 | * |
356 | * Returns: %TRUE if the value was set successfully. Otherwise |
357 | * @error will be set to describe the failure. |
358 | */ |
359 | gboolean |
360 | gdk_content_provider_get_value (GdkContentProvider *provider, |
361 | GValue *value, |
362 | GError **error) |
363 | { |
364 | g_return_val_if_fail (GDK_IS_CONTENT_PROVIDER (provider), FALSE); |
365 | g_return_val_if_fail (G_IS_VALUE (value), FALSE); |
366 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
367 | |
368 | return GDK_CONTENT_PROVIDER_GET_CLASS (provider)->get_value (provider, |
369 | value, |
370 | error); |
371 | } |
372 | |
373 | void |
374 | gdk_content_provider_attach_clipboard (GdkContentProvider *provider, |
375 | GdkClipboard *clipboard) |
376 | { |
377 | g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); |
378 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
379 | |
380 | GDK_CONTENT_PROVIDER_GET_CLASS (provider)->attach_clipboard (provider, clipboard); |
381 | } |
382 | |
383 | void |
384 | gdk_content_provider_detach_clipboard (GdkContentProvider *provider, |
385 | GdkClipboard *clipboard) |
386 | { |
387 | g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider)); |
388 | g_return_if_fail (GDK_IS_CLIPBOARD (clipboard)); |
389 | |
390 | GDK_CONTENT_PROVIDER_GET_CLASS (provider)->detach_clipboard (provider, clipboard); |
391 | } |
392 | |