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