1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
move_byte_forward(char * dest_m,const char * src_m,size_t count)18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA  02110-1301, USA.
20  */
21 
22 #include <libgda/gda-value.h>
23 #include "common-bin.h"
24 #include "../internal/popup-container.h"
25 #include <string.h>
26 #include <gtk/gtk.h>
27 #include <glib/gi18n-lib.h>
28 #include <libgda/gda-blob-op.h>
29 
30 #ifdef HAVE_GIO
31 #include <gio/gio.h>
32 #endif
33 
34 static void
35 file_load_cb (GtkWidget *button, BinMenu *menu)
36 {
37 	GtkWidget *dlg;
38 
39 	gtk_widget_hide (menu->popup);
40         dlg = gtk_file_chooser_dialog_new (_("Select file to load"),
41                                            GTK_WINDOW (gtk_widget_get_toplevel (button)),
42                                            GTK_FILE_CHOOSER_ACTION_OPEN,
43                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
44                                            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
45                                            NULL);
46 	if (menu->current_folder)
47 		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg), menu->current_folder);
48 
49 	if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
50                 char *filename;
51                 gsize length;
52                 GError *error = NULL;
53                 gchar *data;
54 
55                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
56 
57 		if (menu->entry_type == GDA_TYPE_BLOB) {
58 			menu->loaded_value_cb (menu->loaded_value_cb_data,
59 					       gda_value_new_blob_from_file (filename));
60 		}
61 		else if (menu->entry_type == GDA_TYPE_BINARY) {
62 			if (g_file_get_contents (filename, &data, &length, &error)) {
63 				GdaBinary *bin;
64 				GValue *nvalue;
65 				bin = g_new0 (GdaBinary, 1);
66 				bin->data = (guchar*) data;
67 				bin->binary_length = length;
68 				nvalue = gda_value_new (GDA_TYPE_BINARY);
69 				gda_value_take_binary (nvalue, bin);
70 
71 				menu->loaded_value_cb (menu->loaded_value_cb_data, nvalue);
72 			}
73 			else {
74 				GtkWidget *msg;
75 
76 				msg = gtk_message_dialog_new_with_markup (GTK_WINDOW (gtk_widget_get_toplevel (button)),
77 						   GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
78 						   GTK_BUTTONS_CLOSE,
79 						   _("Could not load the contents of '%s':\n %s"),
80 						   filename,
81 						   error && error->message ? error->message : _("No detail"));
82 				if (error)
83 					g_error_free (error);
84 				gtk_widget_destroy (dlg);
85 				dlg = NULL;
86 
87 				gtk_dialog_run (GTK_DIALOG (msg));
88 				gtk_widget_destroy (msg);
89 			}
90 		}
91 		else
92 			g_assert_not_reached ();
93 
94                 g_free (filename);
95         }
96 
97         if (dlg) {
98 		g_free (menu->current_folder);
99 		menu->current_folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg));
100                 gtk_widget_destroy (dlg);
101 	}
102 }
103 
104 static void
105 file_save_cb (GtkWidget *button, BinMenu *menu)
106 {
107 	GtkWidget *dlg;
108 
109 	gtk_widget_hide (menu->popup);
110         dlg = gtk_file_chooser_dialog_new (_("Select a file to save data to"),
111                                            GTK_WINDOW (gtk_widget_get_toplevel (button)),
112                                            GTK_FILE_CHOOSER_ACTION_SAVE,
113                                            GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
114                                            GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
115                                            NULL);
116 
117 	if (menu->current_folder)
118 		gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dlg), menu->current_folder);
119 
120         if (gtk_dialog_run (GTK_DIALOG (dlg)) == GTK_RESPONSE_ACCEPT) {
121                 char *filename;
122                 gboolean allok = TRUE;
123                 GError *error = NULL;
124 
125                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dlg));
126 		if (menu->entry_type == GDA_TYPE_BINARY) {
127 			const GdaBinary *bin;
128 			bin = gda_value_get_binary (menu->tmpvalue);
129 			allok = g_file_set_contents (filename, (gchar *) bin->data,
130 						     bin->binary_length, &error);
131 		}
132 		else if (menu->entry_type == GDA_TYPE_BLOB) {
133 			GdaBlob *blob;
134 			blob = (GdaBlob*) gda_value_get_blob (menu->tmpvalue);
135 			if (blob->op) {
136 
137 				GValue *dest_value;
138 				GdaBlob *dest_blob;
139 
140 				dest_value = gda_value_new_blob_from_file (filename);
141 				dest_blob = (GdaBlob*) gda_value_get_blob (dest_value);
142 				allok = gda_blob_op_write_all (dest_blob->op, (GdaBlob*) blob);
143 				gda_value_free (dest_value);
144 			}
145 			else
146 				allok = g_file_set_contents (filename, (gchar *) ((GdaBinary*)blob)->data,
147 							     ((GdaBinary*)blob)->binary_length, &error);
148 		}
149 		else
150 			g_assert_not_reached ();
151                 if (!allok) {
152                         GtkWidget *msg;
153 			msg = gtk_message_dialog_new_with_markup (GTK_WINDOW (gtk_widget_get_toplevel (button)),
154                                                                   GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
155                                                                   GTK_BUTTONS_CLOSE,
156                                                                   _("Could not save data to '%s':\n %s"),
157                                                                   filename,
158                                                                   error && error->message ? error->message : _("No detail"));
159                         if (error)
160                                 g_error_free (error);
161                         gtk_widget_destroy (dlg);
162                         dlg = NULL;
163 
164                         gtk_dialog_run (GTK_DIALOG (msg));
165                         gtk_widget_destroy (msg);
166                 }
167                 g_free (filename);
168         }
169 
170         if (dlg) {
171 		g_free (menu->current_folder);
172 		menu->current_folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (dlg));
173                 gtk_widget_destroy (dlg);
174 
175 	}
176 }
177 
178 void
179 common_bin_create_menu (BinMenu *binmenu, PopupContainerPositionFunc pos_func, GType entry_type,
180 			BinCallback loaded_value_cb, gpointer loaded_value_cb_data)
181 {
182 	GtkWidget *popup, *vbox, *hbox, *bbox, *button, *label;
183 	gchar *str;
184 
185 	binmenu->entry_type = entry_type;
186 	binmenu->loaded_value_cb = loaded_value_cb;
187 	binmenu->loaded_value_cb_data = loaded_value_cb_data;
188 
189 	popup = popup_container_new_with_func (pos_func);
190 	binmenu->popup = popup;
191 
192 	vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
193 	gtk_container_add (GTK_CONTAINER (popup), vbox);
194 
195 	label = gtk_label_new ("");
196 	str = g_strdup_printf ("<b>%s:</b>", _("Properties"));
197 	gtk_label_set_markup (GTK_LABEL (label), str);
198 	g_free (str);
199 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
200 	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
201 
202 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
203         gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 5);
204         gtk_widget_show (hbox);
205         label = gtk_label_new ("    ");
206         gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
207         gtk_widget_show (label);
208 
209 	label = gtk_label_new ("");
210 	gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
211 	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
212 	binmenu->props_label = label;
213 
214 	bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
215 	gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
216 
217 	button = gtk_button_new_from_stock (GTK_STOCK_OPEN);
218 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
219 	g_signal_connect (button, "clicked",
220 			  G_CALLBACK (file_load_cb), binmenu);
221 	binmenu->load_button = button;
222 
223 	button = gtk_button_new_from_stock (GTK_STOCK_SAVE_AS);
224 	gtk_box_pack_start (GTK_BOX (bbox), button, FALSE, FALSE, 0);
225 	g_signal_connect (button, "clicked",
226 			  G_CALLBACK (file_save_cb), binmenu);
227 	binmenu->save_button = button;
228 
229 	gtk_widget_show_all (vbox);
230 }
231 
232 static gchar *
233 format_size (gulong size)
234 {
235 	if (size < 1024)
236 		return g_strdup_printf (ngettext ("%lu Byte", "%lu Bytes", size), size);
237 	else if (size < 1048576)
238 		return g_strdup_printf ("%.1f Kio", (gfloat) (size / 1024));
239 	else if (size < 1073741824)
240 		return g_strdup_printf ("%.1f Mio", (gfloat) (size / 1048576));
241 	else
242 		return g_strdup_printf ("%.1f Gio", (gfloat) (size / 1073741824));
243 }
244 
245 /*
246  * adjust the sensitiveness of the menu items in the popup menu
247  */
248 void
249 common_bin_adjust_menu (BinMenu *binmenu, gboolean editable, const GValue *value)
250 {
251 	gchar *size;
252 	GString *string;
253 #ifdef HAVE_GIO
254 	gchar *ctype = NULL;
255 #endif
256 
257 	if (!binmenu || !binmenu->popup)
258 		return;
259 
260 	if (binmenu->tmpvalue) {
261 		gda_value_free (binmenu->tmpvalue);
262 		binmenu->tmpvalue = NULL;
263 	}
264 	string = g_string_new ("");
265 	if (value) {
266 		binmenu->tmpvalue = gda_value_copy (value);
267 		if (G_VALUE_TYPE (value) == GDA_TYPE_NULL)
268 			g_string_append_printf (string, "<i>%s</i>", _("No data"));
269 		else if (G_VALUE_TYPE (value) == GDA_TYPE_BINARY) {
270 			const GdaBinary *bin;
271 			bin = gda_value_get_binary (value);
272 			size = format_size (bin->binary_length);
273 			g_string_append_printf (string, "%s: %s", _("Data size"), size);
274 			g_free (size);
275 #ifdef HAVE_GIO
276 			ctype = g_content_type_guess (NULL, bin->data, (gsize) bin->binary_length, NULL);
277 #endif
278 		}
279 		else if (G_VALUE_TYPE (value) == GDA_TYPE_BLOB) {
280 			const GdaBlob *blob;
281 			GdaBinary *bin;
282 			blob = gda_value_get_blob (value);
283 			bin = (GdaBinary *) blob;
284 			if (blob->op) {
285 				glong len;
286 				len = gda_blob_op_get_length (blob->op);
287 				if (len >= 0) {
288 					size = format_size (len);
289 					g_string_append_printf (string, "%s: %s", _("Data size"), size);
290 					g_free (size);
291 #ifdef HAVE_GIO
292 					GdaBlob *blob2;
293 					blob2 = (GdaBlob*) gda_blob_copy ((gpointer) blob);
294 					gda_blob_op_read (blob2->op, blob2, 0, 1024);
295 					bin = (GdaBinary *) blob2;
296 					ctype = g_content_type_guess (NULL, bin->data,
297 								      (gsize) bin->binary_length, NULL);
298 					gda_blob_free ((gpointer) blob2);
299 #endif
300 				}
301 				else
302 					g_string_append_printf (string, "%s: %s", _("Data size"), _("Unknown"));
303 			}
304 			else {
305 				size = format_size (bin->binary_length);
306 				g_string_append_printf (string, "%s: %s", _("Data size"), size);
307 				g_free (size);
308 #ifdef HAVE_GIO
309 				ctype = g_content_type_guess (NULL, bin->data, (gsize) bin->binary_length, NULL);
310 #endif
311 			}
312 		}
313 		else
314 			g_assert_not_reached ();
315 	}
316 	else
317 		g_string_append_printf (string, "<i>%s</i>", _("No data"));
318 
319 #ifdef HAVE_GIO
320 	if (ctype) {
321 		GList *list;
322 		gchar *descr, *tmp;
323 		descr = g_content_type_get_description (ctype);
324 		tmp = g_markup_escape_text (descr, -1);
325 		g_free (descr);
326 		g_string_append_printf (string, "\n%s: %s", _("Data type"), tmp);
327 		g_free (tmp);
328 
329 		list = g_app_info_get_all_for_type (ctype);
330 		for (; list; list = list->next) {
331 			GAppInfo *ai;
332 			ai = (GAppInfo*) list->data;
333 			g_print ("\t open with %s (%s)\n", g_app_info_get_name (ai),
334 				 g_app_info_get_executable (ai));
335 		}
336 		g_free (ctype);
337 	}
338 #endif
339 
340 
341 	gtk_label_set_markup (GTK_LABEL (binmenu->props_label), string->str);
342 	g_string_free (string, TRUE);
343 
344 	gtk_widget_set_sensitive (binmenu->load_button, editable);
345 	gtk_widget_set_sensitive (binmenu->save_button, (value && !gda_value_is_null (value)) ? TRUE : FALSE);
346 }
347 
348 /*
349  * Reset @bonmenu's contents
350  */
351 void
352 common_bin_reset (BinMenu *binmenu)
353 {
354 	if (binmenu->tmpvalue) {
355 		gda_value_free (binmenu->tmpvalue);
356 		binmenu->tmpvalue = NULL;
357 	}
358 	if (binmenu->popup)
359 		gtk_widget_destroy (binmenu->popup);
360 	g_free (binmenu->current_folder);
361 
362 	memset (binmenu, 0, sizeof (BinMenu));
363 }
364