"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-clip-data.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-clip-data.c: clipboard data
4  *
5  * Copyright (C) 2006 Novell Inc
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 #include "gsf-config.h"
23 #include <glib/gi18n-lib.h>
24 #include "gsf-clip-data.h"
25 #include "gsf-utils.h"
26 #include "gsf-impl-utils.h"
27
28 /* Private part of the GsfClipData structure */
29 struct _GsfClipDataPrivate {
30         GsfClipFormat format;
31         GsfBlob *data_blob;
32 };
33
34 static GObjectClass *gsf_clip_data_parent_class;
35 static void
36 gsf_clip_data_finalize (GObject *object)
37 {
38         GsfClipData *clip_data;
39         GsfClipDataPrivate *priv;
40
41         clip_data = GSF_CLIP_DATA (object);
42         priv = clip_data->priv;
43
44         if (priv->data_blob)
45                 g_object_unref (priv->data_blob);
46
47         g_free (priv);
48
49         gsf_clip_data_parent_class->finalize (object);
50 }
51
52 static void
53 gsf_clip_data_init (GsfClipData *clip_data)
54 {
55         GsfClipDataPrivate *priv;
56
57         priv = g_new0 (GsfClipDataPrivate, 1);
58         clip_data->priv = priv;
59 }
60 static void
61 gsf_clip_data_class_init (GObjectClass *gobject_class)
62 {
63         gobject_class->finalize = gsf_clip_data_finalize;
64
65         gsf_clip_data_parent_class = g_type_class_peek_parent (gobject_class);
66 }
67
68
69 GSF_CLASS (GsfClipData, gsf_clip_data,
70            gsf_clip_data_class_init, gsf_clip_data_init,
71            G_TYPE_OBJECT);
72
73 /**
74  * gsf_clip_data_new:
75  * @format: Format for the data inside the @data_blob
76  * @data_blob: Object which holds the binary contents for the #GsfClipData
77  *
78  * Creates a new #GsfClipData object.  This function acquires a reference to the
79  * @data_blob, so you should unref the blob on your own if you no longer need it
80  * directly.
81  *
82  * Return value: A newly-created #GsfClipData.
83  **/
84 GsfClipData *
85 gsf_clip_data_new (GsfClipFormat format, GsfBlob *data_blob)
86 {
87         GsfClipData *clip_data;
88         GsfClipDataPrivate *priv;
89
90         g_return_val_if_fail (GSF_IS_BLOB (data_blob), NULL);
91
92         clip_data = g_object_new (GSF_TYPE_CLIP_DATA, NULL);
93         if (G_UNLIKELY (NULL == clip_data)) return NULL;
94
95         priv = clip_data->priv;
96
97         priv->format = format;
98         priv->data_blob = g_object_ref (data_blob);
99
100         return clip_data;
101 }
102
103 /**
104  * gsf_clip_data_get_format:
105  * @clip_data: A #GsfClipData.
106  *
107  * Queries the clipboard data format of a #GsfClipData.  The format refers to the data
108  * blob inside the @clip_data; use gsf_clip_data_get_data_blob() to get that data blob.
109  *
110  * Return value: The format in which the #GsfClipData's data blob is stored.
111  **/
112 GsfClipFormat
113 gsf_clip_data_get_format (GsfClipData *clip_data)
114 {
115         GsfClipDataPrivate *priv;
116
117         g_return_val_if_fail (GSF_IS_CLIP_DATA (clip_data), GSF_CLIP_FORMAT_UNKNOWN);
118
119         priv = clip_data->priv;
120         return priv->format;
121 }
122
123 /**
124  * gsf_clip_data_get_data_blob:
125  * @clip_data: A #GsfClipData.
126  *
127  * Queries the data blob that actually stores a #GsfClipData's binary data.
128  *
129  * Return value: A new reference to the #GsfBlob that stores this @clip_data's
130  * binary data.  You must use g_object_unref() to dispose of that data blob when
131  * you are done with it.
132  **/
133 GsfBlob *
134 gsf_clip_data_get_data_blob (GsfClipData *clip_data)
135 {
136         GsfClipDataPrivate *priv;
137
138         g_return_val_if_fail (GSF_IS_CLIP_DATA (clip_data), NULL);
139
140         priv = clip_data->priv;
141         return g_object_ref (priv->data_blob);
142 }
143
144 static void
145 set_error_missing_clipboard_data (GError **error, const char *format_name, gsize at_least_size)
146 {
147         gchar *size_str;
148
149         size_str = g_strdup_printf ("%" G_GSIZE_FORMAT, at_least_size);
150         g_set_error (error,
151                      GSF_ERROR,
152                      GSF_ERROR_INVALID_DATA,
153                      _("The clip_data is in %s, but it is smaller than "
154                        "at least %s bytes"),
155                      format_name,
156                      size_str);
157         g_free (size_str);
158 }
159
160 static gsize
161 get_windows_clipboard_data_offset (GsfClipFormatWindows format)
162 {
163         struct format_offset_pair {
164                 GsfClipFormatWindows format;
165                 gsize offset;
166         };
167
168         static const struct format_offset_pair pairs[] = {
169                 { GSF_CLIP_FORMAT_WINDOWS_UNKNOWN, 4 },
170                 { GSF_CLIP_FORMAT_WINDOWS_METAFILE, 12 },
171                 { GSF_CLIP_FORMAT_WINDOWS_DIB, 4 },
172                 { GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE, 4 } /* FIXME: does this have a PACKEDMETA in front
173                                                                   * as well, similar to GSF_CLIP_FORMAT_WINDOWS_METAFILE? */
174         };
175         static const int num_pairs = G_N_ELEMENTS (pairs);
176
177         int i;
178
179         for (i = 0; i < num_pairs; i++)
180                 if (pairs[i].format == format)
181                         return pairs[i].offset;
182
183         g_assert_not_reached ();
184         return 0;
185 }
186
187 /**
188  * check_format_windows:
189  * @format : #GsfClipFormatWindows
190  * @format_name : const char *
191  * @blob_size : #gsize
192  * @error :#GError
193  *
194  * Checks that the specified blob size matches the expected size for the format.
195  *
196  * Returns: the same format if the size is correct, or
197  *      GSF_CLIP_FORMAT_WINDOWS_ERROR if the size is too small.
198  **/
199 static GsfClipFormatWindows
200 check_format_windows (GsfClipFormatWindows format, const char *format_name, gsize blob_size, GError **error)
201 {
202         gsize offset;
203
204         offset = get_windows_clipboard_data_offset (format);
205         if (blob_size <= offset) {
206                 set_error_missing_clipboard_data (error, format_name, offset + 1);
207                 format = GSF_CLIP_FORMAT_WINDOWS_ERROR;
208         }
209
210         return format;
211 }
212
213 /**
214  * gsf_clip_data_get_windows_clipboard_format:
215  * @clip_data: A #GsfClipData.
216  * @error: Location to store error, or %NULL
217  *
218  * Queries the Windows clipboard data format for a #GsfClipData.  The @clip_data must
219  * have been created with #GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD.
220  *
221  * Returns: A #GsfClipFormatWindows value.
222  *
223  * Possible errors: #GSF_ERROR_INVALID_DATA if the data blob in the @clip_data is
224  * smaller than it should be; in this case GSF_CLIP_FORMAT_WINDOWS_ERROR will be returned.
225  **/
226 GsfClipFormatWindows
227 gsf_clip_data_get_windows_clipboard_format (GsfClipData *clip_data, GError **error)
228 {
229         GsfClipDataPrivate *priv;
230         gsize size;
231         guint32 value;
232         gconstpointer data;
233         GsfClipFormatWindows format;
234
235         g_return_val_if_fail (GSF_IS_CLIP_DATA (clip_data), GSF_CLIP_FORMAT_WINDOWS_ERROR);
236         g_return_val_if_fail (error == NULL || *error == NULL, GSF_CLIP_FORMAT_WINDOWS_ERROR);
237
238         priv = clip_data->priv;
239         g_return_val_if_fail (priv->format == GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD, GSF_CLIP_FORMAT_WINDOWS_ERROR);
240
241         size = gsf_blob_get_size (priv->data_blob);
242
243         if (size < 4) {
244                 g_set_error (error,
245                              GSF_ERROR,
246                              GSF_ERROR_INVALID_DATA,
247                              _("The clip_data is in Windows clipboard format, but it is smaller than "
248                                "the required 4 bytes."));
249                 return GSF_CLIP_FORMAT_WINDOWS_ERROR;
250         }
251
252         data = gsf_blob_peek_data (priv->data_blob);
253
254         value = GSF_LE_GET_GUINT32 (data);
255
256         switch (value) {
257         case GSF_CLIP_FORMAT_WINDOWS_METAFILE:
258                 format = check_format_windows (GSF_CLIP_FORMAT_WINDOWS_METAFILE, _("Windows Metafile format"),
259                                                size, error);
260                 break;
261
262         case GSF_CLIP_FORMAT_WINDOWS_DIB:
263         case 2: /* CF_BITMAP */
264                 format = check_format_windows (GSF_CLIP_FORMAT_WINDOWS_DIB, _("Windows DIB or BITMAP format"),
265                                                size, error);
266                 break;
267
268         case GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE:
269                 format = check_format_windows (GSF_CLIP_FORMAT_WINDOWS_ENHANCED_METAFILE, _("Windows Enhanced Metafile format"),
270                                                size, error);
271                 break;
272
273         default:
274                 format = GSF_CLIP_FORMAT_WINDOWS_UNKNOWN;
275                 break;
276         }
277
278         return format;
279 }
280
281 /**
282  * gsf_clip_data_peek_real_data:
283  * @clip_data: A #GsfClipData.
284  * @ret_size: Location to return the size of the returned data buffer.
285  * @error: Location to store error, or %NULL.
286  * 
287  * Queries a pointer directly to the clipboard data of a #GsfClipData.  The
288  * resulting pointer is not necessarily the same data pointer that was passed to
289  * gsf_blob_new() prior to creating the @clip_data.  For example, if the data is
290  * in #GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD format, then it will have extra header
291  * bytes in front of the actual metafile data.  This function will skip over
292  * those header bytes if necessary and return a pointer to the "real" data.
293  * 
294  * Return value: Pointer to the real clipboard data.  The size in bytes of this
295  * buffer is returned in the @ret_size argument.
296  **/
297 gconstpointer
298 gsf_clip_data_peek_real_data (GsfClipData *clip_data, gsize *ret_size, GError **error)
299 {
300         GsfClipDataPrivate *priv;
301         gconstpointer data;
302         gsize offset;
303
304         g_return_val_if_fail (GSF_IS_CLIP_DATA (clip_data), NULL);
305         g_return_val_if_fail (ret_size != NULL, NULL);
306         g_return_val_if_fail (error == NULL || *error == NULL, NULL);
307
308         priv = clip_data->priv;
309
310         data = gsf_blob_peek_data (priv->data_blob);
311
312         if (priv->format == GSF_CLIP_FORMAT_WINDOWS_CLIPBOARD) {
313                 GsfClipFormatWindows win_format;
314
315                 win_format = gsf_clip_data_get_windows_clipboard_format (clip_data, error);
316                 if (win_format == GSF_CLIP_FORMAT_WINDOWS_ERROR)
317                         return NULL;
318
319                 /* gsf_clip_data_get_windows_clipboard_format() already did the size checks for us,
320                  * so we can jump to the offset right away without doing extra checks.
321                  */
322
323                 offset = get_windows_clipboard_data_offset (win_format);
324         } else
325                 offset = 0;
326
327         *ret_size = gsf_blob_get_size (priv->data_blob) - offset;
328         return (char *) data + offset; /* cast to avoid warning about void pointer arithmetic */
329 }