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 
G_DEFINE_TYPE_WITH_PRIVATE(GdkContentProvider,gdk_content_provider,G_TYPE_OBJECT)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
gdk_content_provider_real_detach_clipboard(GdkContentProvider * provider,GdkClipboard * clipboard)72 gdk_content_provider_real_detach_clipboard (GdkContentProvider *provider,
73                                             GdkClipboard       *clipboard)
74 {
75 }
76 
77 static GdkContentFormats *
gdk_content_provider_real_ref_formats(GdkContentProvider * provider)78 gdk_content_provider_real_ref_formats (GdkContentProvider *provider)
79 {
80   return gdk_content_formats_new (NULL, 0);
81 }
82 
83 static GdkContentFormats *
gdk_content_provider_real_ref_storable_formats(GdkContentProvider * provider)84 gdk_content_provider_real_ref_storable_formats (GdkContentProvider *provider)
85 {
86   return gdk_content_provider_ref_formats (provider);
87 }
88 
89 static void
gdk_content_provider_real_write_mime_type_async(GdkContentProvider * provider,const char * mime_type,GOutputStream * stream,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)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 (provider, cancellable, callback, user_data);
101   g_task_set_priority (task, 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, G_IO_ERROR_NOT_SUPPORTED,
105                            _("Cannot provide contents as “%s”"), mime_type);
106   g_object_unref (task);
107 }
108 
109 static gboolean
gdk_content_provider_real_write_mime_type_finish(GdkContentProvider * provider,GAsyncResult * result,GError ** error)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
gdk_content_provider_real_get_value(GdkContentProvider * provider,GValue * value,GError ** error)121 gdk_content_provider_real_get_value (GdkContentProvider  *provider,
122                                      GValue              *value,
123                                      GError             **error)
124 {
125   g_set_error (error, G_IO_ERROR, 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
gdk_content_provider_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)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, gdk_content_provider_ref_formats (provider));
143       break;
144 
145     case PROP_STORABLE_FORMATS:
146       g_value_take_boxed (value, 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
gdk_content_provider_class_init(GdkContentProviderClass * class)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 ("formats",
177                         "Formats",
178                         "The possible formats for data",
179                         GDK_TYPE_CONTENT_FORMATS,
180                         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 ("storable-formats",
191                         "Storable formats",
192                         "The formats that data should be stored in",
193                         GDK_TYPE_CONTENT_FORMATS,
194                         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 ("content-changed",
205                   G_TYPE_FROM_CLASS (class),
206                   G_SIGNAL_RUN_LAST,
207                   G_STRUCT_OFFSET (GdkContentProviderClass, content_changed),
208                   NULL, NULL, NULL,
209                   G_TYPE_NONE, 0);
210 
211   g_object_class_install_properties (object_class, N_PROPERTIES, properties);
212 }
213 
214 static void
gdk_content_provider_init(GdkContentProvider * provider)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 *
gdk_content_provider_ref_formats(GdkContentProvider * provider)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 *
gdk_content_provider_ref_storable_formats(GdkContentProvider * provider)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
gdk_content_provider_content_changed(GdkContentProvider * provider)263 gdk_content_provider_content_changed (GdkContentProvider *provider)
264 {
265   g_return_if_fail (GDK_IS_CONTENT_PROVIDER (provider));
266 
267   g_signal_emit (provider, signals[CONTENT_CHANGED], 0);
268 
269   g_object_notify_by_pspec (G_OBJECT (provider), 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
gdk_content_provider_write_mime_type_async(GdkContentProvider * provider,const char * mime_type,GOutputStream * stream,int io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)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 (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
gdk_content_provider_write_mime_type_finish(GdkContentProvider * provider,GAsyncResult * result,GError ** error)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: 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
gdk_content_provider_get_value(GdkContentProvider * provider,GValue * value,GError ** error)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
gdk_content_provider_attach_clipboard(GdkContentProvider * provider,GdkClipboard * clipboard)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
gdk_content_provider_detach_clipboard(GdkContentProvider * provider,GdkClipboard * clipboard)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