1 /*
2  * dialog-doc-metadata.c: Edit document metadata
3  *
4  * Copyright (C) 2005 Jody Goldberg (jody@gnome.org)
5  * Copyright (C) 2011 Andreas J. Guelzow (aguelzow@pyrshep.ca)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (at your option) version 3.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20  * USA
21  */
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <dialogs/dialogs.h>
25 #include <dialogs/help.h>
26 
27 #include <workbook.h>
28 #include <workbook-control.h>
29 #include <wbc-gtk.h>
30 #include <workbook-view.h>
31 #include <workbook-priv.h>
32 #include <gui-util.h>
33 #include <parse-util.h>
34 #include <value.h>
35 #include <expr.h>
36 #include <commands.h>
37 #include <number-match.h>
38 #include <dead-kittens.h>
39 
40 #include <gsf/gsf-doc-meta-data.h>
41 #include <gsf/gsf-meta-names.h>
42 #include <gsf/gsf-timestamp.h>
43 #include <gsf/gsf-docprop-vector.h>
44 
45 #include <goffice/goffice.h>
46 
47 
48 #include <glib-object.h>
49 #include <glib/gi18n-lib.h>
50 #include <glib/gprintf.h>
51 
52 #include <string.h>
53 
54 
55 #define DOC_METADATA_KEY "dialog-doc-metadata"
56 
57 enum {
58 	ITEM_ICON,
59 	ITEM_NAME,
60 	PAGE_NUMBER,
61 	NUM_COLUMNS
62 };
63 
64 typedef struct {
65 	GtkBuilder		*gui;
66 	GtkWidget		*dialog;
67 
68 	/*pointer to the document metadata*/
69 	GsfDocMetaData		*metadata;
70 
71 	gboolean		permissions_changed;
72 	GOFilePermissions	*file_permissions;
73 
74 	WBCGtk	*wbcg;
75 	Workbook                *wb;
76 	GODoc			*doc;
77 
78 	GtkTreeStore            *store;
79 	GtkTreeView             *view;
80 
81 	/* Dialog Widgets */
82 	GtkNotebook		*notebook;
83 	GtkButton		*help_button;
84 	GtkButton		*close_button;
85 
86 	/* File Information Page */
87 	GtkLabel		*file_name;
88 	GtkLabel		*location;
89 	GtkLabel		*created;
90 	GtkLabel		*modified;
91 	GtkLabel		*accessed;
92 	GtkLabel		*owner;
93 	GtkLabel		*group;
94 
95 	GtkCheckButton		*owner_read;
96 	GtkCheckButton		*owner_write;
97 
98 	GtkCheckButton		*group_read;
99 	GtkCheckButton		*group_write;
100 
101 	GtkCheckButton		*others_read;
102 	GtkCheckButton		*others_write;
103 
104 	/* Description Page */
105 	GtkEntry		*title;
106 	GtkEntry		*subject;
107 	GtkEntry		*author;
108 	GtkEntry		*manager;
109 	GtkEntry		*company;
110 	GtkEntry		*category;
111 
112 	GtkTextView		*comments;
113 
114 	/* Properties Page */
115 	GtkTreeView		*properties;
116 	GtkTreeStore		*properties_store;
117 
118 	GtkEntry	        *ppt_name;
119 	GtkEntry	        *ppt_value;
120 	GtkComboBox		*ppt_type;
121 	GtkListStore            *type_store;
122 	GtkTreeModelFilter      *type_store_filter;
123 
124 	GtkButton		*add_button;
125 	GtkButton		*remove_button;
126 
127 	GtkLabel                *instruction;
128 	GtkLabel                *warning;
129 
130 	/* Keyword Page */
131 	GtkTreeView             *key_tree_view;
132 	GtkListStore            *key_store;
133 	GtkButton               *key_add_button;
134 	GtkButton               *key_remove_button;
135 
136 	/* Statistics Page */
137 	GtkLabel		*sheets;
138 	GtkLabel		*cells;
139 	GtkLabel		*pages;
140 
141 	/* Calculation Page */
142 	GtkCheckButton		*recalc_auto;
143 	GtkCheckButton		*recalc_manual;
144 	GtkCheckButton		*recalc_iteration;
145 	GtkEntry		*recalc_max;
146 	GtkEntry		*recalc_tolerance;
147 	GtkWidget               *recalc_iteration_grid;
148 
149 } DialogDocMetaData;
150 
151 #define trim_string(s_) g_strstrip (g_strdup ((s_)))
152 
153 static gchar *dialog_doc_metadata_get_prop_val (DialogDocMetaData *state, char const *prop_name,
154 						GValue *prop_value);
155 
156 static gboolean cb_dialog_doc_metadata_ppt_changed (G_GNUC_UNUSED GtkEntry      *entry,
157 						    G_GNUC_UNUSED GdkEventFocus *event,
158 						    DialogDocMetaData *state);
159 
160 
161 static GType
dialog_doc_metadata_get_value_type_from_name(gchar const * name,GType def)162 dialog_doc_metadata_get_value_type_from_name (gchar const *name, GType def)
163 {
164 	/* shared by all instances and never freed */
165 	static GHashTable *dialog_doc_metadata_name_to_type = NULL;
166 	gpointer res;
167 
168 	if (NULL == dialog_doc_metadata_name_to_type) {
169 		static struct {
170 			char const *name;
171 			GType type;
172 		} const map [] = {
173 			{GSF_META_NAME_GENERATOR,            G_TYPE_STRING},
174 			{GSF_META_NAME_INITIAL_CREATOR,      G_TYPE_STRING},
175 			{GSF_META_NAME_CREATOR,              G_TYPE_STRING},
176 			{GSF_META_NAME_TITLE,                G_TYPE_STRING},
177 			{GSF_META_NAME_SUBJECT,              G_TYPE_STRING},
178 			{GSF_META_NAME_MANAGER,              G_TYPE_STRING},
179 			{GSF_META_NAME_COMPANY,              G_TYPE_STRING},
180 			{GSF_META_NAME_CATEGORY,             G_TYPE_STRING},
181 			{GSF_META_NAME_DESCRIPTION,          G_TYPE_STRING},
182 			{GSF_META_NAME_LAST_SAVED_BY,        G_TYPE_STRING},
183 			{GSF_META_NAME_TEMPLATE,             G_TYPE_STRING},
184 			{GSF_META_NAME_EDITING_DURATION,     G_TYPE_STRING}, /* special */
185 			{GSF_META_NAME_SPREADSHEET_COUNT,    G_TYPE_INT},
186 			{GSF_META_NAME_TABLE_COUNT,          G_TYPE_INT},
187 			{GSF_META_NAME_CELL_COUNT,           G_TYPE_INT},
188 			{GSF_META_NAME_CHARACTER_COUNT,      G_TYPE_INT},
189 			{GSF_META_NAME_BYTE_COUNT,           G_TYPE_INT},
190 			{GSF_META_NAME_SECURITY,             G_TYPE_INT},
191 			{GSF_META_NAME_HIDDEN_SLIDE_COUNT,   G_TYPE_INT},
192 			{GSF_META_NAME_LINE_COUNT,           G_TYPE_INT},
193 			{GSF_META_NAME_SLIDE_COUNT,          G_TYPE_INT},
194 			{GSF_META_NAME_WORD_COUNT,           G_TYPE_INT},
195 			{GSF_META_NAME_MM_CLIP_COUNT,        G_TYPE_INT},
196 			{GSF_META_NAME_NOTE_COUNT,           G_TYPE_INT},
197 			{GSF_META_NAME_PARAGRAPH_COUNT,      G_TYPE_INT},
198 			{GSF_META_NAME_PAGE_COUNT,           G_TYPE_INT},
199 			{GSF_META_NAME_CODEPAGE,             G_TYPE_INT},
200 			{GSF_META_NAME_LOCALE_SYSTEM_DEFAULT,G_TYPE_INT},
201 			{GSF_META_NAME_OBJECT_COUNT,         G_TYPE_INT},
202 			{"xlsx:HyperlinksChanged",           G_TYPE_BOOLEAN},
203 			{GSF_META_NAME_LINKS_DIRTY,          G_TYPE_BOOLEAN},
204 			{"xlsx:SharedDoc",                   G_TYPE_BOOLEAN},
205 			{GSF_META_NAME_SCALE,                G_TYPE_BOOLEAN}
206 		};
207 		static char const *map_vector[] =
208 			{GSF_META_NAME_KEYWORDS,
209 			 GSF_META_NAME_DOCUMENT_PARTS,
210 			 GSF_META_NAME_HEADING_PAIRS};
211 		static char const *map_timestamps[] =
212 			{GSF_META_NAME_DATE_CREATED,
213 			 GSF_META_NAME_DATE_MODIFIED};
214 
215 		/*Missing:GSF_META_NAME_THUMBNAIL */
216 
217 		int i = G_N_ELEMENTS (map);
218 		dialog_doc_metadata_name_to_type = g_hash_table_new (g_str_hash, g_str_equal);
219 		while (i-- > 0)
220 			g_hash_table_insert (dialog_doc_metadata_name_to_type,
221 					     (gpointer)map[i].name,
222 					     GINT_TO_POINTER (map[i].type));
223 
224 		i = G_N_ELEMENTS (map_vector);
225 		while (i-- > 0)
226 			g_hash_table_insert (dialog_doc_metadata_name_to_type,
227 					     (gpointer)map_vector[i],
228 					     GINT_TO_POINTER (GSF_DOCPROP_VECTOR_TYPE));
229 
230 		i = G_N_ELEMENTS (map_timestamps);
231 		while (i-- > 0)
232 			g_hash_table_insert (dialog_doc_metadata_name_to_type,
233 					     (gpointer)map_timestamps[i],
234 					     GINT_TO_POINTER (GSF_TIMESTAMP_TYPE));
235 
236 	}
237 
238 	res = g_hash_table_lookup (dialog_doc_metadata_name_to_type, name);
239 
240 	if (res != NULL)
241 		return GPOINTER_TO_INT (res);
242 	else
243 		return def;
244 }
245 
246 static GType
dialog_doc_metadata_get_value_type(GValue * value)247 dialog_doc_metadata_get_value_type (GValue *value)
248 {
249 	GType val_type = G_VALUE_TYPE (value);
250 
251 	switch (val_type) {
252 	case G_TYPE_INT:
253 	case G_TYPE_UINT:
254 	case G_TYPE_FLOAT:
255 	case G_TYPE_DOUBLE:
256 	case G_TYPE_STRING:
257 	case G_TYPE_BOOLEAN:
258 		/* Just leave it as is */
259 		break;
260 
261 	default:
262 		/* Check if it is a GsfDocPropVector or GsfTimeStamp */
263 		if (VAL_IS_GSF_TIMESTAMP (value))
264 			val_type = GSF_TIMESTAMP_TYPE;
265 		else if (VAL_IS_GSF_DOCPROP_VECTOR (value))
266 			val_type = GSF_DOCPROP_VECTOR_TYPE;
267 		else {
268 			g_printerr ("GType %s (%i) not handled in metadata dialog.\n",
269 				    g_type_name (val_type), (int) val_type);
270 			val_type = G_TYPE_INVALID;
271 		}
272 
273 		break;
274 	}
275 	return val_type;
276 }
277 
278 
279 /******************************************************************************
280  * G_VALUE TRANSFORM FUNCTIONS
281  ******************************************************************************/
282 
283 /*
284  * G_TYPE_STRING TO OTHER
285  */
286 
287 static void
dialog_doc_metadata_transform_str_to_timestamp(const char * str,GValue * timestamp_value)288 dialog_doc_metadata_transform_str_to_timestamp (const char *str,
289 						GValue       *timestamp_value)
290 {
291 	time_t s;
292 	gnm_float serial;
293 	gint int_serial;
294 	GsfTimestamp *gt;
295 	GnmValue *conversion;
296 	GOFormat *fmt;
297 
298 	g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
299 
300 	fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
301 	conversion = format_match_number (str, fmt, NULL);
302 	go_format_unref (fmt);
303 	if (conversion) {
304 		serial = value_get_as_float (conversion);
305 		value_release (conversion);
306 
307 		/* Convert from Gnumeric time to unix time */
308 		int_serial = (int)serial;
309 		s = go_date_serial_to_timet (int_serial, NULL);
310 
311 		if (gnm_abs (serial - int_serial) >= 1.0 || s == (time_t)-1) {
312 			s = time (NULL);
313 		} else
314 			s += (gnm_fake_round (3600 * 24 * (serial - int_serial)));
315 
316 	} else
317 		s  = time (NULL);
318 
319 	gt = gsf_timestamp_new ();
320 	gsf_timestamp_set_time (gt, s);
321 	gsf_timestamp_to_value (gt, timestamp_value);
322 }
323 
324 static void
dialog_doc_metadata_transform_str_to_docprop_vect(const char * str,GValue * docprop_value)325 dialog_doc_metadata_transform_str_to_docprop_vect (const char *str,
326 						   GValue     *docprop_value)
327 {
328 	char const *s;
329 	GsfDocPropVector *gdpv;
330 	GValue *value;
331 
332 	g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
333 
334 	gdpv = gsf_docprop_vector_new ();
335 
336 	while (*str == ' ') {str++;}
337 
338 	while (*str == '"') {
339 		char *key;
340 		s = str += 1;
341 		while (*s != '"' && *s != '\0') {
342 			if (*(s++) == '\\') {
343 				if (*s == '\0')
344 					goto str_done;
345 				s++;
346 			}
347 		}
348 		if (*s == '\0')
349 			goto str_done;
350 		/* s == '"' */
351 		key = g_strndup (str, s - str);
352 		value = g_new0 (GValue, 1);
353 		g_value_take_string (g_value_init (value, G_TYPE_STRING),
354 				     g_strcompress (key));
355 		gsf_docprop_vector_append (gdpv, value);
356 		g_free (key);
357 		str = s + 1;
358 		while (*str == ' ') {str++;}
359 		if (str[0] != ',')
360 			goto str_done;
361 		str++;
362 		while (*str == ' ') {str++;}
363 	}
364  str_done:
365 	g_value_set_object (docprop_value, gdpv);
366 	g_object_unref (gdpv);
367 }
368 
369 static char *
time2str(time_t t)370 time2str (time_t t)
371 {
372 	char buffer[4000];
373 	gsize len;
374 	char const *format = "%c";
375 
376         if (t == -1)
377                 return NULL;
378 
379 	len = strftime (buffer, sizeof (buffer), format, localtime (&t));
380 	if (len == 0)
381 		return NULL;
382 
383 	return g_locale_to_utf8 (buffer, len, NULL, NULL, NULL);
384 }
385 
386 static char *
time2str_go(time_t t)387 time2str_go (time_t t)
388 {
389 	/* We need to create a format that is also parsable */
390 	char *str;
391 	GOFormat *fmt;
392 	gnm_float t_gnm;
393 
394 	if (t == -1)
395 		return NULL;
396 
397 	t_gnm = go_date_timet_to_serial_raw (t, NULL);
398 
399 	fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
400 	str = go_format_value (fmt, t_gnm);
401 	go_format_unref (fmt);
402 	return str;
403 }
404 
405 /*
406  * OTHER TO G_TYPE_STRING
407  */
408 static void
dialog_doc_metadata_transform_timestamp_to_str(const GValue * timestamp_value,GValue * string_value)409 dialog_doc_metadata_transform_timestamp_to_str (const GValue *timestamp_value,
410 						GValue       *string_value)
411 {
412 	GsfTimestamp const *timestamp = NULL;
413 
414 	g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
415 	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
416 
417 	timestamp = g_value_get_boxed (timestamp_value);
418 	if (timestamp != NULL)
419 		g_value_take_string (string_value,
420 				     time2str_go (timestamp->timet));
421 }
422 
423 static gchar*
gnm_docprop_vector_as_string(GsfDocPropVector * vector)424 gnm_docprop_vector_as_string (GsfDocPropVector *vector)
425 {
426 	GString         *rstring;
427 	guint		 i;
428 	guint		 num_values;
429 	GValueArray	*gva;
430 	GValue           vl = G_VALUE_INIT;
431 
432 	g_return_val_if_fail (vector != NULL, NULL);
433 
434 	g_value_set_object (g_value_init (&vl, GSF_DOCPROP_VECTOR_TYPE), vector);
435 	gva = gsf_value_get_docprop_varray (&vl);
436 
437 	g_return_val_if_fail (gva != NULL, NULL);
438 
439 	num_values = gva->n_values;
440 	rstring = g_string_sized_new (num_values * 8);
441 
442 	for (i = 0; i < num_values; i++) {
443 		char       *str;
444 		GValue	   *v;
445 
446 		v = g_value_array_get_nth (gva, i);
447 
448 		if (G_VALUE_TYPE(v) == G_TYPE_STRING)
449 			str = g_strescape (g_value_get_string (v), "");
450 		else {
451 			char *b_str = g_strdup_value_contents (v);
452 			str = g_strescape (b_str, "");
453 			g_free (b_str);
454 		}
455 		g_string_append_c (rstring, '"');
456 		g_string_append (rstring, str);
457 		g_string_append (rstring, "\", ");
458 		g_free (str);
459 	}
460 	if (rstring->len > 0)
461 		g_string_truncate (rstring, rstring->len - 2);
462 
463 	g_value_unset (&vl);
464 
465 	return g_string_free (rstring, FALSE);
466 }
467 
468 static void
dialog_doc_metadata_transform_docprop_vect_to_str(const GValue * docprop_value,GValue * string_value)469 dialog_doc_metadata_transform_docprop_vect_to_str (const GValue *docprop_value,
470 						   GValue       *string_value)
471 {
472 	GsfDocPropVector * docprop_vector = NULL;
473 
474 	g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
475 	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
476 
477 	docprop_vector = gsf_value_get_docprop_vector (docprop_value);
478 
479 	if (docprop_vector != NULL)
480 		g_value_set_string (string_value,
481 				    gnm_docprop_vector_as_string (docprop_vector));
482 }
483 
484 /******************************************************************************
485  * FUNCTIONS RELATED TO 'FILE' PAGE
486  ******************************************************************************/
487 
488 static void
cb_dialog_doc_metadata_change_permission(GtkCheckButton * bt,DialogDocMetaData * state)489 cb_dialog_doc_metadata_change_permission (GtkCheckButton    *bt,
490 					  DialogDocMetaData *state)
491 {
492 	g_return_if_fail (state->file_permissions != NULL);
493 
494 	/* Check which button was toggled */
495 	if (bt == state->owner_read)
496 		state->file_permissions->owner_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
497 	else if (bt == state->owner_write)
498 		state->file_permissions->owner_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
499 	else if (bt == state->group_read)
500 		state->file_permissions->group_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
501 	else if (bt == state->group_write)
502 		state->file_permissions->group_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
503 	else if (bt == state->others_read)
504 		state->file_permissions->others_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
505 	else if (bt == state->others_write)
506 		state->file_permissions->others_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
507 	else
508 		return;
509 
510 	state->permissions_changed = TRUE;
511 }
512 
513 static void
dialog_doc_metadata_set_up_permissions(DialogDocMetaData * state)514 dialog_doc_metadata_set_up_permissions (DialogDocMetaData *state)
515 {
516 	g_return_if_fail (state->metadata != NULL);
517 
518 	state->file_permissions = go_get_file_permissions (
519 		go_doc_get_uri (state->doc));
520 
521 	if (state->file_permissions != NULL) {
522 		/* Set Check buttons */
523 
524 		/* Owner */
525 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_read),
526 					      state->file_permissions->owner_read);
527 
528 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_write),
529 					      state->file_permissions->owner_write);
530 
531 		/* Group */
532 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_read),
533 						state->file_permissions->group_read);
534 
535 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_write),
536 					      state->file_permissions->group_write);
537 
538 		/* Others */
539 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_read),
540 					      state->file_permissions->others_read);
541 
542 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_write),
543 					      state->file_permissions->others_write);
544 	}
545 
546 	/* At this moment we don't let user change file permissions */
547 	gtk_widget_set_sensitive (GTK_WIDGET (state->owner_read), FALSE);
548 	gtk_widget_set_sensitive (GTK_WIDGET (state->owner_write), FALSE);
549 	gtk_widget_set_sensitive (GTK_WIDGET (state->group_read), FALSE);
550 	gtk_widget_set_sensitive (GTK_WIDGET (state->group_write), FALSE);
551 	gtk_widget_set_sensitive (GTK_WIDGET (state->others_read), FALSE);
552 	gtk_widget_set_sensitive (GTK_WIDGET (state->others_write), FALSE);
553 }
554 
555  /* @auto_fill : if TRUE and the text is NULL, try to set the label text with an automatic value. */
556 static void
dialog_doc_metadata_set_label(DialogDocMetaData * state,GtkLabel * label,char const * text,gboolean auto_fill)557 dialog_doc_metadata_set_label (DialogDocMetaData *state,
558 			       GtkLabel          *label,
559 			       char        const *text,
560 			       gboolean          auto_fill)
561 {
562 	Workbook *wb = state->wb;
563 	gchar    *str_value = NULL;
564 
565 	g_return_if_fail (label != NULL);
566 
567 	if (text != NULL)
568 		str_value = g_strdup (text);
569 
570 	if (str_value == NULL && auto_fill == TRUE) {
571 		/* File Name */
572 		if (label == state->file_name) {
573 			str_value = go_basename_from_uri (go_doc_get_uri (state->doc));
574 		}
575 
576 		/* File Location */
577 		else if (label == state->location) {
578 			str_value = go_dirname_from_uri (go_doc_get_uri (state->doc), TRUE);
579 		}
580 
581 		/* Date Created */
582 		else if (label == state->created) {
583 			/* Nothing to do ATM */
584 		}
585 
586 		/* Date Modified */
587 		else if (label == state->modified) {
588 			str_value = time2str (go_file_get_date_modified (go_doc_get_uri (state->doc)));
589 		}
590 
591 		/* Date Accessed */
592 		else if (label == state->accessed) {
593 			str_value = time2str (go_file_get_date_accessed (go_doc_get_uri (state->doc)));
594 		}
595 
596 		/* Owner */
597 		else if (label == state->owner) {
598 			str_value = go_file_get_owner_name (go_doc_get_uri (state->doc));
599 		}
600 
601 		/* Group */
602 		else if (label == state->group) {
603 			str_value = go_file_get_group_name (go_doc_get_uri (state->doc));
604 		}
605 
606 		/* Number of Sheets */
607 		else if (label == state->sheets) {
608 			str_value = g_strdup_printf ("%d",  workbook_sheet_count (wb));
609 		}
610 
611 		/* Number of cells */
612 		else if (label == state->cells) {
613 			/* Nothing to do ATM */
614 		}
615 
616 		/* Number of pages */
617 		else if (label == state->pages) {
618 			/* Nothing to do ATM */
619 		}
620 	}
621 
622 	if (str_value != NULL) {
623 		gtk_label_set_text (label, str_value);
624 		g_free (str_value);
625 	} else
626 		gtk_label_set_text (label, _("Unknown"));
627 }
628 
629 static void
dialog_doc_metadata_init_file_page(DialogDocMetaData * state)630 dialog_doc_metadata_init_file_page (DialogDocMetaData *state)
631 {
632 	g_return_if_fail (state->metadata != NULL);
633 
634 	/* Set up the labels */
635 	dialog_doc_metadata_set_label (state, state->file_name, NULL, TRUE);
636 	dialog_doc_metadata_set_label (state, state->location, NULL, TRUE);
637 	dialog_doc_metadata_set_label (state, state->created, NULL, TRUE);
638 	dialog_doc_metadata_set_label (state, state->modified, NULL, TRUE);
639 	dialog_doc_metadata_set_label (state, state->accessed, NULL, TRUE);
640 	dialog_doc_metadata_set_label (state, state->owner, NULL, TRUE);
641 	dialog_doc_metadata_set_label (state, state->group, NULL, TRUE);
642 
643 	/* Set up check buttons */
644 	state->permissions_changed = FALSE;
645 	dialog_doc_metadata_set_up_permissions (state);
646 
647 	/* Signals */
648 
649 	/* Owner */
650 	g_signal_connect (G_OBJECT (state->owner_read),
651 			  "toggled",
652 			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
653 			  state);
654 
655 	g_signal_connect (G_OBJECT (state->owner_write),
656 			  "toggled",
657 			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
658 			  state);
659 
660 	/* Group */
661 	g_signal_connect (G_OBJECT (state->group_read),
662 			  "toggled",
663 			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
664 			  state);
665 
666 	g_signal_connect (G_OBJECT (state->group_write),
667 			  "toggled",
668 			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
669 			  state);
670 
671 	/* Others */
672 	g_signal_connect (G_OBJECT (state->others_read),
673 			  "toggled",
674 			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
675 			  state);
676 
677 	g_signal_connect (G_OBJECT (state->others_write),
678 			  "toggled",
679 			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
680 			  state);
681 
682 }
683 
684 /******************************************************************************
685  * FUNCTIONS RELATED TO 'DESCRIPTION' PAGE
686  ******************************************************************************/
687 
688 /* @activate_property : if TRUE, sets the tree view row which was added active. */
689 static void
dialog_doc_metadata_add_prop(DialogDocMetaData * state,const gchar * name,const gchar * value,const gchar * lnk,GType val_type)690 dialog_doc_metadata_add_prop (DialogDocMetaData *state,
691 			      const gchar       *name,
692 			      const gchar       *value,
693 			      const gchar       *lnk,
694 			      GType              val_type)
695 {
696 	gboolean editable = (val_type != G_TYPE_INVALID)
697 		&& (val_type != GSF_DOCPROP_VECTOR_TYPE);
698 	if (value == NULL)
699 		value = "";
700 
701 	if (lnk == NULL)
702 		lnk = "";
703 
704 	/* Append new values in tree view */
705 	gtk_tree_store_insert_with_values (state->properties_store, NULL, NULL, G_MAXINT,
706 					   0, name,
707 					   1, value,
708 					   2, lnk,
709 					   3, editable,
710 					   4, val_type,
711 					   -1);
712 }
713 
714 static GType
dialog_doc_metadata_get_gsf_prop_val_type(DialogDocMetaData * state,const gchar * name)715 dialog_doc_metadata_get_gsf_prop_val_type (DialogDocMetaData *state,
716 					   const gchar       *name)
717 {
718 	GsfDocProp *prop = NULL;
719 	GValue     *value = NULL;
720 
721 	g_return_val_if_fail (state->metadata != NULL, G_TYPE_INVALID);
722 
723 	/* First we try the clean way */
724 	prop = gsf_doc_meta_data_lookup (state->metadata, name);
725 
726 	if (prop != NULL)
727 		value = (GValue *) gsf_doc_prop_get_val (prop);
728 
729 	if (value != NULL)
730 		return dialog_doc_metadata_get_value_type (value);
731 	else
732 		return dialog_doc_metadata_get_value_type_from_name (name, G_TYPE_STRING);
733 }
734 
735 static void
dialog_doc_metadata_set_gsf_prop_val(DialogDocMetaData * state,GValue * prop_value,const gchar * str_val)736 dialog_doc_metadata_set_gsf_prop_val (DialogDocMetaData *state,
737 				      GValue            *prop_value,
738 				      const gchar       *str_val)
739 {
740 	GType t = G_VALUE_TYPE (prop_value);
741 
742 	/* The preinstalled transform functions do not handle simple transformations */
743 	/* such as from string to double, so we do that ourselves */
744 	switch (t) {
745 	case G_TYPE_STRING:
746 		g_value_set_string (prop_value, str_val);
747 		return;
748 	case G_TYPE_DOUBLE:
749 	case G_TYPE_FLOAT: {
750 		GnmValue *val = format_match_number (str_val, NULL, workbook_date_conv (state->wb));
751 		if (val != NULL) {
752 			gnm_float fl = value_get_as_float (val);
753 			value_release (val);
754 			if (t == G_TYPE_DOUBLE)
755 				g_value_set_double (prop_value, fl);
756 			else
757 				g_value_set_float (prop_value, fl);
758 		}
759 		return;
760 	}
761 	case G_TYPE_INT:
762 		g_value_set_int (prop_value, strtol (str_val, NULL, 10));
763 		return;
764 	case G_TYPE_UINT:
765 		g_value_set_uint (prop_value, strtoul (str_val, NULL, 10));
766 		return;
767 	case G_TYPE_BOOLEAN: {
768 		GnmValue *val = format_match_number (str_val, NULL, workbook_date_conv (state->wb));
769 		gboolean b = FALSE;
770 		if (val != NULL) {
771 			gboolean err;
772 			b = value_get_as_bool (val, &err);
773 			value_release (val);
774 		}
775 		g_value_set_boolean (prop_value, b);
776 		return;
777 	}
778 	}
779 
780 	if (t == GSF_TIMESTAMP_TYPE) {
781 		dialog_doc_metadata_transform_str_to_timestamp (str_val, prop_value);
782 	} else if (t == GSF_DOCPROP_VECTOR_TYPE) {
783 		dialog_doc_metadata_transform_str_to_docprop_vect (str_val, prop_value);
784 	} else {
785 		g_printerr (_("Transform function of G_TYPE_STRING to %s is required!\n"),
786 			    g_type_name (t));
787 	}
788 }
789 
790 /**
791  * dialog_doc_metadata_set_gsf_prop
792  * @state: dialog main struct
793  * @name: property name
794  * @value: property value
795  * @lnk: property linked to
796  *
797  * Sets a new value to the property in the GsfDocMetaData struct
798  *
799  **/
800 static GType
dialog_doc_metadata_set_gsf_prop(DialogDocMetaData * state,const gchar * name,const gchar * value,const gchar * lnk,GType type)801 dialog_doc_metadata_set_gsf_prop (DialogDocMetaData *state,
802 				  const gchar       *name,
803 				  const gchar       *value,
804 				  const gchar       *lnk,
805 				  GType              type)
806 {
807 	GsfDocProp *existing_prop = NULL;
808 	GsfDocProp *doc_prop;
809 	GValue     *existing_value = NULL;
810 	char const *existing_link  = NULL;
811 	GType       val_type;
812 
813 	existing_prop = gsf_doc_meta_data_lookup (state->metadata, name);
814 	if (existing_prop != NULL) {
815 		existing_value = (GValue *) gsf_doc_prop_get_val (existing_prop);
816 		existing_link  = gsf_doc_prop_get_link (existing_prop);
817 	}
818 
819 	if (lnk != NULL && *lnk == 0)
820 		lnk = NULL;
821 	if (value != NULL && *value == 0)
822 		value = NULL;
823 	if ((value == NULL) && (lnk == NULL)) {
824 		if ((existing_prop == NULL) ||
825 		    ((existing_value == NULL) && (existing_link == NULL)))
826 			return G_TYPE_INVALID;
827 		else {
828 			cmd_change_meta_data (GNM_WBC (state->wbcg), NULL,
829 					      g_slist_prepend (NULL, g_strdup (name)));
830 			return G_TYPE_INVALID;
831 		}
832 	}
833 
834 	if (existing_prop != NULL) {
835 		gboolean    link_changed;
836 		gboolean    value_changed = TRUE;
837 
838 		if (existing_link!= NULL && *existing_link == 0)
839 			existing_link = NULL;
840 		if (lnk == existing_link)
841 			link_changed = FALSE;
842 		else if (lnk == NULL || existing_link == NULL)
843 			link_changed = TRUE;
844 		else
845 			link_changed = (0 != strcmp (lnk, existing_link));
846 
847 		if (existing_value == NULL)
848 			value_changed = (value != NULL);
849 		else if (G_VALUE_HOLDS_STRING (existing_value) && (type == 0 || type == G_TYPE_STRING)) {
850 			char const * existing_val_str = g_value_get_string (existing_value);
851 			if (existing_val_str != NULL && *existing_val_str == 0)
852 				existing_val_str = NULL;
853 			if (value == existing_val_str)
854 				value_changed = FALSE;
855 			else if (value == NULL || existing_val_str == NULL)
856 				value_changed = TRUE;
857 			else
858 				value_changed = (0 != strcmp (value, existing_val_str));
859 			if (!link_changed && !value_changed)
860 				return G_TYPE_STRING;
861 		}
862 	}
863 
864 
865 	/* Create a new GsfDocProp */
866 	doc_prop = gsf_doc_prop_new (g_strdup (name));
867 
868 	if (type == 0)
869 		val_type = dialog_doc_metadata_get_gsf_prop_val_type (state, name);
870 	else
871 		val_type = type;
872 
873 	if (val_type != G_TYPE_INVALID) {
874 		GValue     *doc_prop_value;
875 
876 		/* Create a new Value */
877 		doc_prop_value = g_new0 (GValue, 1);
878 
879 		g_value_init (doc_prop_value, val_type);
880 		dialog_doc_metadata_set_gsf_prop_val (state, doc_prop_value, value);
881 		gsf_doc_prop_set_val (doc_prop, doc_prop_value);
882 	}
883 
884 	if (lnk != NULL)
885 		gsf_doc_prop_set_link (doc_prop, g_strdup (lnk));
886 
887 	cmd_change_meta_data (GNM_WBC (state->wbcg),
888 			      g_slist_prepend (NULL, doc_prop), NULL);
889 
890 	return val_type;
891 }
892 
893 /**
894  * dialog_doc_metadata_set_prop
895  * @state: dialog main struct
896  * @prop_name: property name
897  * @prop_value: property value
898  *
899  * Tries to update the property value in all the dialog and in the GsfDocMetaData
900  * struct. If the property was not found, creates a new one.
901  *
902  **/
903 static void
dialog_doc_metadata_set_prop(DialogDocMetaData * state,const gchar * prop_name,const gchar * prop_value,const gchar * link_value,GType type)904 dialog_doc_metadata_set_prop (DialogDocMetaData *state,
905 			      const gchar       *prop_name,
906 			      const gchar       *prop_value,
907 			      const gchar       *link_value,
908 			      GType type)
909 {
910 	GtkTreeIter tree_iter;
911 	GValue      *value;
912 	gboolean    ret;
913 	gboolean    found;
914 	GType       val_type;
915 	GsfDocProp *updated_prop;
916 	gchar      *new_prop_value = NULL;
917 
918 	g_return_if_fail (state->metadata != NULL);
919 
920 	val_type = dialog_doc_metadata_set_gsf_prop (state, prop_name, prop_value,
921 						     link_value, type);
922 
923 	/* Due to changes in type, prop_value may have changed */
924 	updated_prop = gsf_doc_meta_data_lookup (state->metadata, prop_name);
925 	if (updated_prop != NULL) {
926 		GValue *new_value = (GValue *) gsf_doc_prop_get_val (updated_prop);
927 		if (new_value != NULL)
928 			new_prop_value = dialog_doc_metadata_get_prop_val (state, prop_name, new_value);
929 		if (new_prop_value == NULL)
930 			new_prop_value = g_strdup ("");
931 	}
932 
933 	found = FALSE;
934 
935 	/* Search tree view for property */
936 	value = g_new0 (GValue, 1);
937 
938 	ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->properties_store),
939 					     &tree_iter);
940 
941 	while (ret == TRUE) {
942 
943 		gtk_tree_model_get_value (GTK_TREE_MODEL (state->properties_store),
944 					  &tree_iter,
945 					  0,
946 					  value);
947 
948 		if (strcmp (prop_name, g_value_get_string (value)) == 0) {
949 			if (updated_prop != NULL) {
950 				/* Set new value */
951 				gtk_tree_store_set (state->properties_store,
952 						    &tree_iter,
953 						    1, new_prop_value,
954 					    -1);
955 
956 				if (link_value != NULL)
957 					gtk_tree_store_set (state->properties_store,
958 							    &tree_iter,
959 							    2, link_value,
960 							    -1);
961 			} else
962 				/* Clear value */
963 				gtk_tree_store_remove (state->properties_store,
964 						       &tree_iter);
965 
966 			g_value_unset (value);
967 
968 			found = TRUE;
969 			break;
970 		}
971 
972 		g_value_unset (value);
973 		ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (state->properties_store),
974 						&tree_iter);
975 	}
976 
977 	if (val_type != G_TYPE_INVALID && found == FALSE)
978 		dialog_doc_metadata_add_prop (state, prop_name, new_prop_value, "", val_type);
979 
980 	/* Free all data */
981 	g_free (value);
982 	g_free (new_prop_value );
983 }
984 
985 /*
986  * CALLBACKS for 'Description' page entries
987  */
988 static gboolean
cb_dialog_doc_metadata_title_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)989 cb_dialog_doc_metadata_title_changed (GtkEntry          *entry,
990 				      G_GNUC_UNUSED GdkEventFocus *event,
991 				      DialogDocMetaData *state)
992 {
993 	dialog_doc_metadata_set_prop (state,
994 				      GSF_META_NAME_TITLE,
995 				      gtk_entry_get_text (entry),
996 				      NULL, G_TYPE_STRING);
997 	return FALSE;
998 }
999 
1000 static gboolean
cb_dialog_doc_metadata_subject_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1001 cb_dialog_doc_metadata_subject_changed (GtkEntry          *entry,
1002 					G_GNUC_UNUSED GdkEventFocus *event,
1003 					DialogDocMetaData *state)
1004 {
1005 	dialog_doc_metadata_set_prop (state,
1006 				      GSF_META_NAME_SUBJECT,
1007 				      gtk_entry_get_text (entry),
1008 				      NULL, G_TYPE_STRING);
1009 	return FALSE;
1010 }
1011 
1012 static gboolean
cb_dialog_doc_metadata_author_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1013 cb_dialog_doc_metadata_author_changed (GtkEntry          *entry,
1014 				       G_GNUC_UNUSED GdkEventFocus *event,
1015 				       DialogDocMetaData *state)
1016 {
1017 	dialog_doc_metadata_set_prop (state,
1018 				      GSF_META_NAME_INITIAL_CREATOR,
1019 				      gtk_entry_get_text (entry),
1020 				      NULL,  G_TYPE_STRING);
1021 	return FALSE;
1022 }
1023 
1024 static gboolean
cb_dialog_doc_metadata_manager_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1025 cb_dialog_doc_metadata_manager_changed (GtkEntry          *entry,
1026 					G_GNUC_UNUSED GdkEventFocus *event,
1027 					DialogDocMetaData *state)
1028 {
1029 	dialog_doc_metadata_set_prop (state,
1030 				      GSF_META_NAME_MANAGER,
1031 				      gtk_entry_get_text (entry),
1032 				      NULL,  G_TYPE_STRING);
1033 	return FALSE;
1034 }
1035 
1036 static gboolean
cb_dialog_doc_metadata_company_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1037 cb_dialog_doc_metadata_company_changed (GtkEntry          *entry,
1038 					G_GNUC_UNUSED GdkEventFocus *event,
1039 					DialogDocMetaData *state)
1040 {
1041 	dialog_doc_metadata_set_prop (state,
1042 				      GSF_META_NAME_COMPANY,
1043 				      gtk_entry_get_text (entry),
1044 				      NULL,  G_TYPE_STRING);
1045 	return FALSE;
1046 }
1047 
1048 static gboolean
cb_dialog_doc_metadata_category_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1049 cb_dialog_doc_metadata_category_changed (GtkEntry          *entry,
1050 					 G_GNUC_UNUSED GdkEventFocus *event,
1051 					 DialogDocMetaData *state)
1052 {
1053 	dialog_doc_metadata_set_prop (state,
1054 				      GSF_META_NAME_CATEGORY,
1055 				      gtk_entry_get_text (entry),
1056 				      NULL, G_TYPE_STRING );
1057 	return FALSE;
1058 }
1059 
1060 static gboolean
cb_dialog_doc_metadata_comments_changed(GtkTextView * view,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1061 cb_dialog_doc_metadata_comments_changed (GtkTextView     *view,
1062 					 G_GNUC_UNUSED GdkEventFocus *event,
1063 					 DialogDocMetaData *state)
1064 {
1065 	GtkTextIter start_iter;
1066 	GtkTextIter end_iter;
1067 	gchar *text;
1068 	GtkTextBuffer     *buffer = gtk_text_view_get_buffer (view);
1069 
1070 	gtk_text_buffer_get_start_iter (buffer, &start_iter);
1071 	gtk_text_buffer_get_end_iter   (buffer, &end_iter);
1072 
1073 	text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, TRUE);
1074 
1075 	dialog_doc_metadata_set_prop (state,
1076 				      GSF_META_NAME_DESCRIPTION,
1077 				      text,
1078 				      NULL,  G_TYPE_STRING);
1079 	return FALSE;
1080 }
1081 
1082 /**
1083  * dialog_doc_metadata_init_description_page
1084  * @state: dialog main struct
1085  *
1086  * Initializes the widgets and signals for the 'Description' page.
1087  *
1088  **/
1089 static void
dialog_doc_metadata_init_description_page(DialogDocMetaData * state)1090 dialog_doc_metadata_init_description_page (DialogDocMetaData *state)
1091 {
1092 	g_return_if_fail (state->metadata != NULL);
1093 
1094 	/* At this point, the entry values were already filled */
1095 
1096 	/* Set up the signals */
1097 	g_signal_connect (G_OBJECT (state->title),
1098 			  "focus-out-event",
1099 			  G_CALLBACK (cb_dialog_doc_metadata_title_changed),
1100 			  state);
1101 
1102 	g_signal_connect (G_OBJECT (state->subject),
1103 			  "focus-out-event",
1104 			  G_CALLBACK (cb_dialog_doc_metadata_subject_changed),
1105 			  state);
1106 
1107 	g_signal_connect (G_OBJECT (state->author),
1108 			  "focus-out-event",
1109 			  G_CALLBACK (cb_dialog_doc_metadata_author_changed),
1110 			  state);
1111 
1112 	g_signal_connect (G_OBJECT (state->manager),
1113 			  "focus-out-event",
1114 			  G_CALLBACK (cb_dialog_doc_metadata_manager_changed),
1115 			  state);
1116 
1117 	g_signal_connect (G_OBJECT (state->company),
1118 			  "focus-out-event",
1119 			  G_CALLBACK (cb_dialog_doc_metadata_company_changed),
1120 			  state);
1121 
1122 	g_signal_connect (G_OBJECT (state->category),
1123 			  "focus-out-event",
1124 			  G_CALLBACK (cb_dialog_doc_metadata_category_changed),
1125 			  state);
1126 
1127 	g_signal_connect (G_OBJECT (state->comments),
1128 			  "focus-out-event",
1129 			  G_CALLBACK (cb_dialog_doc_metadata_comments_changed),
1130 			  state);
1131 }
1132 
1133 /******************************************************************************
1134  * FUNCTIONS RELATED TO 'KEYWORDS' PAGE
1135  ******************************************************************************/
1136 
1137 static void
dialog_doc_metadata_update_keywords_changed(DialogDocMetaData * state)1138 dialog_doc_metadata_update_keywords_changed (DialogDocMetaData *state)
1139 {
1140 	GValue val = G_VALUE_INIT;
1141 	GtkTreeIter iter;
1142 	GsfDocPropVector *vector = gsf_docprop_vector_new ();
1143 
1144 	g_value_init (&val, GSF_DOCPROP_VECTOR_TYPE);
1145 
1146 	if (gtk_tree_model_get_iter_first
1147 	    (GTK_TREE_MODEL (state->key_store), &iter)) {
1148 		do {
1149 			GValue *value = g_new0 (GValue, 1);
1150 			gtk_tree_model_get_value
1151 				(GTK_TREE_MODEL (state->key_store), &iter,
1152 				 0, value);
1153 			gsf_docprop_vector_append (vector, value);
1154 			g_value_unset (value);
1155 			g_free (value);
1156 		} while (gtk_tree_model_iter_next
1157 			 (GTK_TREE_MODEL (state->key_store), &iter));
1158 	}
1159 	g_value_set_object (&val, vector);
1160 	g_object_unref (vector);
1161 
1162 	dialog_doc_metadata_set_prop
1163 		(state, GSF_META_NAME_KEYWORDS,
1164 		 dialog_doc_metadata_get_prop_val (state, GSF_META_NAME_KEYWORDS, &val),
1165 		 NULL, GSF_DOCPROP_VECTOR_TYPE);
1166 
1167 	g_value_unset (&val);
1168 }
1169 
1170 static void
cb_dialog_doc_metadata_keywords_sel_changed(GtkTreeSelection * treeselection,DialogDocMetaData * state)1171 cb_dialog_doc_metadata_keywords_sel_changed (GtkTreeSelection *treeselection,
1172 					     DialogDocMetaData *state)
1173 {
1174 	gtk_widget_set_sensitive
1175 		(GTK_WIDGET (state->key_remove_button),
1176 		 gtk_tree_selection_get_selected (treeselection, NULL, NULL));
1177 }
1178 
1179 static void
dialog_doc_metadata_update_keyword_list(DialogDocMetaData * state,GsfDocProp * prop)1180 dialog_doc_metadata_update_keyword_list (DialogDocMetaData *state, GsfDocProp *prop)
1181 {
1182 	guint i;
1183 	GtkTreeSelection *sel;
1184 
1185 	gtk_list_store_clear (state->key_store);
1186 
1187 	if (prop != NULL) {
1188 		GValueArray *array;
1189 		array = gsf_value_get_docprop_varray (gsf_doc_prop_get_val (prop));
1190 		if (array != NULL) {
1191 			for (i = 0; i < array->n_values; i++) {
1192 				GValue *val = g_value_array_get_nth (array, i);
1193 				gtk_list_store_insert_with_values
1194 					(state->key_store, NULL, G_MAXINT,
1195 					 0, g_value_get_string (val), -1);
1196 			}
1197 		}
1198 	}
1199 
1200 	sel = gtk_tree_view_get_selection (state->key_tree_view);
1201 	cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
1202 }
1203 
1204 static void
cb_dialog_doc_metadata_keywords_add_clicked(G_GNUC_UNUSED GtkWidget * w,DialogDocMetaData * state)1205 cb_dialog_doc_metadata_keywords_add_clicked (G_GNUC_UNUSED GtkWidget *w,
1206 					     DialogDocMetaData       *state)
1207 {
1208 	gtk_list_store_insert_with_values (state->key_store, NULL, G_MAXINT,
1209 					   0, "<?>", -1);
1210 	dialog_doc_metadata_update_keywords_changed (state);
1211 }
1212 
1213 static void
cb_dialog_doc_metadata_keywords_remove_clicked(G_GNUC_UNUSED GtkWidget * w,DialogDocMetaData * state)1214 cb_dialog_doc_metadata_keywords_remove_clicked (G_GNUC_UNUSED GtkWidget *w,
1215 						DialogDocMetaData       *state)
1216 {
1217 	GtkTreeIter iter;
1218 	GtkTreeSelection *sel = gtk_tree_view_get_selection (state->key_tree_view);
1219 
1220 	if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
1221 		gtk_list_store_remove (state->key_store, &iter);
1222 		dialog_doc_metadata_update_keywords_changed (state);
1223 	}
1224 }
1225 
1226 static void
cb_dialog_doc_metadata_keyword_edited(G_GNUC_UNUSED GtkCellRendererText * renderer,gchar * path,gchar * new_text,DialogDocMetaData * state)1227 cb_dialog_doc_metadata_keyword_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
1228 				       gchar                             *path,
1229 				       gchar                             *new_text,
1230 				       DialogDocMetaData                 *state)
1231 {
1232 	GtkTreeIter iter;
1233 	if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (state->key_store), &iter, path)) {
1234 		gtk_list_store_set (state->key_store, &iter, 0, new_text, -1);
1235 		dialog_doc_metadata_update_keywords_changed (state);
1236 	}
1237 }
1238 
1239 
1240 /**
1241  * dialog_doc_metadata_init_keywords_page
1242  * @state: dialog main struct
1243  *
1244  * Initializes the widgets and signals for the 'Description' page.
1245  *
1246  **/
1247 static void
dialog_doc_metadata_init_keywords_page(DialogDocMetaData * state)1248 dialog_doc_metadata_init_keywords_page (DialogDocMetaData *state)
1249 {
1250 	GtkTreeViewColumn *column;
1251 	GtkCellRenderer   *renderer;
1252 	GtkTreeSelection *sel;
1253 
1254 	g_return_if_fail (state->metadata != NULL);
1255 
1256 	renderer = gtk_cell_renderer_text_new ();
1257 	g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
1258 	column = gtk_tree_view_column_new_with_attributes (_("Keywords"),
1259 							   renderer,
1260 							   "text", 0,
1261 							   NULL);
1262 	gtk_tree_view_insert_column (state->key_tree_view, column, -1);
1263 
1264 	gtk_widget_set_sensitive (GTK_WIDGET (state->key_add_button), TRUE);
1265 	gtk_widget_set_sensitive (GTK_WIDGET (state->key_remove_button), FALSE);
1266 
1267 	sel = gtk_tree_view_get_selection (state->key_tree_view);
1268 	g_signal_connect (G_OBJECT (sel),
1269 			  "changed",
1270 			  G_CALLBACK (cb_dialog_doc_metadata_keywords_sel_changed),
1271 			  state);
1272 
1273 	g_signal_connect (G_OBJECT (state->key_add_button),
1274 			  "clicked",
1275 			  G_CALLBACK (cb_dialog_doc_metadata_keywords_add_clicked),
1276 			  state);
1277 	g_signal_connect (G_OBJECT (state->key_remove_button),
1278 			  "clicked",
1279 			  G_CALLBACK (cb_dialog_doc_metadata_keywords_remove_clicked),
1280 			  state);
1281 	g_signal_connect (G_OBJECT (renderer),
1282 			  "edited",
1283 			  G_CALLBACK (cb_dialog_doc_metadata_keyword_edited),
1284 			  state);
1285 
1286 	cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
1287 }
1288 
1289 /******************************************************************************
1290  * FUNCTIONS RELATED TO 'PROPERTIES' PAGE
1291  ******************************************************************************/
1292 
1293 static void
cb_dialog_doc_metadata_value_edited(G_GNUC_UNUSED GtkCellRendererText * renderer,gchar * path,gchar * new_text,DialogDocMetaData * state)1294 cb_dialog_doc_metadata_value_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
1295 				     gchar                             *path,
1296 				     gchar                             *new_text,
1297 				     DialogDocMetaData                 *state)
1298 {
1299 	GtkTreeIter iter;
1300 	if (gtk_tree_model_get_iter_from_string
1301 	    (GTK_TREE_MODEL (state->properties_store), &iter, path)) {
1302 		gchar       *prop_name;
1303 		gchar       *link_value;
1304 		GType        type;
1305 
1306 		gtk_tree_model_get (GTK_TREE_MODEL (state->properties_store),
1307 				    &iter,
1308 				    0, &prop_name,
1309 				    2, &link_value,
1310 				    4, &type,
1311 				    -1);
1312 		dialog_doc_metadata_set_prop (state, prop_name, new_text, link_value, type);
1313 		g_free (prop_name);
1314 		g_free (link_value);
1315 	}
1316 }
1317 
1318 /**
1319  * cb_dialog_doc_metadata_add_clicked
1320  * @w: widget
1321  * @state: dialog main struct
1322  *
1323  * Adds a new "empty" property to the tree view.
1324  *
1325  **/
1326 static void
cb_dialog_doc_metadata_add_clicked(G_GNUC_UNUSED GtkWidget * w,DialogDocMetaData * state)1327 cb_dialog_doc_metadata_add_clicked (G_GNUC_UNUSED GtkWidget *w,
1328 				    DialogDocMetaData       *state)
1329 {
1330 	const gchar *name = gtk_entry_get_text (state->ppt_name);
1331 	const gchar *value = gtk_entry_get_text (state->ppt_value);
1332 	gchar *name_trimmed = trim_string (name);
1333 	GType t = G_TYPE_INVALID; /* we need to initialize that one since
1334 								gtk_tree_model_get() will only set the low bytes */
1335 	GtkTreeIter filter_iter;
1336 
1337 	if (gtk_combo_box_get_active_iter (state->ppt_type, &filter_iter)) {
1338 		GtkTreeIter child_iter;
1339 		gtk_tree_model_filter_convert_iter_to_child_iter
1340 			(state->type_store_filter, &child_iter, &filter_iter);
1341 		gtk_tree_model_get (GTK_TREE_MODEL (state->type_store), &child_iter,
1342 				    1, &t, -1);
1343 	} else
1344 		t = dialog_doc_metadata_get_value_type_from_name (name_trimmed, G_TYPE_STRING);
1345 	dialog_doc_metadata_set_prop (state, name_trimmed, value, NULL, t);
1346 
1347 	g_free (name_trimmed);
1348 
1349 	cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1350 	gtk_label_set_text (state->warning, "");
1351 }
1352 
1353 /**
1354  * dialog_doc_metadata_update_prop
1355  * @state: dialog main struct
1356  * @prop_name: property name
1357  * @prop_value: property value
1358  *
1359  * Updates a label or a entry text with the new value.
1360  *
1361  **/
1362 
1363 static void
dialog_doc_metadata_update_prop(DialogDocMetaData * state,const gchar * prop_name,const gchar * prop_value,GsfDocProp * prop)1364 dialog_doc_metadata_update_prop (DialogDocMetaData *state,
1365 				 const gchar       *prop_name,
1366 				 const gchar       *prop_value,
1367 				 GsfDocProp	   *prop)
1368 {
1369 	/* Labels */
1370 	if (strcmp (prop_name, GSF_META_NAME_DATE_CREATED) == 0) {
1371 		dialog_doc_metadata_set_label (state,
1372 					       state->created,
1373 					       prop_value,
1374 					       TRUE);
1375 	}
1376 	else if (strcmp (prop_name, GSF_META_NAME_DATE_MODIFIED) == 0) {
1377 		dialog_doc_metadata_set_label (state,
1378 					       state->modified,
1379 					       prop_value,
1380 					       TRUE);
1381 	}
1382 	else if (strcmp (prop_name, GSF_META_NAME_SPREADSHEET_COUNT) == 0) {
1383 		dialog_doc_metadata_set_label (state,
1384 					       state->sheets,
1385 					       prop_value,
1386 					       TRUE);
1387 	}
1388 	else if (strcmp (prop_name, GSF_META_NAME_CELL_COUNT) == 0) {
1389 		dialog_doc_metadata_set_label (state,
1390 					       state->cells,
1391 					       prop_value,
1392 					       TRUE);
1393 	}
1394 	else if (strcmp (prop_name, GSF_META_NAME_PAGE_COUNT) == 0) {
1395 		dialog_doc_metadata_set_label (state,
1396 					       state->pages,
1397 					       prop_value,
1398 					       TRUE);
1399 	}
1400 
1401 	/* Entries */
1402 	if (prop_value == NULL)
1403 		prop_value = "";
1404 
1405 	if (strcmp (prop_name, GSF_META_NAME_TITLE) == 0) {
1406 		gtk_entry_set_text (state->title, prop_value);
1407 	}
1408 
1409 	else if (strcmp (prop_name, GSF_META_NAME_SUBJECT) == 0) {
1410 		gtk_entry_set_text (state->subject, prop_value);
1411 	}
1412 
1413 	else if (strcmp (prop_name, GSF_META_NAME_INITIAL_CREATOR) == 0) {
1414 		gtk_entry_set_text (state->author, prop_value);
1415 	}
1416 
1417 	else if (strcmp (prop_name, GSF_META_NAME_MANAGER) == 0) {
1418 		gtk_entry_set_text (state->manager, prop_value);
1419 	}
1420 
1421 	else if (strcmp (prop_name, GSF_META_NAME_COMPANY) == 0) {
1422 		gtk_entry_set_text (state->company, prop_value);
1423 	}
1424 
1425 	else if (strcmp (prop_name, GSF_META_NAME_CATEGORY) == 0) {
1426 		gtk_entry_set_text (state->category, prop_value);
1427 	}
1428 
1429 	else if (strcmp (prop_name, GSF_META_NAME_KEYWORDS) == 0) {
1430 		dialog_doc_metadata_update_keyword_list (state, prop);
1431 	}
1432 
1433 	else if (strcmp (prop_name, GSF_META_NAME_DESCRIPTION) == 0) {
1434 		gtk_text_buffer_set_text (gtk_text_view_get_buffer (state->comments),
1435 					  prop_value,
1436 					  -1);
1437 	}
1438 }
1439 
1440 /**
1441  * cb_dialog_doc_metadata_remove_clicked
1442  * @remove_bt: widget
1443  * @state: dialog main struct
1444  *
1445  * Removes a property from the tree view and updates all the dialog and
1446  * the GsfDocMetaData struct.
1447  *
1448  **/
1449 static void
cb_dialog_doc_metadata_remove_clicked(GtkWidget * remove_bt,DialogDocMetaData * state)1450 cb_dialog_doc_metadata_remove_clicked (GtkWidget         *remove_bt,
1451 				       DialogDocMetaData *state)
1452 {
1453 	GtkTreeIter tree_iter;
1454 	GValue      *prop_name;
1455 	GtkTreeSelection *sel = gtk_tree_view_get_selection (state->properties);
1456 
1457 	g_return_if_fail (state->metadata != NULL);
1458 
1459 	if (gtk_tree_selection_get_selected (sel, NULL, &tree_iter)) {
1460 
1461 		/* Get the property name */
1462 		prop_name = g_new0 (GValue, 1);
1463 		gtk_tree_model_get_value (GTK_TREE_MODEL (state->properties_store),
1464 					  &tree_iter,
1465 					  0,
1466 					  prop_name);
1467 
1468 		/* Update other pages */
1469 		dialog_doc_metadata_update_prop (state,
1470 						 g_value_get_string (prop_name),
1471 						 NULL, NULL);
1472 
1473 		/* Remove property from GsfMetadata */
1474 		cmd_change_meta_data (GNM_WBC (state->wbcg), NULL,
1475 				      g_slist_prepend (NULL, g_value_dup_string (prop_name)));
1476 
1477 		/* Remove from Tree View */
1478 		gtk_tree_store_remove (state->properties_store,
1479 				       &tree_iter);
1480 
1481 		/* Free all data */
1482 		g_value_unset (prop_name);
1483 		g_free (prop_name);
1484 	}
1485 
1486 	/* Set remove button insensitive */
1487 	gtk_widget_set_sensitive (remove_bt, FALSE);
1488 }
1489 
1490 
1491 /**
1492  * cb_dialog_doc_metadata_tree_prop_selected
1493  * @combo_box: widget
1494  * @state: dialog main struct
1495  *
1496  * Update the highlited item in the 'Properties' page combo box.
1497  *
1498  **/
1499 static void
cb_dialog_doc_metadata_tree_prop_selected(GtkTreeSelection * selection,DialogDocMetaData * state)1500 cb_dialog_doc_metadata_tree_prop_selected (GtkTreeSelection  *selection,
1501 					   DialogDocMetaData *state)
1502 {
1503 	GtkTreeIter iter;
1504 	gboolean selected;
1505 	gchar const *text = "";
1506 
1507 	g_return_if_fail (state->metadata != NULL);
1508 
1509 	selected = gtk_tree_selection_get_selected (selection, NULL, &iter);
1510 
1511 	/* Set remove button sensitive */
1512 	gtk_widget_set_sensitive (GTK_WIDGET (state->remove_button), selected);
1513 
1514 	if (selected) {
1515 		GType val_type = G_TYPE_INVALID;
1516 		gchar *prop_name = NULL;
1517 		gtk_tree_model_get (GTK_TREE_MODEL (state->properties_store),
1518 				    &iter,
1519 				    0, &prop_name,
1520 				    4, &val_type,
1521 				    -1);
1522 		switch (val_type) {
1523 		case G_TYPE_STRING:
1524 			text = _("Edit string value directly in above listing.");
1525 			break;
1526 		case G_TYPE_UINT:
1527 			text = _("Edit positive integer value directly in above listing.");
1528 			break;
1529 		case G_TYPE_INT:
1530 			text = _("Edit integer value directly in above listing.");
1531 			break;
1532 		case G_TYPE_FLOAT:
1533 		case G_TYPE_DOUBLE:
1534 			text = _("Edit decimal number value directly in above listing.");
1535 			break;
1536 		case G_TYPE_BOOLEAN:
1537 			text = _("Edit TRUE/FALSE value directly in above listing.");
1538 			break;
1539 		default:
1540 			if (val_type == GSF_DOCPROP_VECTOR_TYPE) {
1541 				if (0 == strcmp (prop_name, "dc:keywords"))
1542 					text = _("To edit, use the keywords tab.");
1543 				else
1544 					text = _("This property value cannot be edited.");
1545 			} else if (val_type == GSF_TIMESTAMP_TYPE)
1546 				text= _("Edit timestamp directly in above listing.");
1547 			break;
1548 		}
1549 		g_free (prop_name);
1550 	}
1551 
1552 	gtk_label_set_text (state->instruction, text);
1553 }
1554 
1555 /**
1556  * dialog_doc_metadata_get_prop_val:
1557  * @state:
1558  * @prop_name: property name
1559  * @prop_value: property value
1560  *
1561  * Retrieves an arbitrary property value always as string.
1562  *
1563  **/
1564 static gchar *
dialog_doc_metadata_get_prop_val(G_GNUC_UNUSED DialogDocMetaData * state,char const * prop_name,GValue * prop_value)1565 dialog_doc_metadata_get_prop_val (G_GNUC_UNUSED DialogDocMetaData *state,
1566 				  char const                      *prop_name,
1567 				  GValue                          *prop_value)
1568 {
1569 	GValue str_value = G_VALUE_INIT;
1570 	gboolean ret = FALSE;
1571 	GType t;
1572 	char *s;
1573 
1574 	g_return_val_if_fail (prop_value != NULL, NULL);
1575 
1576 	g_value_init (&str_value, G_TYPE_STRING);
1577 	t = G_VALUE_TYPE (prop_value);
1578 	switch (t) {
1579 	case G_TYPE_INT:
1580 	case G_TYPE_UINT:
1581 	case G_TYPE_STRING:
1582 		ret = g_value_transform (prop_value, &str_value);
1583 		break;
1584 
1585 	case G_TYPE_BOOLEAN: {
1586 		gboolean b = g_value_get_boolean (prop_value);
1587 		g_value_set_string (&str_value, go_locale_boolean_name (b));
1588 		ret = TRUE;
1589 		break;
1590 	}
1591 
1592 	case G_TYPE_FLOAT:
1593 	case G_TYPE_DOUBLE: {
1594 		double d = (t == G_TYPE_FLOAT)
1595 			? g_value_get_float (prop_value)
1596 			: g_value_get_double (prop_value);
1597 		GString *res = g_string_new (NULL);
1598 		go_dtoa (res, "!g", d);
1599 		g_value_set_string (&str_value, res->str);
1600 		g_string_free (res, TRUE);
1601 		ret = TRUE;
1602 		break;
1603 	}
1604 	}
1605 
1606 	if (t == GSF_TIMESTAMP_TYPE) {
1607 		dialog_doc_metadata_transform_timestamp_to_str (prop_value, &str_value);
1608 		ret = TRUE;
1609 	} else if (t == GSF_DOCPROP_VECTOR_TYPE) {
1610 		dialog_doc_metadata_transform_docprop_vect_to_str (prop_value, &str_value);
1611 		ret = TRUE;
1612 	}
1613 
1614 	if (ret == FALSE) {
1615 		g_warning ("Metadata tag '%s' holds unrecognized value type.", prop_name);
1616 		return NULL;
1617 	}
1618 
1619 	s = g_value_dup_string (&str_value);
1620 	g_value_unset (&str_value);
1621 	return s;
1622 }
1623 
1624 /**
1625  * dialog_doc_metadata_populate_tree_view
1626  * @name: property name
1627  * @prop: property stored in GsfDocMetaData
1628  * @state: dialog main struct
1629  *
1630  * Populates the tree view in 'Properties' page.
1631  *
1632  **/
1633 static void
dialog_doc_metadata_populate_tree_view(gchar * name,GsfDocProp * prop,DialogDocMetaData * state)1634 dialog_doc_metadata_populate_tree_view (gchar             *name,
1635 					GsfDocProp	  *prop,
1636 					DialogDocMetaData *state)
1637 {
1638 	gchar  *str_value;
1639 	char   *link_value;
1640 	GValue *value;
1641 
1642 	g_return_if_fail (state->metadata != NULL);
1643 
1644 	value = (GValue *) gsf_doc_prop_get_val (prop);
1645 	/* Get the prop value as string */
1646 	str_value = dialog_doc_metadata_get_prop_val (state, name, value);
1647 
1648 	link_value = (char *) gsf_doc_prop_get_link (prop);
1649 
1650 	dialog_doc_metadata_add_prop
1651 		(state,
1652 		 gsf_doc_prop_get_name (prop),
1653 		 str_value == NULL ? "" : str_value,
1654 		 link_value == NULL ? "" : link_value,
1655 		 dialog_doc_metadata_get_value_type (value));
1656 
1657 	dialog_doc_metadata_update_prop (state, gsf_doc_prop_get_name (prop), str_value, prop);
1658 
1659 	g_free (str_value);
1660 }
1661 
1662 static gboolean
dialog_doc_metadata_show_all_types(GtkTreeModel * model,G_GNUC_UNUSED GtkTreePath * path,GtkTreeIter * iter,G_GNUC_UNUSED gpointer data)1663 dialog_doc_metadata_show_all_types (GtkTreeModel *model,
1664 				    G_GNUC_UNUSED GtkTreePath *path,
1665 				    GtkTreeIter *iter,
1666 				    G_GNUC_UNUSED gpointer data)
1667 {
1668 	gtk_list_store_set (GTK_LIST_STORE (model), iter, 2, TRUE, -1);
1669 	return FALSE;
1670 }
1671 
1672 static gboolean
dialog_doc_metadata_show_this_type(GtkTreeModel * model,G_GNUC_UNUSED GtkTreePath * path,GtkTreeIter * iter,gpointer data)1673 dialog_doc_metadata_show_this_type (GtkTreeModel *model,
1674 				    G_GNUC_UNUSED GtkTreePath *path,
1675 				    GtkTreeIter *iter,
1676 				    gpointer data)
1677 {
1678 	GType t, type = GPOINTER_TO_INT (data);
1679 
1680 	gtk_tree_model_get (model, iter, 1, &t, -1);
1681 	gtk_list_store_set (GTK_LIST_STORE (model), iter, 2, t == type, -1);
1682 	return FALSE;
1683 }
1684 
1685 static gboolean
cb_dialog_doc_metadata_ppt_changed(G_GNUC_UNUSED GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1686 cb_dialog_doc_metadata_ppt_changed (G_GNUC_UNUSED GtkEntry          *entry,
1687 				    G_GNUC_UNUSED GdkEventFocus     *event,
1688 				    DialogDocMetaData *state)
1689 {
1690 	const gchar *name;
1691 	const gchar *value;
1692 	gchar *name_trimmed;
1693 	gboolean enable = FALSE;
1694 	gchar *str = NULL;
1695 	GsfDocProp *prop = NULL;
1696 	GtkTreeIter iter;
1697 
1698 	name = gtk_entry_get_text (state->ppt_name);
1699 	value = gtk_entry_get_text (state->ppt_value);
1700 	name_trimmed = trim_string (name);
1701 
1702 	enable = strlen (name_trimmed) > 0 && strlen (value) > 0;
1703 
1704 	if (enable)
1705 		enable = gtk_combo_box_get_active_iter (state->ppt_type, &iter);
1706 
1707 	if (enable) {
1708 		prop = gsf_doc_meta_data_lookup (state->metadata, name_trimmed);
1709 		if (prop != NULL) {
1710 			str = g_strdup_printf
1711 				(_("A document property with the name \'%s\' already exists."),
1712 				 name_trimmed);
1713 			enable = FALSE;
1714 		}
1715 	}
1716 	g_free (name_trimmed);
1717 	gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), enable);
1718 	gtk_label_set_text (state->warning, str ? str : "");
1719 	g_free (str);
1720 	return FALSE;
1721 }
1722 
1723 static void
cb_dialog_doc_metadata_ppt_type_changed(G_GNUC_UNUSED GtkComboBox * widget,DialogDocMetaData * state)1724 cb_dialog_doc_metadata_ppt_type_changed (G_GNUC_UNUSED GtkComboBox *widget,
1725 					 DialogDocMetaData *state)
1726 {
1727 	cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1728 }
1729 
1730 static gboolean
cb_dialog_doc_metadata_ppt_name_changed(G_GNUC_UNUSED GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1731 cb_dialog_doc_metadata_ppt_name_changed (G_GNUC_UNUSED GtkEntry          *entry,
1732 					 G_GNUC_UNUSED GdkEventFocus     *event,
1733 					 DialogDocMetaData *state)
1734 {
1735 	const gchar *name;
1736 	gchar *name_trimmed;
1737 	gboolean enable = FALSE;
1738 	GtkTreeIter iter;
1739 	gchar *str = NULL;
1740 
1741 	name = gtk_entry_get_text (state->ppt_name);
1742 	name_trimmed = trim_string (name);
1743 
1744 	enable = strlen (name_trimmed) > 0;
1745 
1746 	if (enable) {
1747 		GType t = dialog_doc_metadata_get_value_type_from_name (name_trimmed, G_TYPE_INVALID);
1748 
1749 		if (t == GSF_DOCPROP_VECTOR_TYPE) {
1750 			str = g_strdup_printf
1751 					(_("Use the keywords tab to create this property."));
1752 			enable = FALSE;
1753 		}
1754 
1755 		if (t == G_TYPE_INVALID) {
1756 			g_signal_handlers_block_by_func(G_OBJECT (state->ppt_type),
1757 							cb_dialog_doc_metadata_ppt_type_changed,
1758 							state);
1759 			gtk_tree_model_foreach (GTK_TREE_MODEL (state->type_store),
1760 						dialog_doc_metadata_show_all_types, NULL);
1761 			gtk_tree_model_filter_refilter (state->type_store_filter);
1762 			g_signal_handlers_unblock_by_func(G_OBJECT (state->ppt_type),
1763 							  cb_dialog_doc_metadata_ppt_type_changed,
1764 							  state);
1765 		} else {
1766 			gtk_combo_box_set_active_iter (state->ppt_type, NULL);
1767 			g_signal_handlers_block_by_func(G_OBJECT (state->ppt_type),
1768 							cb_dialog_doc_metadata_ppt_type_changed,
1769 							state);
1770 			gtk_tree_model_foreach (GTK_TREE_MODEL (state->type_store),
1771 						dialog_doc_metadata_show_this_type,
1772 						GINT_TO_POINTER (t));
1773 			gtk_tree_model_filter_refilter (state->type_store_filter);
1774 			g_signal_handlers_unblock_by_func(G_OBJECT (state->ppt_type),
1775 							  cb_dialog_doc_metadata_ppt_type_changed,
1776 							  state);
1777 			if (gtk_tree_model_get_iter_first
1778 			    (GTK_TREE_MODEL (state->type_store_filter), &iter))
1779 				gtk_combo_box_set_active_iter (state->ppt_type, &iter);
1780 		}
1781 	}
1782 	g_free (name_trimmed);
1783 
1784 	if (enable)
1785 		return cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1786 	else {
1787 		gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), FALSE);
1788 		gtk_label_set_text (state->warning, str ? str : "");
1789 		g_free (str);
1790 	}
1791 
1792 	return FALSE;
1793 }
1794 
1795 
1796 
1797 /**
1798  * dialog_doc_metadata_init_properties_page
1799  * @state: dialog main struct
1800  *
1801  * Initializes the widgets and signals for the 'Properties' page.
1802  *
1803  **/
1804 static void
dialog_doc_metadata_init_properties_page(DialogDocMetaData * state)1805 dialog_doc_metadata_init_properties_page (DialogDocMetaData *state)
1806 {
1807 	GtkTreeSelection *sel;
1808 	GtkCellRenderer  *cell;
1809 	guint i;
1810 
1811 	struct types {
1812 		char const *type_name;
1813 		GType type;
1814 	} ppt_types[] = {
1815 		{N_("String"), G_TYPE_STRING},
1816 		{N_("Integer"), G_TYPE_INT},
1817 		{N_("Decimal Number"), G_TYPE_FLOAT},
1818 		{N_("TRUE/FALSE"), G_TYPE_BOOLEAN}
1819 	};
1820 
1821 	g_return_if_fail (state->metadata != NULL);
1822 	g_return_if_fail (state->properties != NULL);
1823 
1824 	/* Set Remove and Apply buttons insensitive */
1825 	gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), FALSE);
1826 	gtk_widget_set_sensitive (GTK_WIDGET (state->remove_button), FALSE);
1827 
1828 	/* Intialize Combo Box */
1829 	/* gtk_combo_box_set_id_column (state->ppt_type, 0); */
1830 	cell = gtk_cell_renderer_text_new();
1831 	gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(state->ppt_type), cell, TRUE);
1832 	gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(state->ppt_type), cell, "text", 0, NULL);
1833 
1834 	for (i = 0; i < G_N_ELEMENTS (ppt_types); i++)
1835 		gtk_list_store_insert_with_values (state->type_store, NULL, G_MAXINT,
1836 						   0, _(ppt_types[i].type_name),
1837 						   1, ppt_types[i].type,
1838 						   2, TRUE,
1839 						   -1);
1840 	gtk_list_store_insert_with_values (state->type_store, NULL, G_MAXINT,
1841 					   0, _("Date & Time"),
1842 					   1, GSF_TIMESTAMP_TYPE,
1843 					   2, TRUE,
1844 					   -1);
1845 	gtk_tree_model_filter_set_visible_column (state->type_store_filter, 2);
1846 	gtk_tree_model_filter_refilter (state->type_store_filter);
1847 
1848 	/* Populate Treeview */
1849 	state->properties_store = gtk_tree_store_new (5,
1850 						      G_TYPE_STRING,
1851 						      G_TYPE_STRING,
1852 						      G_TYPE_STRING,
1853 						      G_TYPE_BOOLEAN,
1854 						      G_TYPE_GTYPE);
1855 
1856 	gtk_tree_view_set_model (state->properties,
1857 				 GTK_TREE_MODEL (state->properties_store));
1858 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (state->properties_store),
1859 					      0, GTK_SORT_ASCENDING);
1860 	g_object_unref (state->properties_store);
1861 
1862 	/* Append Columns */
1863 	gtk_tree_view_insert_column_with_attributes (state->properties,
1864 						     0, _("Name"),
1865 						     gtk_cell_renderer_text_new(),
1866 						     "text", 0,
1867 						     NULL);
1868 
1869 	cell =  gtk_cell_renderer_text_new();
1870 	gtk_tree_view_insert_column_with_attributes (state->properties,
1871 						     1, _("Value"),
1872 						     cell,
1873 						     "text", 1,
1874 						     "editable", 3,
1875 						     NULL);
1876 	g_signal_connect (G_OBJECT (cell),
1877 			  "edited",
1878 			  G_CALLBACK (cb_dialog_doc_metadata_value_edited),
1879 			  state);
1880 
1881 	gtk_tree_view_insert_column_with_attributes (state->properties,
1882 						     2, _("Linked To"),
1883 						     gtk_cell_renderer_text_new(),
1884 						     "text", 2,
1885 						     NULL);
1886 
1887 	/* Read all metadata */
1888 	gsf_doc_meta_data_foreach (state->metadata,
1889 				   (GHFunc) dialog_doc_metadata_populate_tree_view,
1890 				   state);
1891 
1892 	/* Set up signals */
1893 	/* Tree View */
1894 	sel = gtk_tree_view_get_selection (state->properties);
1895 	g_signal_connect (G_OBJECT (sel),
1896 			  "changed",
1897 			  G_CALLBACK (cb_dialog_doc_metadata_tree_prop_selected),
1898 			  state);
1899 
1900 	/* Entries */
1901 	g_signal_connect (G_OBJECT (state->ppt_name),
1902 			  "focus-out-event",
1903 			  G_CALLBACK (cb_dialog_doc_metadata_ppt_name_changed),
1904 			  state);
1905 	g_signal_connect (G_OBJECT (state->ppt_value),
1906 			  "focus-out-event",
1907 			  G_CALLBACK (cb_dialog_doc_metadata_ppt_changed),
1908 			  state);
1909 	/* ComboBox */
1910 	g_signal_connect (G_OBJECT (state->ppt_type),
1911 			  "changed",
1912 			  G_CALLBACK (cb_dialog_doc_metadata_ppt_type_changed),
1913 			  state);
1914 
1915 	/* 'Add', 'Remove' and 'Apply' Button Signals */
1916 	g_signal_connect (G_OBJECT (state->add_button),
1917 			  "clicked",
1918 			  G_CALLBACK (cb_dialog_doc_metadata_add_clicked),
1919 			  state);
1920 
1921 	g_signal_connect (G_OBJECT (state->remove_button),
1922 			  "clicked",
1923 			  G_CALLBACK (cb_dialog_doc_metadata_remove_clicked),
1924 			  state);
1925 
1926 	cb_dialog_doc_metadata_tree_prop_selected (sel, state);
1927 	gtk_combo_box_set_active (state->ppt_type, 0);
1928 }
1929 
1930 /******************************************************************************
1931  * FUNCTIONS RELATED TO 'STATISTICS' PAGE
1932  ******************************************************************************/
1933 
1934 /**
1935  * dialog_doc_metadata_init_statistics_page
1936  * @state: dialog main struct
1937  *
1938  * Initializes the widgets and signals for the 'Statistics' page.
1939  *
1940  **/
1941 static void
dialog_doc_metadata_init_statistics_page(DialogDocMetaData * state)1942 dialog_doc_metadata_init_statistics_page (DialogDocMetaData *state)
1943 {
1944 	g_return_if_fail (state->metadata != NULL);
1945 
1946 	/* Set up the labels */
1947 	dialog_doc_metadata_set_label (state, state->sheets, NULL, TRUE);
1948 	dialog_doc_metadata_set_label (state, state->cells, NULL, TRUE);
1949 	dialog_doc_metadata_set_label (state, state->pages, NULL, TRUE);
1950 }
1951 
1952 /******************************************************************************
1953  * FUNCTIONS RELATED TO 'CALCULATIONS' PAGE
1954  ******************************************************************************/
1955 
1956 static gboolean
cb_dialog_doc_metadata_recalc_max_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1957 cb_dialog_doc_metadata_recalc_max_changed (GtkEntry          *entry,
1958 					   G_GNUC_UNUSED GdkEventFocus *event,
1959 					   DialogDocMetaData *state)
1960 {
1961 	int val;
1962 	if (!entry_to_int (entry, &val, TRUE))
1963 		/* FIXME: make undoable */
1964 		workbook_iteration_max_number (state->wb, val);
1965 	return FALSE;
1966 }
1967 
1968 static gboolean
cb_dialog_doc_metadata_recalc_tolerance_changed(GtkEntry * entry,G_GNUC_UNUSED GdkEventFocus * event,DialogDocMetaData * state)1969 cb_dialog_doc_metadata_recalc_tolerance_changed (GtkEntry          *entry,
1970 					   G_GNUC_UNUSED GdkEventFocus *event,
1971 					   DialogDocMetaData *state)
1972 {
1973 	gnm_float val;
1974 	if (!entry_to_float (entry, &val, TRUE))
1975 		/* FIXME: make undoable */
1976 		workbook_iteration_tolerance (state->wb, val);
1977 	return FALSE;
1978 }
1979 
1980 static void
cb_dialog_doc_metadata_recalc_auto_changed(GtkWidget * widget,DialogDocMetaData * state)1981 cb_dialog_doc_metadata_recalc_auto_changed (GtkWidget *widget, DialogDocMetaData *state)
1982 {
1983 	/* FIXME: make undoable */
1984 	workbook_set_recalcmode	(state->wb, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
1985 }
1986 
1987 static void
cb_dialog_doc_metadata_recalc_iteration_changed(G_GNUC_UNUSED GtkWidget * widget,DialogDocMetaData * state)1988 cb_dialog_doc_metadata_recalc_iteration_changed (G_GNUC_UNUSED GtkWidget *widget, DialogDocMetaData *state)
1989 {
1990 	/* FIXME: make undoable */
1991 	workbook_iteration_enabled (state->wb, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
1992 	gtk_widget_set_sensitive (state->recalc_iteration_grid, state->wb->iteration.enabled);
1993 }
1994 
1995 /**
1996  * dialog_doc_metadata_init_calculations_page
1997  * @state: dialog main struct
1998  *
1999  * Initializes the widgets and signals for the 'Calculations' page.
2000  *
2001  **/
2002 static void
dialog_doc_metadata_init_calculations_page(DialogDocMetaData * state)2003 dialog_doc_metadata_init_calculations_page (DialogDocMetaData *state)
2004 {
2005 	char *buf;
2006 
2007 	gtk_toggle_button_set_active
2008 		(GTK_TOGGLE_BUTTON ( workbook_get_recalcmode (state->wb) ? state->recalc_auto : state->recalc_manual),
2009 		 TRUE);
2010 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->recalc_iteration),
2011 				      state->wb->iteration.enabled);
2012 	gtk_widget_set_sensitive (state->recalc_iteration_grid, state->wb->iteration.enabled);
2013 
2014 	buf = g_strdup_printf ("%d", state->wb->iteration.max_number);
2015 	gtk_entry_set_text (state->recalc_max, buf);
2016 	g_free (buf);
2017 	buf = g_strdup_printf ("%g", state->wb->iteration.tolerance);
2018 	gtk_entry_set_text (state->recalc_tolerance, buf);
2019 	g_free (buf);
2020 
2021 	g_signal_connect (G_OBJECT (state->recalc_auto),
2022 			  "toggled",
2023 			  G_CALLBACK (cb_dialog_doc_metadata_recalc_auto_changed), state);
2024 	g_signal_connect (G_OBJECT (state->recalc_iteration),
2025 			  "toggled",
2026 			  G_CALLBACK (cb_dialog_doc_metadata_recalc_iteration_changed), state);
2027 	g_signal_connect (G_OBJECT (state->recalc_max),
2028 			  "focus-out-event",
2029 			  G_CALLBACK (cb_dialog_doc_metadata_recalc_max_changed),
2030 			  state);
2031 	g_signal_connect (G_OBJECT (state->recalc_tolerance),
2032 			  "focus-out-event",
2033 			  G_CALLBACK (cb_dialog_doc_metadata_recalc_tolerance_changed),
2034 			  state);
2035 
2036 }
2037 
2038 /******************************************************************************
2039  * DIALOG INITIALIZE/FINALIZE FUNCTIONS
2040  ******************************************************************************/
2041 
2042 /**
2043  * dialog_doc_metadata_set_file_permissions
2044  * @state: dialog main struct
2045  *
2046  * Writes the new file permissions if there were any changes.
2047  *
2048  **/
2049 static void
dialog_doc_metadata_set_file_permissions(DialogDocMetaData * state)2050 dialog_doc_metadata_set_file_permissions (DialogDocMetaData *state)
2051 {
2052 	if (state->file_permissions != NULL &&
2053 	    state->permissions_changed == TRUE)
2054 		go_set_file_permissions (go_doc_get_uri (state->doc),
2055 					 state->file_permissions);
2056 }
2057 
2058 static void
dialog_doc_metadata_free(DialogDocMetaData * state)2059 dialog_doc_metadata_free (DialogDocMetaData *state)
2060 {
2061 	WorkbookControl *wbc = GNM_WBC (state->wbcg);
2062 
2063 	wb_view_selection_desc (wb_control_view (wbc), TRUE, wbc);
2064 
2065 	if (state->gui != NULL) {
2066 		dialog_doc_metadata_set_file_permissions (state);
2067 
2068 		g_object_unref (state->gui);
2069 		state->gui = NULL;
2070 	}
2071 
2072 	g_free (state->file_permissions);
2073 	state->file_permissions = NULL;
2074 
2075 	wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
2076 
2077 	state->dialog = NULL;
2078 
2079 	g_free (state);
2080 }
2081 
2082 static void
dialog_doc_metadata_init_widgets(DialogDocMetaData * state)2083 dialog_doc_metadata_init_widgets (DialogDocMetaData *state)
2084 {
2085 	state->dialog = go_gtk_builder_get_widget (state->gui, "GOMetadataDialog");
2086 
2087 	state->notebook     = GTK_NOTEBOOK (go_gtk_builder_get_widget (state->gui, "notebook"));
2088 	state->help_button  = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "help_button"));
2089 	state->close_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "close_button"));
2090 
2091 	/* File Information Page */
2092 	state->file_name = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "file_name"));
2093 	state->location  = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "location"));
2094 	state->created   = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "created"));
2095 	state->modified  = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "modified"));
2096 	state->accessed  = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "accessed"));
2097 	state->owner     = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "owner"));
2098 	state->group     = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "group"));
2099 
2100 	state->owner_read  = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "owner_read"));
2101 	state->owner_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "owner_write"));
2102 
2103 	state->group_read  = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "group_read"));
2104 	state->group_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "group_write"));
2105 
2106 	state->others_read  = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "others_read"));
2107 	state->others_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "others_write"));
2108 
2109 	/* Description Page */
2110 	state->title    = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "title"));
2111 	state->subject  = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "subject"));
2112 	state->author   = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "author"));
2113 	state->manager  = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "manager"));
2114 	state->company  = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "company"));
2115 	state->category = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "category"));
2116 
2117 	state->comments = GTK_TEXT_VIEW (go_gtk_builder_get_widget (state->gui, "comments"));
2118 
2119 	/* Properties Page */
2120 	state->properties = GTK_TREE_VIEW (go_gtk_builder_get_widget (state->gui, "properties"));
2121 
2122 	state->ppt_name  = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "property-name"));
2123 	state->ppt_value  = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "property-value"));
2124 	state->ppt_type  = GTK_COMBO_BOX (go_gtk_builder_get_widget (state->gui, "type-combo"));
2125 	state->type_store = GTK_LIST_STORE (gtk_builder_get_object (state->gui, "typestore"));
2126 	state->type_store_filter = GTK_TREE_MODEL_FILTER (gtk_combo_box_get_model (state->ppt_type));
2127 
2128 	state->add_button    = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "add_button"));
2129 	state->remove_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "remove_button"));
2130 	state->instruction   = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "instruction-label"));
2131 	state->warning   = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "warning"));
2132 
2133 	/* Keyword Page */
2134 	state->key_tree_view = GTK_TREE_VIEW  (go_gtk_builder_get_widget (state->gui, "keyview"));
2135 	state->key_store = GTK_LIST_STORE (gtk_tree_view_get_model (state->key_tree_view));
2136 	state->key_add_button  = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "key-add-button"));
2137 	state->key_remove_button  = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "key-remove-button"));
2138 
2139 	/* Statistics Page */
2140 	state->sheets = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "sheets"));
2141 	state->cells  = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "cells"));
2142 	state->pages  = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "pages"));
2143 
2144 	/* Calculations Page */
2145 	state->recalc_auto = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "recalc_auto"));
2146 	state->recalc_manual = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "recalc_manual"));
2147 	state->recalc_iteration = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "iteration_enabled"));
2148 	state->recalc_max  = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "max_iterations"));
2149 	state->recalc_tolerance  = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "iteration_tolerance"));
2150 	state->recalc_iteration_grid = go_gtk_builder_get_widget (state->gui, "iteration-grid");
2151 }
2152 
2153 static void
dialog_doc_meta_data_add_item(DialogDocMetaData * state,char const * page_name,char const * icon_name,int page,char const * parent_path)2154 dialog_doc_meta_data_add_item (DialogDocMetaData *state, char const *page_name,
2155 			       char const *icon_name,
2156 			       int page, char const* parent_path)
2157 {
2158 	GtkTreeIter iter, parent;
2159 	GdkPixbuf * icon = NULL;
2160 
2161 	if (icon_name != NULL)
2162 		icon = gtk_widget_render_icon_pixbuf (state->dialog, icon_name,
2163 					       GTK_ICON_SIZE_MENU);
2164 	if ((parent_path != NULL) && gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (state->store),
2165 									  &parent, parent_path))
2166 		gtk_tree_store_append (state->store, &iter, &parent);
2167 	else
2168 		gtk_tree_store_append (state->store, &iter, NULL);
2169 
2170 	gtk_tree_store_set (state->store, &iter,
2171 			    ITEM_ICON, icon,
2172 			    ITEM_NAME, _(page_name),
2173 			    PAGE_NUMBER, page,
2174 			    -1);
2175 	if (icon != NULL)
2176 		g_object_unref (icon);
2177 }
2178 
2179 typedef struct {
2180 	char const *page_name;
2181 	char const *icon_name;
2182 	char const *parent_path;
2183 	int  const page;
2184 	void (*page_initializer) (DialogDocMetaData *state);
2185 } page_info_t;
2186 
2187 static page_info_t const page_info[] = {
2188 	/* IMPORTANT: OBEY THE ORDER 0 - 3 - 2 - 1 */
2189 	{N_("File"),        GTK_STOCK_FILE,   	  NULL, 0, &dialog_doc_metadata_init_file_page          },
2190 	{N_("Statistics"),  "gnumeric-graphguru", NULL, 3 ,&dialog_doc_metadata_init_statistics_page    },
2191 	{N_("Properties"),  GTK_STOCK_PROPERTIES, NULL, 2, &dialog_doc_metadata_init_properties_page    },
2192 	{N_("Description"), GTK_STOCK_ABOUT,	  NULL, 1, &dialog_doc_metadata_init_description_page   },
2193 	{N_("Keywords"),    GTK_STOCK_INDEX,	  NULL, 5, &dialog_doc_metadata_init_keywords_page   },
2194 	{N_("Calculation"), GTK_STOCK_EXECUTE,    NULL, 4, &dialog_doc_metadata_init_calculations_page  },
2195 	{NULL, NULL, NULL, -1, NULL},
2196 };
2197 
2198 typedef struct {
2199 	int  const page;
2200 	GtkTreePath *path;
2201 } page_search_t;
2202 
2203 static gboolean
dialog_doc_metadata_select_page_search(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,page_search_t * pst)2204 dialog_doc_metadata_select_page_search (GtkTreeModel *model,
2205 					GtkTreePath *path,
2206 					GtkTreeIter *iter,
2207 					page_search_t *pst)
2208 {
2209 	int page;
2210 	gtk_tree_model_get (model, iter, PAGE_NUMBER, &page, -1);
2211 	if (page == pst->page) {
2212 		pst->path = gtk_tree_path_copy (path);
2213 		return TRUE;
2214 	} else
2215 		return FALSE;
2216 }
2217 
2218 static void
dialog_doc_metadata_select_page(DialogDocMetaData * state,int page)2219 dialog_doc_metadata_select_page (DialogDocMetaData *state, int page)
2220 {
2221 	page_search_t pst = {page, NULL};
2222 
2223 	if (page >= 0)
2224 		gtk_tree_model_foreach (GTK_TREE_MODEL (state->store),
2225 					(GtkTreeModelForeachFunc) dialog_doc_metadata_select_page_search,
2226 					&pst);
2227 
2228 	if (pst.path == NULL)
2229 		pst.path = gtk_tree_path_new_from_string ("0");
2230 
2231 	if (pst.path != NULL) {
2232 		gtk_tree_view_set_cursor (state->view, pst.path, NULL, FALSE);
2233 		gtk_tree_view_expand_row (state->view, pst.path, TRUE);
2234 		gtk_tree_path_free (pst.path);
2235 	}
2236 }
2237 
2238 static void
cb_dialog_doc_metadata_selection_changed(GtkTreeSelection * selection,DialogDocMetaData * state)2239 cb_dialog_doc_metadata_selection_changed (GtkTreeSelection *selection,
2240 					  DialogDocMetaData *state)
2241 {
2242 	GtkTreeIter iter;
2243 	int page;
2244 
2245 	if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
2246 		gtk_tree_model_get (GTK_TREE_MODEL (state->store), &iter,
2247 				    PAGE_NUMBER, &page,
2248 				    -1);
2249 		gtk_notebook_set_current_page (state->notebook, page);
2250 	} else {
2251 		dialog_doc_metadata_select_page (state, 0);
2252 	}
2253 }
2254 
2255 static gboolean
dialog_doc_metadata_init(DialogDocMetaData * state,WBCGtk * wbcg)2256 dialog_doc_metadata_init (DialogDocMetaData *state,
2257 			  WBCGtk *wbcg)
2258 {
2259 	GtkTreeViewColumn *column;
2260 	GtkTreeSelection  *selection;
2261 	int i;
2262 
2263 	state->wbcg     = wbcg;
2264 	state->wb       = wb_control_get_workbook (GNM_WBC(wbcg));
2265 	state->doc      = GO_DOC (state->wb);
2266 	state->metadata = go_doc_get_meta_data (wb_control_get_doc (GNM_WBC (state->wbcg)));
2267 
2268 	g_return_val_if_fail (state->metadata  != NULL, TRUE);
2269 
2270 	state->gui = gnm_gtk_builder_load ("res:ui/doc-meta-data.ui", NULL,
2271 	                                  GO_CMD_CONTEXT (wbcg));
2272 
2273         if (state->gui == NULL)
2274                 return TRUE;
2275 
2276 	dialog_doc_metadata_init_widgets (state);
2277 
2278 	state->view = GTK_TREE_VIEW(go_gtk_builder_get_widget (state->gui, "itemlist"));
2279 	state->store = gtk_tree_store_new (NUM_COLUMNS,
2280 					   GDK_TYPE_PIXBUF,
2281 					   G_TYPE_STRING,
2282 					   G_TYPE_INT);
2283 	gtk_tree_view_set_model (state->view, GTK_TREE_MODEL(state->store));
2284 	g_object_unref (state->store);
2285 	selection = gtk_tree_view_get_selection (state->view);
2286 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2287 	column = gtk_tree_view_column_new_with_attributes ("",
2288 							   gtk_cell_renderer_pixbuf_new (),
2289 							   "pixbuf", ITEM_ICON,
2290 							   NULL);
2291 	gtk_tree_view_append_column (state->view, column);
2292 	column = gtk_tree_view_column_new_with_attributes ("",
2293 							   gtk_cell_renderer_text_new (),
2294 							   "text", ITEM_NAME,
2295 							   NULL);
2296 	gtk_tree_view_append_column (state->view, column);
2297 	gtk_tree_view_set_expander_column (state->view, column);
2298 
2299 	g_signal_connect (selection,
2300 			  "changed",
2301 			  G_CALLBACK (cb_dialog_doc_metadata_selection_changed), state);
2302 
2303 	for (i = 0; page_info[i].page > -1; i++) {
2304 		const page_info_t *this_page =  &page_info[i];
2305 		this_page->page_initializer (state);
2306 		dialog_doc_meta_data_add_item (state, this_page->page_name, this_page->icon_name,
2307 					       this_page->page, this_page->parent_path);
2308 	}
2309 
2310 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (state->store), ITEM_NAME, GTK_SORT_ASCENDING);
2311 
2312 	/* A candidate for merging into attach guru */
2313 	gnm_keyed_dialog (state->wbcg,
2314 			       GTK_WINDOW (state->dialog),
2315 			       DOC_METADATA_KEY);
2316 
2317 
2318 	go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
2319 				GTK_WINDOW (state->dialog));
2320 
2321 	wbc_gtk_attach_guru (state->wbcg, state->dialog);
2322 	g_object_set_data_full (G_OBJECT (state->dialog), "state",
2323 		state, (GDestroyNotify) dialog_doc_metadata_free);
2324 
2325 	/* Help and Close buttons */
2326 	gnm_init_help_button (GTK_WIDGET (state->help_button),
2327 				   GNUMERIC_HELP_LINK_METADATA);
2328 
2329 	g_signal_connect_swapped (G_OBJECT (state->close_button),
2330 				  "clicked",
2331 				  G_CALLBACK (gtk_widget_destroy),
2332 				  state->dialog);
2333 
2334 	gtk_widget_show_all (GTK_WIDGET (state->dialog));
2335 
2336 	return FALSE;
2337 }
2338 
2339 /******************************************************************************
2340  * EXPORTED FUNCTIONS
2341  ******************************************************************************/
2342 
2343 /**
2344  * dialog_doc_metadata_new:
2345  * @wbcg: WBCGtk
2346  *
2347  * Creates a new instance of the dialog.
2348  **/
2349 void
dialog_doc_metadata_new(WBCGtk * wbcg,int page)2350 dialog_doc_metadata_new (WBCGtk *wbcg, int page)
2351 {
2352 	DialogDocMetaData *state;
2353 
2354 	g_return_if_fail (wbcg != NULL);
2355 
2356 	/* Only one guru per workbook. */
2357 	if (wbc_gtk_get_guru (wbcg))
2358 		return;
2359 
2360 	/* Only pop up one copy per workbook */
2361 	if (gnm_dialog_raise_if_exists (wbcg, DOC_METADATA_KEY))
2362 		return;
2363 
2364 	state = g_new0 (DialogDocMetaData, 1);
2365 
2366 	if (dialog_doc_metadata_init (state, wbcg)) {
2367 		go_gtk_notice_dialog (wbcg_toplevel (wbcg),
2368 				      GTK_MESSAGE_ERROR,
2369 				      _("Could not create the Properties dialog."));
2370 
2371 		g_free (state);
2372 		return;
2373 	}
2374 
2375 	dialog_doc_metadata_select_page (state, page);
2376 }
2377