1 /*
2 * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3 * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
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, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "common-pict.h"
22 #include <string.h>
23 #include <libgda/gda-quark-list.h>
24 #include <libgda/gda-blob-op.h>
25 #include <libgda-ui/libgda-ui.h>
26 #include <libgda-ui/gdaui-data-entry.h>
27
28 /*
29 * Fills in @bindata->data and @bindata->data_length with the contents of @value.
30 *
31 * Returns: TRUE if the data has been loaded correctly
32 */
33 gboolean
common_pict_load_data(PictOptions * options,const GValue * value,PictBinData * bindata,const gchar ** stock,GError ** error)34 common_pict_load_data (PictOptions *options, const GValue *value, PictBinData *bindata,
35 const gchar **stock, GError **error)
36 {
37 gboolean allok = TRUE;
38
39 if (value) {
40 if (gda_value_is_null ((GValue *) value)) {
41 *stock = GTK_STOCK_MISSING_IMAGE;
42 g_set_error (error, GDAUI_DATA_ENTRY_ERROR, GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
43 "%s", _("No data"));
44 allok = FALSE;
45 }
46 else {
47 if (G_VALUE_TYPE ((GValue *) value) == GDA_TYPE_BLOB) {
48 GdaBlob *blob;
49 GdaBinary *bin;
50
51 blob = (GdaBlob *) gda_value_get_blob ((GValue *) value);
52 g_assert (blob);
53 bin = (GdaBinary *) blob;
54 if (blob->op &&
55 (bin->binary_length != gda_blob_op_get_length (blob->op)))
56 gda_blob_op_read_all (blob->op, blob);
57 if (bin->binary_length > 0) {
58 bindata->data = g_new (guchar, bin->binary_length);
59 bindata->data_length = bin->binary_length;
60 memcpy (bindata->data, bin->data, bin->binary_length);
61 }
62 }
63 else if (G_VALUE_TYPE ((GValue *) value) == GDA_TYPE_BINARY) {
64 GdaBinary *bin;
65
66 bin = (GdaBinary *) gda_value_get_binary ((GValue *) value);
67 if (bin && bin->binary_length > 0) {
68 bindata->data = g_new (guchar, bin->binary_length);
69 bindata->data_length = bin->binary_length;
70 memcpy (bindata->data, bin->data, bin->binary_length);
71 }
72 else {
73 *stock = GTK_STOCK_DIALOG_ERROR;
74 g_set_error (error, GDAUI_DATA_ENTRY_ERROR, GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
75 "%s", _("No data"));
76 allok = FALSE;
77 }
78 }
79 else if (G_VALUE_TYPE ((GValue *) value) == G_TYPE_STRING) {
80 const gchar *str;
81
82 str = g_value_get_string (value);
83 if (str) {
84 switch (options->encoding) {
85 case ENCODING_NONE:
86 bindata->data = (guchar *) g_strdup (str);
87 bindata->data_length = strlen ((gchar *) bindata->data);
88 break;
89 case ENCODING_BASE64: {
90 gsize out_len;
91 bindata->data = g_base64_decode (str, &out_len);
92 if (out_len > 0)
93 bindata->data_length = out_len;
94 else {
95 g_free (bindata->data);
96 bindata->data = NULL;
97 bindata->data_length = 0;
98 }
99 break;
100 }
101 }
102 }
103 else {
104 *stock = GTK_STOCK_MISSING_IMAGE;
105 g_set_error (error, GDAUI_DATA_ENTRY_ERROR, GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
106 "%s", _("Empty data"));
107 allok = FALSE;
108 }
109 }
110 else {
111 *stock = GTK_STOCK_DIALOG_ERROR;
112 g_set_error (error, GDAUI_DATA_ENTRY_ERROR, GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
113 "%s", _("Unhandled type of data"));
114 allok = FALSE;
115 }
116 }
117 }
118 else {
119 *stock = GTK_STOCK_MISSING_IMAGE;
120 g_set_error (error, GDAUI_DATA_ENTRY_ERROR, GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
121 "%s", _("Empty data"));
122 allok = FALSE;
123 }
124
125 return allok;
126 }
127
128 static void
compute_reduced_size(gint width,gint height,PictAllocation * allocation,gint * out_width,gint * out_height)129 compute_reduced_size (gint width, gint height, PictAllocation *allocation,
130 gint *out_width, gint *out_height)
131 {
132 gint reqw, reqh;
133
134 reqw = allocation->width;
135 reqh = allocation->height;
136
137 if ((reqw < width) || (reqh < height)) {
138 gint w, h;
139
140 if ((double) height * (double) reqw > (double) width * (double) reqh) {
141 w = 0.5 + (double) width * (double) reqh / (double) height;
142 h = reqh;
143 } else {
144 h = 0.5 + (double) height * (double) reqw / (double) width;
145 w = reqw;
146 }
147 *out_width = w;
148 *out_height = h;
149 }
150 else {
151 *out_width = width;
152 *out_height = height;
153 }
154 }
155
156 static void
loader_size_prepared_cb(GdkPixbufLoader * loader,gint width,gint height,PictAllocation * allocation)157 loader_size_prepared_cb (GdkPixbufLoader *loader, gint width, gint height, PictAllocation *allocation)
158 {
159 gint w, h;
160
161 compute_reduced_size (width, height, allocation, &w, &h);
162 if ((w != width) || (h != height))
163 gdk_pixbuf_loader_set_size (loader, w, h);
164
165 /*
166 gint reqw, reqh;
167
168 reqw = allocation->width;
169 reqh = allocation->height;
170
171 if ((reqw < width) || (reqh < height)) {
172 gint w, h;
173
174 if ((double) height * (double) reqw > (double) width * (double) reqh) {
175 w = 0.5 + (double) width * (double) reqh / (double) height;
176 h = reqh;
177 } else {
178 h = 0.5 + (double) height * (double) reqw / (double) width;
179 w = reqw;
180 }
181
182 gdk_pixbuf_loader_set_size (loader, w, h);
183 }
184 */
185 }
186
187 /*
188 * Creates a GdkPixbuf from @bindata and @options; returns NULL if an error occured.
189 *
190 * if @allocation is %NULL, then the GdaPixbuf will have the real size of the image.
191 */
192 GdkPixbuf *
common_pict_make_pixbuf(PictOptions * options,PictBinData * bindata,PictAllocation * allocation,const gchar ** stock,GError ** error)193 common_pict_make_pixbuf (PictOptions *options, PictBinData *bindata, PictAllocation *allocation,
194 const gchar **stock, GError **error)
195 {
196 GdkPixbuf *retpixbuf = NULL;
197
198 if (bindata->data) {
199 if (options->serialize) {
200 GdkPixdata pixdata;
201 GError *loc_error = NULL;
202
203 if (!gdk_pixdata_deserialize (&pixdata, bindata->data_length,
204 bindata->data, &loc_error)) {
205 g_free (bindata->data);
206 bindata->data = NULL;
207 bindata->data_length = 0;
208
209 *stock = GTK_STOCK_DIALOG_ERROR;
210 g_set_error (error, GDAUI_DATA_ENTRY_ERROR, GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
211 _("Error while deserializing data:\n%s"),
212 loc_error && loc_error->message ? loc_error->message : _("No detail"));
213
214 g_error_free (loc_error);
215 }
216 else {
217 retpixbuf = gdk_pixbuf_from_pixdata (&pixdata, FALSE, &loc_error);
218 if (!retpixbuf) {
219 *stock = GTK_STOCK_DIALOG_ERROR;
220 g_set_error (error, GDAUI_DATA_ENTRY_ERROR, GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
221 _("Error while interpreting data as an image:\n%s"),
222 loc_error && loc_error->message ? loc_error->message : _("No detail"));
223 g_error_free (loc_error);
224 }
225 else {
226 /* scale resulting pixbuf */
227 GdkPixbuf *tmp;
228 gint width, height, w, h;
229 width = gdk_pixbuf_get_width (retpixbuf);
230 height = gdk_pixbuf_get_height (retpixbuf);
231 compute_reduced_size (width, height, allocation, &w, &h);
232 if ((w != width) || (h != height)) {
233 tmp = gdk_pixbuf_scale_simple (retpixbuf, w, h,
234 GDK_INTERP_BILINEAR);
235 if (tmp) {
236 g_object_unref (retpixbuf);
237 retpixbuf = tmp;
238 }
239 }
240 }
241 }
242 }
243 else {
244 GdkPixbufLoader *loader;
245 GError *loc_error = NULL;
246
247 loader = gdk_pixbuf_loader_new ();
248 if (allocation)
249 g_signal_connect (G_OBJECT (loader), "size-prepared",
250 G_CALLBACK (loader_size_prepared_cb), allocation);
251 if (gdk_pixbuf_loader_write (loader, bindata->data, bindata->data_length, &loc_error) &&
252 gdk_pixbuf_loader_close (loader, &loc_error)) {
253 retpixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
254 if (!retpixbuf) {
255 if (loc_error)
256 g_propagate_error (error, loc_error);
257 *stock = GTK_STOCK_MISSING_IMAGE;
258 }
259 else
260 g_object_ref (retpixbuf);
261 }
262 else {
263 gchar *notice_msg;
264 notice_msg = g_strdup_printf (_("Error while interpreting data as an image:\n%s"),
265 loc_error && loc_error->message ? loc_error->message : _("No detail"));
266 *stock = GTK_STOCK_DIALOG_WARNING;
267 g_set_error_literal (error, loc_error ? loc_error->domain : GDAUI_DATA_ENTRY_ERROR,
268 loc_error ? loc_error->code : GDAUI_DATA_ENTRY_INVALID_DATA_ERROR,
269 notice_msg);
270 g_error_free (loc_error);
271 g_free (notice_msg);
272 }
273
274 g_object_unref (loader);
275 }
276 }
277
278 return retpixbuf;
279 }
280
281 /*
282 * Creates a new popup menu, attaches it to @ettach_to. The actions are in reference to @bindata,
283 * and the @callback callback is called when the data in @bindata has been modified
284 */
285 typedef struct {
286 PictBinData *bindata;
287 PictOptions *options;
288 PictCallback callback;
289 gpointer data;
290 } PictMenuData;
291 static void file_load_cb (GtkWidget *button, PictMenuData *menudata);
292 static void file_save_cb (GtkWidget *button, PictMenuData *menudata);
293 static void copy_cb (GtkWidget *button, PictMenuData *menudata);
294
295 /* Commented out because it's not used:
296 static void
297 menudata_free (PictMenuData *menudata)
298 {
299 if (menudata->bindata) {
300 g_free (menudata->bindata->data);
301 g_free (menudata->bindata);
302 }
303 g_free (menudata);
304 }
305 */
306
307 void
common_pict_create_menu(PictMenu * pictmenu,GtkWidget * attach_to,PictBinData * bindata,PictOptions * options,PictCallback callback,gpointer data)308 common_pict_create_menu (PictMenu *pictmenu, GtkWidget *attach_to, PictBinData *bindata, PictOptions *options,
309 PictCallback callback, gpointer data)
310 {
311 GtkWidget *menu, *mitem;
312 PictMenuData *menudata;
313
314 menudata = g_new (PictMenuData, 1);
315 menudata->bindata = g_new (PictBinData, 1);
316 menudata->bindata->data = g_memdup (bindata->data, bindata->data_length);
317 menudata->bindata->data_length = bindata->data_length;
318 menudata->options = options;
319 menudata->callback = callback;
320 menudata->data = data;
321
322 menu = gtk_menu_new ();
323 g_object_set_data_full (G_OBJECT (menu), "menudata", menudata, g_free);
324 g_signal_connect (menu, "deactivate",
325 G_CALLBACK (gtk_widget_hide), NULL);
326 pictmenu->menu = menu;
327
328 mitem = gtk_menu_item_new_with_mnemonic (_("_Copy image"));
329 gtk_widget_show (mitem);
330 gtk_container_add (GTK_CONTAINER (menu), mitem);
331 g_signal_connect (mitem, "activate",
332 G_CALLBACK (copy_cb), menudata);
333 gtk_widget_set_sensitive (mitem, bindata->data ? TRUE : FALSE);
334 pictmenu->copy_mitem = mitem;
335
336 mitem = gtk_menu_item_new_with_mnemonic (_("_Load image from file"));
337 gtk_widget_show (mitem);
338 gtk_container_add (GTK_CONTAINER (menu), mitem);
339 g_signal_connect (mitem, "activate",
340 G_CALLBACK (file_load_cb), menudata);
341 pictmenu->load_mitem = mitem;
342
343 mitem = gtk_menu_item_new_with_mnemonic (_("_Save image"));
344 gtk_widget_show (mitem);
345 gtk_container_add (GTK_CONTAINER (menu), mitem);
346 g_signal_connect (mitem, "activate",
347 G_CALLBACK (file_save_cb), menudata);
348 gtk_widget_set_sensitive (mitem, bindata->data ? TRUE : FALSE);
349 pictmenu->save_mitem = mitem;
350
351 gtk_menu_attach_to_widget (GTK_MENU (menu), attach_to, NULL);
352 }
353
354 static void
file_load_cb(GtkWidget * button,PictMenuData * menudata)355 file_load_cb (GtkWidget *button, PictMenuData *menudata)
356 {
357 GtkWidget *dlg;
358 GtkFileFilter *filter;
359
360 dlg = gtk_file_chooser_dialog_new (_("Select image to load"),
361 GTK_WINDOW (gtk_widget_get_toplevel (button)),
362 GTK_FILE_CHOOSER_ACTION_OPEN,
363 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
364 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
365 NULL);
366 filter = gtk_file_filter_new ();
367 gtk_file_filter_add_pixbuf_formats (filter);
368 gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dlg), filter);
369 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg),
370 gdaui_get_default_path ());
371
372 if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
373 char *filename;
374 gsize length;
375 GError *error = NULL;
376 gchar *data;
377
378 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
379 gdaui_set_default_path (gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg)));
380
381 if (g_file_get_contents (filename, &data, &length, &error)) {
382 g_free (menudata->bindata->data);
383 menudata->bindata->data = NULL;
384 menudata->bindata->data_length = 0;
385
386 if (menudata->options->serialize) {
387 GdkPixdata pixdata;
388 GdkPixbuf *pixbuf;
389 guint stream_length;
390
391 pixbuf = gdk_pixbuf_new_from_file (filename, &error);
392 if (pixbuf) {
393 gdk_pixdata_from_pixbuf (&pixdata, pixbuf, TRUE);
394 menudata->bindata->data = gdk_pixdata_serialize (&pixdata, &stream_length);
395 menudata->bindata->data_length = stream_length;
396
397 g_object_unref (pixbuf);
398 g_free (data);
399 }
400 else {
401 menudata->bindata->data = (guchar *) data;
402 menudata->bindata->data_length = length;
403 }
404 }
405 else {
406 menudata->bindata->data = (guchar *) data;
407 menudata->bindata->data_length = length;
408 }
409
410 /* call the callback */
411 if (menudata->callback)
412 (menudata->callback) (menudata->bindata, menudata->data);
413 menudata->bindata->data = NULL;
414 menudata->bindata->data_length = 0;
415 }
416 else {
417 GtkWidget *msg;
418
419 msg = gtk_message_dialog_new_with_markup (GTK_WINDOW (gtk_widget_get_toplevel (button)),
420 GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
421 GTK_BUTTONS_CLOSE,
422 _("Could not load the contents of '%s':\n %s"),
423 filename,
424 error && error->message ? error->message : _("No detail"));
425 if (error)
426 g_error_free (error);
427 gtk_widget_destroy (dlg);
428 dlg = NULL;
429
430 gtk_dialog_run (GTK_DIALOG (msg));
431 gtk_widget_destroy (msg);
432 }
433 g_free (filename);
434 }
435
436 if (dlg)
437 gtk_widget_destroy (dlg);
438 }
439
440 typedef struct {
441 GtkComboBox *combo;
442 GSList *formats;
443 } PictFormat;
444
445 static void
add_if_writable(GdkPixbufFormat * data,PictFormat * format)446 add_if_writable (GdkPixbufFormat *data, PictFormat *format)
447 {
448 if (gdk_pixbuf_format_is_writable (data)) {
449 gchar *str;
450
451 str= g_strdup_printf ("%s (%s)", gdk_pixbuf_format_get_name (data),
452 gdk_pixbuf_format_get_description (data));
453 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (format->combo), str);
454 g_free (str);
455 format->formats = g_slist_append (format->formats, g_strdup (gdk_pixbuf_format_get_name (data)));
456 }
457 }
458
459 static void
file_save_cb(GtkWidget * button,PictMenuData * menudata)460 file_save_cb (GtkWidget *button, PictMenuData *menudata)
461 {
462 GtkWidget *dlg;
463 GtkWidget *combo, *expander, *hbox, *label;
464 GSList *formats;
465 PictFormat pictformat;
466
467 /* determine writable formats */
468 expander = gtk_expander_new (_("Image format"));
469 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
470 gtk_container_add (GTK_CONTAINER (expander), hbox);
471
472 label = gtk_label_new (_("Format image as:"));
473 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
474 combo = gtk_combo_box_text_new ();
475 gtk_box_pack_start (GTK_BOX (hbox), combo, TRUE, TRUE, 0);
476 gtk_widget_show_all (hbox);
477
478 formats = gdk_pixbuf_get_formats ();
479 pictformat.combo = (GtkComboBox*) combo;
480 pictformat.formats = NULL;
481 g_slist_foreach (formats, (GFunc) add_if_writable, &pictformat);
482 g_slist_free (formats);
483
484 gtk_combo_box_text_prepend_text (GTK_COMBO_BOX_TEXT (combo), _("Current format"));
485 gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0);
486
487 dlg = gtk_file_chooser_dialog_new (_("Select a file to save the image to"),
488 GTK_WINDOW (gtk_widget_get_toplevel (button)),
489 GTK_FILE_CHOOSER_ACTION_SAVE,
490 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
491 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
492 NULL);
493 gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg),
494 gdaui_get_default_path ());
495
496 gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (dlg), expander);
497 if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
498 char *filename;
499 gboolean allok = TRUE;
500 GError *error = NULL;
501 gint format;
502
503 format = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
504 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
505 gdaui_set_default_path (gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg)));
506
507 if (format == 0) {
508 /* save data AS IS */
509 allok = g_file_set_contents (filename, (gchar *) menudata->bindata->data,
510 menudata->bindata->data_length, &error);
511 }
512 else {
513 /* export data to another format */
514 GdkPixbuf *pixbuf;
515 gchar *format_str;
516 const gchar *stock;
517
518 format_str = g_slist_nth_data (pictformat.formats, format - 1);
519 pixbuf = common_pict_make_pixbuf (menudata->options, menudata->bindata, NULL, &stock, &error);
520 if (pixbuf) {
521 allok = gdk_pixbuf_save (pixbuf, filename, format_str, &error, NULL);
522 g_object_unref (pixbuf);
523 }
524 else
525 allok = FALSE;
526 }
527
528 if (!allok) {
529 GtkWidget *msg;
530
531 msg = gtk_message_dialog_new_with_markup (GTK_WINDOW (gtk_widget_get_toplevel (button)),
532 GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
533 GTK_BUTTONS_CLOSE,
534 _("Could not save the image to '%s':\n %s"),
535 filename,
536 error && error->message ? error->message : _("No detail"));
537 if (error)
538 g_error_free (error);
539 gtk_widget_destroy (dlg);
540 dlg = NULL;
541
542 gtk_dialog_run (GTK_DIALOG (msg));
543 gtk_widget_destroy (msg);
544 }
545 g_free (filename);
546 }
547
548 if (dlg)
549 gtk_widget_destroy (dlg);
550
551 g_slist_foreach (pictformat.formats, (GFunc) g_free, NULL);
552 g_slist_free (pictformat.formats);
553 }
554
555 static void
copy_cb(G_GNUC_UNUSED GtkWidget * button,PictMenuData * menudata)556 copy_cb (G_GNUC_UNUSED GtkWidget *button, PictMenuData *menudata)
557 {
558 GtkClipboard *cp;
559 cp = gtk_clipboard_get (gdk_atom_intern_static_string ("CLIPBOARD"));
560 if (!cp)
561 return;
562
563 GdkPixbufLoader *loader;
564 GdkPixbuf *pixbuf = NULL;
565 loader = gdk_pixbuf_loader_new ();
566 if (gdk_pixbuf_loader_write (loader, menudata->bindata->data,
567 menudata->bindata->data_length, NULL)) {
568 if (gdk_pixbuf_loader_close (loader, NULL)) {
569 pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
570 g_object_ref (pixbuf);
571 }
572 else
573 gdk_pixbuf_loader_close (loader, NULL);
574 }
575 else
576 gdk_pixbuf_loader_close (loader, NULL);
577 g_object_unref (loader);
578
579 if (pixbuf) {
580 gtk_clipboard_set_image (cp, pixbuf);
581 g_object_unref (pixbuf);
582 }
583 else
584 gtk_clipboard_set_image (cp, NULL);
585 }
586
587 /*
588 * adjust the sensitiveness of the menu items in the popup menu
589 */
590 void
common_pict_adjust_menu_sensitiveness(PictMenu * pictmenu,gboolean editable,PictBinData * bindata)591 common_pict_adjust_menu_sensitiveness (PictMenu *pictmenu, gboolean editable, PictBinData *bindata)
592 {
593 if (!pictmenu || !pictmenu->menu)
594 return;
595 gtk_widget_set_sensitive (pictmenu->load_mitem, editable);
596 gtk_widget_set_sensitive (pictmenu->save_mitem, bindata->data ? TRUE : FALSE);
597 gtk_widget_set_sensitive (pictmenu->copy_mitem, bindata->data ? TRUE : FALSE);
598 }
599
600 /*
601 * Inits the hash table in @options
602 */
603 void
common_pict_init_cache(PictOptions * options)604 common_pict_init_cache (PictOptions *options)
605 {
606 g_assert (!options->pixbuf_hash);
607 options->pixbuf_hash = g_hash_table_new_full (g_int_hash, g_int_equal,
608 g_free, g_object_unref);
609 }
610
611 /*
612 * computes a "hash" of the binary data
613 */
614 static gint
compute_hash(guchar * data,glong data_length)615 compute_hash (guchar *data, glong data_length)
616 {
617 gint result = 0;
618 guchar *ptr;
619
620 if (!data)
621 return 0;
622 for (ptr = data; ptr <= data + data_length - 1; ptr++)
623 result += *ptr;
624
625 return result;
626 }
627
628 /*
629 * Adds @pixbuf in the cache
630 */
631 void
common_pict_add_cached_pixbuf(PictOptions * options,const GValue * value,GdkPixbuf * pixbuf)632 common_pict_add_cached_pixbuf (PictOptions *options, const GValue *value, GdkPixbuf *pixbuf)
633 {
634 gint *hash;
635 g_return_if_fail (pixbuf);
636
637 if (!options->pixbuf_hash || !value)
638 return;
639 else if (GDA_VALUE_HOLDS_BINARY (value)) {
640 const GdaBinary *bin;
641 bin = gda_value_get_binary (value);
642 hash = g_new (gint, 1);
643 *hash = compute_hash (bin->data, bin->binary_length);
644 g_hash_table_insert (options->pixbuf_hash, hash, g_object_ref (pixbuf));
645 }
646 else if (GDA_VALUE_HOLDS_BLOB (value)) {
647 const GdaBinary *bin;
648 const GdaBlob *blob;
649 blob = gda_value_get_blob (value);
650 bin = (GdaBinary *) blob;
651 if (bin) {
652 if (!bin->data && blob->op)
653 gda_blob_op_read_all (blob->op, (GdaBlob*) blob);
654 hash = g_new (gint, 1);
655 *hash = compute_hash (bin->data, bin->binary_length);
656 g_hash_table_insert (options->pixbuf_hash, hash, g_object_ref (pixbuf));
657 }
658 }
659 }
660
661 /*
662 * Tries to find a cached pixbuf
663 */
664 GdkPixbuf *
common_pict_fetch_cached_pixbuf(PictOptions * options,const GValue * value)665 common_pict_fetch_cached_pixbuf (PictOptions *options, const GValue *value)
666 {
667 GdkPixbuf *pixbuf = NULL;
668 gint hash;
669
670 if (!options->pixbuf_hash)
671 return NULL;
672 if (!value)
673 return NULL;
674 else if (GDA_VALUE_HOLDS_BINARY (value)) {
675 const GdaBinary *bin;
676 bin = gda_value_get_binary (value);
677 if (bin) {
678 hash = compute_hash (bin->data, bin->binary_length);
679 pixbuf = g_hash_table_lookup (options->pixbuf_hash, &hash);
680 }
681 }
682 else if (GDA_VALUE_HOLDS_BLOB (value)) {
683 const GdaBinary *bin;
684 const GdaBlob *blob;
685 blob = gda_value_get_blob (value);
686 bin = (GdaBinary *) blob;
687 if (bin) {
688 if (!bin->data && blob->op)
689 gda_blob_op_read_all (blob->op, (GdaBlob*) blob);
690 hash = compute_hash (bin->data, bin->binary_length);
691 pixbuf = g_hash_table_lookup (options->pixbuf_hash, &hash);
692 }
693 }
694
695 return pixbuf;
696 }
697
698 /*
699 * clears all the cached pixbuf objects
700 */
701 void
common_pict_clear_pixbuf_cache(PictOptions * options)702 common_pict_clear_pixbuf_cache (PictOptions *options)
703 {
704 if (!options->pixbuf_hash)
705 return;
706 g_hash_table_remove_all (options->pixbuf_hash);
707 }
708
709 /*
710 * Fills @options with the correct values parsed from @options_str
711 */
712 void
common_pict_parse_options(PictOptions * options,const gchar * options_str)713 common_pict_parse_options (PictOptions *options, const gchar *options_str)
714 {
715 if (options_str && *options_str) {
716 GdaQuarkList *params;
717 const gchar *str;
718
719 params = gda_quark_list_new_from_string (options_str);
720 str = gda_quark_list_find (params, "ENCODING");
721 if (str) {
722 if (!strcmp (str, "base64"))
723 options->encoding = ENCODING_BASE64;
724 }
725 str = gda_quark_list_find (params, "SERIALIZE");
726 if (str) {
727 if ((*str == 't') || (*str == 'T'))
728 options->serialize = TRUE;
729 }
730 gda_quark_list_free (params);
731 }
732 }
733
734 /*
735 * Creates a new GValue from the data in @bindata, using @options, of type @gtype
736 */
737 GValue *
common_pict_get_value(PictBinData * bindata,PictOptions * options,GType gtype)738 common_pict_get_value (PictBinData *bindata, PictOptions *options, GType gtype)
739 {
740 GValue *value = NULL;
741
742 if (bindata->data) {
743 if (gtype == GDA_TYPE_BLOB)
744 value = gda_value_new_blob (bindata->data, bindata->data_length);
745 else if (gtype == GDA_TYPE_BINARY)
746 value = gda_value_new_binary (bindata->data, bindata->data_length);
747 else if (gtype == G_TYPE_STRING) {
748 gchar *str = NULL;
749
750 switch (options->encoding) {
751 case ENCODING_NONE:
752 str = g_strndup ((gchar *) bindata->data,
753 bindata->data_length);
754 break;
755 case ENCODING_BASE64:
756 str = g_base64_encode (bindata->data, bindata->data_length);
757 break;
758 }
759
760 value = gda_value_new (G_TYPE_STRING);
761 g_value_take_string (value, str);
762 }
763 else
764 g_assert_not_reached ();
765 }
766
767 if (!value) {
768 /* in case the gda_data_handler_get_value_from_sql() returned an error because
769 the contents of the GtkEntry cannot be interpreted as a GValue */
770 value = gda_value_new_null ();
771 }
772
773 return value;
774 }
775