1.12.42
[gnumeric.git] / src / dialogs / dialog-doc-metadata.c
blob06817fc62aefa8cfbe3c12ea82cb4b692ac7d4fd
1 /*
2 * dialog-doc-metadata.c: Edit document metadata
4 * Copyright (C) 2005 Jody Goldberg (jody@gnome.org)
5 * Copyright (C) 2011 Andreas J. Guelzow (aguelzow@pyrshep.ca)
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.
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.
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
22 #include <gnumeric-config.h>
23 #include <gnumeric.h>
24 #include <dialogs/dialogs.h>
25 #include <dialogs/help.h>
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>
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>
45 #include <goffice/goffice.h>
48 #include <glib-object.h>
49 #include <glib/gi18n-lib.h>
50 #include <glib/gprintf.h>
52 #include <string.h>
55 #define DOC_METADATA_KEY "dialog-doc-metadata"
57 enum {
58 ITEM_ICON,
59 ITEM_NAME,
60 PAGE_NUMBER,
61 NUM_COLUMNS
64 typedef struct {
65 GtkBuilder *gui;
66 GtkWidget *dialog;
68 /*pointer to the document metadata*/
69 GsfDocMetaData *metadata;
71 gboolean permissions_changed;
72 GOFilePermissions *file_permissions;
74 WBCGtk *wbcg;
75 Workbook *wb;
76 GODoc *doc;
78 GtkTreeStore *store;
79 GtkTreeView *view;
81 /* Dialog Widgets */
82 GtkNotebook *notebook;
83 GtkButton *help_button;
84 GtkButton *close_button;
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;
95 GtkCheckButton *owner_read;
96 GtkCheckButton *owner_write;
98 GtkCheckButton *group_read;
99 GtkCheckButton *group_write;
101 GtkCheckButton *others_read;
102 GtkCheckButton *others_write;
104 /* Description Page */
105 GtkEntry *title;
106 GtkEntry *subject;
107 GtkEntry *author;
108 GtkEntry *manager;
109 GtkEntry *company;
110 GtkEntry *category;
112 GtkTextView *comments;
114 /* Properties Page */
115 GtkTreeView *properties;
116 GtkTreeStore *properties_store;
118 GtkEntry *ppt_name;
119 GtkEntry *ppt_value;
120 GtkComboBox *ppt_type;
121 GtkListStore *type_store;
122 GtkTreeModelFilter *type_store_filter;
124 GtkButton *add_button;
125 GtkButton *remove_button;
127 GtkLabel *instruction;
128 GtkLabel *warning;
130 /* Keyword Page */
131 GtkTreeView *key_tree_view;
132 GtkListStore *key_store;
133 GtkButton *key_add_button;
134 GtkButton *key_remove_button;
136 /* Statistics Page */
137 GtkLabel *sheets;
138 GtkLabel *cells;
139 GtkLabel *pages;
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;
149 } DialogDocMetaData;
151 #define trim_string(s_) g_strstrip (g_strdup ((s_)))
153 static gchar *dialog_doc_metadata_get_prop_val (DialogDocMetaData *state, char const *prop_name,
154 GValue *prop_value);
156 static gboolean cb_dialog_doc_metadata_ppt_changed (G_GNUC_UNUSED GtkEntry *entry,
157 G_GNUC_UNUSED GdkEventFocus *event,
158 DialogDocMetaData *state);
161 static GType
162 dialog_doc_metadata_get_value_type_from_name (gchar const *name, GType def)
164 /* shared by all instances and never freed */
165 static GHashTable *dialog_doc_metadata_name_to_type = NULL;
166 gpointer res;
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}
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};
215 /*Missing:GSF_META_NAME_THUMBNAIL */
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));
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));
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));
238 res = g_hash_table_lookup (dialog_doc_metadata_name_to_type, name);
240 if (res != NULL)
241 return GPOINTER_TO_INT (res);
242 else
243 return def;
246 static GType
247 dialog_doc_metadata_get_value_type (GValue *value)
249 GType val_type = G_VALUE_TYPE (value);
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;
261 default:
262 /* Check if it is a GsfDocPropVector or GsfTimeStamp */
264 if (VAL_IS_GSF_TIMESTAMP (value))
265 val_type = GSF_TIMESTAMP_TYPE;
266 else if (VAL_IS_GSF_DOCPROP_VECTOR (value))
267 val_type = GSF_DOCPROP_VECTOR_TYPE;
268 else {
269 g_printerr ("GType %s (%i) not handled in metadata dialog.\n",
270 g_type_name (val_type), (int) val_type);
271 val_type = G_TYPE_INVALID;
274 break;
277 return val_type;
281 /******************************************************************************
282 * G_VALUE TRANSFORM FUNCTIONS
283 ******************************************************************************/
286 * G_TYPE_STRING TO OTHER
289 static void
290 dialog_doc_metadata_transform_str_to_timestamp (const GValue *string_value,
291 GValue *timestamp_value)
293 time_t s;
294 gnm_float serial;
295 gint int_serial;
296 GsfTimestamp *gt;
297 gchar const *str;
298 GnmValue *conversion;
299 GOFormat *fmt;
301 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
302 g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
304 fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
305 str = g_value_get_string (string_value);
306 conversion = format_match_number (str, fmt, NULL);
307 go_format_unref (fmt);
308 if (conversion) {
309 serial = value_get_as_float (conversion);
310 value_release (conversion);
312 /* Convert from Gnumeric time to unix time */
313 int_serial = (int)serial;
314 s = go_date_serial_to_timet (int_serial, NULL);
316 if (gnm_abs (serial - int_serial) >= 1.0 || s == (time_t)-1) {
317 s = time (NULL);
318 } else
319 s += (gnm_fake_round (3600 * 24 * (serial - int_serial)));
321 } else
322 s = time (NULL);
324 gt = gsf_timestamp_new ();
325 gsf_timestamp_set_time (gt, s);
326 gsf_timestamp_to_value (gt, timestamp_value);
329 static void
330 dialog_doc_metadata_transform_str_to_float (const GValue *string_value,
331 GValue *float_value)
333 gnm_float x;
334 gchar const *str;
335 GnmValue *conversion;
337 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
338 g_return_if_fail (G_VALUE_HOLDS_FLOAT (float_value));
340 str = g_value_get_string (string_value);
341 conversion = format_match_number (str, NULL, NULL);
342 if (conversion) {
343 x = value_get_as_float (conversion);
344 value_release (conversion);
345 } else
346 x = 0.;
348 g_value_set_float (float_value, x);
351 static void
352 dialog_doc_metadata_transform_str_to_boolean (const GValue *string_value,
353 GValue *b_value)
355 gboolean x, err;
356 gchar const *str;
357 GnmValue *conversion;
359 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
360 g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (b_value));
362 str = g_value_get_string (string_value);
363 conversion = format_match_number (str, NULL, NULL);
364 if (conversion) {
365 x = value_get_as_bool (conversion, &err);
366 value_release (conversion);
367 if (err)
368 x = FALSE;
369 } else
370 x = FALSE;
372 g_value_set_boolean (b_value, x);
375 static void
376 dialog_doc_metadata_transform_str_to_docprop_vect (const GValue *string_value,
377 GValue *docprop_value)
379 char const *str, *s;
380 GsfDocPropVector *gdpv;
381 GValue *value;
383 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
384 g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
386 gdpv = gsf_docprop_vector_new ();
387 str = g_value_get_string (string_value);
389 while (*str == ' ') {str++;}
391 while (*str == '"') {
392 char *key;
393 s = str += 1;
394 while (*s != '"' && *s != '\0') {
395 if (*(s++) == '\\') {
396 if (*s == '\0')
397 goto str_done;
398 s++;
401 if (*s == '\0')
402 goto str_done;
403 /* s == '"' */
404 key = g_strndup (str, s - str);
405 value = g_new0 (GValue, 1);
406 g_value_take_string (g_value_init (value, G_TYPE_STRING),
407 g_strcompress (key));
408 gsf_docprop_vector_append (gdpv, value);
409 g_free (key);
410 str = s + 1;
411 while (*str == ' ') {str++;}
412 if (str[0] != ',')
413 goto str_done;
414 str++;
415 while (*str == ' ') {str++;}
417 str_done:
418 g_value_set_object (docprop_value, gdpv);
419 g_object_unref (gdpv);
422 static char *
423 time2str (time_t t)
425 char buffer[4000];
426 gsize len;
427 char const *format = "%c";
429 if (t == -1)
430 return NULL;
432 len = strftime (buffer, sizeof (buffer), format, localtime (&t));
433 if (len == 0)
434 return NULL;
436 return g_locale_to_utf8 (buffer, len, NULL, NULL, NULL);
439 static char *
440 time2str_go (time_t t)
442 /* We need to create a format that is also parsable */
443 char *str;
444 GOFormat *fmt;
445 gnm_float t_gnm;
447 if (t == -1)
448 return NULL;
450 t_gnm = go_date_timet_to_serial_raw (t, NULL);
452 fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
453 str = go_format_value (fmt, t_gnm);
454 go_format_unref (fmt);
455 return str;
459 * OTHER TO G_TYPE_STRING
461 static void
462 dialog_doc_metadata_transform_timestamp_to_str (const GValue *timestamp_value,
463 GValue *string_value)
465 GsfTimestamp const *timestamp = NULL;
467 g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
468 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
470 timestamp = g_value_get_boxed (timestamp_value);
471 if (timestamp != NULL)
472 g_value_take_string (string_value,
473 time2str_go (timestamp->timet));
476 static void
477 dialog_doc_metadata_transform_float_to_str (const GValue *float_value,
478 GValue *string_value)
480 gnm_float x;
481 char *str;
482 GOFormat *fmt;
484 g_return_if_fail (G_VALUE_HOLDS_FLOAT (float_value));
485 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
487 x = g_value_get_float (float_value);
489 fmt = go_format_general ();
490 str = go_format_value (fmt, x);
491 g_value_take_string (string_value, str);
494 static void
495 dialog_doc_metadata_transform_boolean_to_str (const GValue *b_value,
496 GValue *string_value)
498 gboolean x;
500 g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (b_value));
501 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
503 x = g_value_get_boolean (b_value);
505 g_value_set_static_string (string_value, x ? _("TRUE") : _("FALSE"));
508 static gchar*
509 gnm_docprop_vector_as_string (GsfDocPropVector *vector)
511 GString *rstring;
512 guint i;
513 guint num_values;
514 GValueArray *gva;
515 GValue vl = G_VALUE_INIT;
517 g_return_val_if_fail (vector != NULL, NULL);
519 g_value_set_object (g_value_init (&vl, GSF_DOCPROP_VECTOR_TYPE), vector);
520 gva = gsf_value_get_docprop_varray (&vl);
522 g_return_val_if_fail (gva != NULL, NULL);
524 num_values = gva->n_values;
525 rstring = g_string_sized_new (num_values * 8);
527 for (i = 0; i < num_values; i++) {
528 char *str;
529 GValue *v;
531 v = g_value_array_get_nth (gva, i);
533 if (G_VALUE_TYPE(v) == G_TYPE_STRING)
534 str = g_strescape (g_value_get_string (v), "");
535 else {
536 char *b_str = g_strdup_value_contents (v);
537 str = g_strescape (b_str, "");
538 g_free (b_str);
540 g_string_append_c (rstring, '"');
541 g_string_append (rstring, str);
542 g_string_append (rstring, "\", ");
543 g_free (str);
545 if (rstring->len > 0)
546 g_string_truncate (rstring, rstring->len - 2);
548 g_value_unset (&vl);
550 return g_string_free (rstring, FALSE);
553 static void
554 dialog_doc_metadata_transform_docprop_vect_to_str (const GValue *docprop_value,
555 GValue *string_value)
557 GsfDocPropVector * docprop_vector = NULL;
559 g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
560 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
562 docprop_vector = gsf_value_get_docprop_vector (docprop_value);
564 if (docprop_vector != NULL)
565 g_value_set_string (string_value,
566 gnm_docprop_vector_as_string (docprop_vector));
569 /******************************************************************************
570 * FUNCTIONS RELATED TO 'FILE' PAGE
571 ******************************************************************************/
573 static void
574 cb_dialog_doc_metadata_change_permission (GtkCheckButton *bt,
575 DialogDocMetaData *state)
577 g_return_if_fail (state->file_permissions != NULL);
579 /* Check which button was toggled */
580 if (bt == state->owner_read)
581 state->file_permissions->owner_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
582 else if (bt == state->owner_write)
583 state->file_permissions->owner_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
584 else if (bt == state->group_read)
585 state->file_permissions->group_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
586 else if (bt == state->group_write)
587 state->file_permissions->group_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
588 else if (bt == state->others_read)
589 state->file_permissions->others_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
590 else if (bt == state->others_write)
591 state->file_permissions->others_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
592 else
593 return;
595 state->permissions_changed = TRUE;
598 static void
599 dialog_doc_metadata_set_up_permissions (DialogDocMetaData *state)
601 g_return_if_fail (state->metadata != NULL);
603 state->file_permissions = go_get_file_permissions (
604 go_doc_get_uri (state->doc));
606 if (state->file_permissions != NULL) {
607 /* Set Check buttons */
609 /* Owner */
610 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_read),
611 state->file_permissions->owner_read);
613 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_write),
614 state->file_permissions->owner_write);
616 /* Group */
617 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_read),
618 state->file_permissions->group_read);
620 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_write),
621 state->file_permissions->group_write);
623 /* Others */
624 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_read),
625 state->file_permissions->others_read);
627 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_write),
628 state->file_permissions->others_write);
631 /* At this moment we don't let user change file permissions */
632 gtk_widget_set_sensitive (GTK_WIDGET (state->owner_read), FALSE);
633 gtk_widget_set_sensitive (GTK_WIDGET (state->owner_write), FALSE);
634 gtk_widget_set_sensitive (GTK_WIDGET (state->group_read), FALSE);
635 gtk_widget_set_sensitive (GTK_WIDGET (state->group_write), FALSE);
636 gtk_widget_set_sensitive (GTK_WIDGET (state->others_read), FALSE);
637 gtk_widget_set_sensitive (GTK_WIDGET (state->others_write), FALSE);
640 /* @auto_fill : if TRUE and the text is NULL, try to set the label text with an automatic value. */
641 static void
642 dialog_doc_metadata_set_label (DialogDocMetaData *state,
643 GtkLabel *label,
644 char const *text,
645 gboolean auto_fill)
647 Workbook *wb = state->wb;
648 gchar *str_value = NULL;
650 g_return_if_fail (label != NULL);
652 if (text != NULL)
653 str_value = g_strdup (text);
655 if (str_value == NULL && auto_fill == TRUE) {
656 /* File Name */
657 if (label == state->file_name) {
658 str_value = go_basename_from_uri (go_doc_get_uri (state->doc));
661 /* File Location */
662 else if (label == state->location) {
663 str_value = go_dirname_from_uri (go_doc_get_uri (state->doc), TRUE);
666 /* Date Created */
667 else if (label == state->created) {
668 /* Nothing to do ATM */
671 /* Date Modified */
672 else if (label == state->modified) {
673 str_value = time2str (go_file_get_date_modified (go_doc_get_uri (state->doc)));
676 /* Date Accessed */
677 else if (label == state->accessed) {
678 str_value = time2str (go_file_get_date_accessed (go_doc_get_uri (state->doc)));
681 /* Owner */
682 else if (label == state->owner) {
683 str_value = go_file_get_owner_name (go_doc_get_uri (state->doc));
686 /* Group */
687 else if (label == state->group) {
688 str_value = go_file_get_group_name (go_doc_get_uri (state->doc));
691 /* Number of Sheets */
692 else if (label == state->sheets) {
693 str_value = g_strdup_printf ("%d", workbook_sheet_count (wb));
696 /* Number of cells */
697 else if (label == state->cells) {
698 /* Nothing to do ATM */
701 /* Number of pages */
702 else if (label == state->pages) {
703 /* Nothing to do ATM */
707 if (str_value != NULL) {
708 gtk_label_set_text (label, str_value);
709 g_free (str_value);
710 } else
711 gtk_label_set_text (label, _("Unknown"));
714 static void
715 dialog_doc_metadata_init_file_page (DialogDocMetaData *state)
717 g_return_if_fail (state->metadata != NULL);
719 /* Set up the labels */
720 dialog_doc_metadata_set_label (state, state->file_name, NULL, TRUE);
721 dialog_doc_metadata_set_label (state, state->location, NULL, TRUE);
722 dialog_doc_metadata_set_label (state, state->created, NULL, TRUE);
723 dialog_doc_metadata_set_label (state, state->modified, NULL, TRUE);
724 dialog_doc_metadata_set_label (state, state->accessed, NULL, TRUE);
725 dialog_doc_metadata_set_label (state, state->owner, NULL, TRUE);
726 dialog_doc_metadata_set_label (state, state->group, NULL, TRUE);
728 /* Set up check buttons */
729 state->permissions_changed = FALSE;
730 dialog_doc_metadata_set_up_permissions (state);
732 /* Signals */
734 /* Owner */
735 g_signal_connect (G_OBJECT (state->owner_read),
736 "toggled",
737 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
738 state);
740 g_signal_connect (G_OBJECT (state->owner_write),
741 "toggled",
742 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
743 state);
745 /* Group */
746 g_signal_connect (G_OBJECT (state->group_read),
747 "toggled",
748 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
749 state);
751 g_signal_connect (G_OBJECT (state->group_write),
752 "toggled",
753 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
754 state);
756 /* Others */
757 g_signal_connect (G_OBJECT (state->others_read),
758 "toggled",
759 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
760 state);
762 g_signal_connect (G_OBJECT (state->others_write),
763 "toggled",
764 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
765 state);
769 /******************************************************************************
770 * FUNCTIONS RELATED TO 'DESCRIPTION' PAGE
771 ******************************************************************************/
773 /* @activate_property : if TRUE, sets the tree view row which was added active. */
774 static void
775 dialog_doc_metadata_add_prop (DialogDocMetaData *state,
776 const gchar *name,
777 const gchar *value,
778 const gchar *lnk,
779 GType val_type)
781 gboolean editable = (val_type != G_TYPE_INVALID)
782 && (val_type != GSF_DOCPROP_VECTOR_TYPE);
783 if (value == NULL)
784 value = "";
786 if (lnk == NULL)
787 lnk = "";
789 /* Append new values in tree view */
790 gtk_tree_store_insert_with_values (state->properties_store, NULL, NULL, G_MAXINT,
791 0, name,
792 1, value,
793 2, lnk,
794 3, editable,
795 4, val_type,
796 -1);
799 static GType
800 dialog_doc_metadata_get_gsf_prop_val_type (DialogDocMetaData *state,
801 const gchar *name)
803 GsfDocProp *prop = NULL;
804 GValue *value = NULL;
806 g_return_val_if_fail (state->metadata != NULL, G_TYPE_INVALID);
808 /* First we try the clean way */
809 prop = gsf_doc_meta_data_lookup (state->metadata, name);
811 if (prop != NULL)
812 value = (GValue *) gsf_doc_prop_get_val (prop);
814 if (value != NULL)
815 return dialog_doc_metadata_get_value_type (value);
816 else
817 return dialog_doc_metadata_get_value_type_from_name (name, G_TYPE_STRING);
820 static void
821 dialog_doc_metadata_set_gsf_prop_val (DialogDocMetaData *state,
822 GValue *prop_value,
823 const gchar *str_val)
825 GType t = G_VALUE_TYPE (prop_value);
827 /* The preinstalled transform functions do not handle simple transformations */
828 /* such as from string to double, so we do that ourselves */
829 switch (t) {
830 case G_TYPE_STRING:
831 g_value_set_string (prop_value, g_strdup (str_val));
832 break;
833 case G_TYPE_DOUBLE:
834 case G_TYPE_FLOAT: {
835 GnmParsePos pos;
836 GnmValue *val = NULL;
837 GnmExprTop const *texpr = NULL;
838 parse_pos_init_sheet (&pos, workbook_sheet_by_index (state->wb, 0));
839 parse_text_value_or_expr (&pos, str_val,
840 &val, &texpr);
841 if (val != NULL) {
842 gnm_float fl = value_get_as_float (val);
843 value_release (val);
844 if (t == G_TYPE_DOUBLE)
845 g_value_set_double (prop_value, fl);
846 else
847 g_value_set_float (prop_value, fl);
849 if (texpr)
850 gnm_expr_top_unref (texpr);
851 break;
853 default:
854 if (g_value_type_transformable (t, G_TYPE_STRING)) {
855 GValue string_value = G_VALUE_INIT;
856 g_value_init (&string_value, G_TYPE_STRING);
857 g_value_set_string (&string_value, g_strdup (str_val));
858 g_value_transform (&string_value, prop_value);
859 g_value_unset (&string_value);
860 } else
861 g_printerr (_("Transform function of G_TYPE_STRING to %s is required!\n"),
862 g_type_name (t));
863 break;
868 * dialog_doc_metadata_set_gsf_prop
869 * @state: dialog main struct
870 * @name: property name
871 * @value: property value
872 * @lnk: property linked to
874 * Sets a new value to the property in the GsfDocMetaData struct
877 static GType
878 dialog_doc_metadata_set_gsf_prop (DialogDocMetaData *state,
879 const gchar *name,
880 const gchar *value,
881 const gchar *lnk,
882 GType type)
884 GsfDocProp *existing_prop = NULL;
885 GsfDocProp *doc_prop;
886 GValue *existing_value = NULL;
887 char const *existing_link = NULL;
888 GType val_type;
890 existing_prop = gsf_doc_meta_data_lookup (state->metadata, name);
891 if (existing_prop != NULL) {
892 existing_value = (GValue *) gsf_doc_prop_get_val (existing_prop);
893 existing_link = gsf_doc_prop_get_link (existing_prop);
896 if (lnk != NULL && *lnk == 0)
897 lnk = NULL;
898 if (value != NULL && *value == 0)
899 value = NULL;
900 if ((value == NULL) && (lnk == NULL)) {
901 if ((existing_prop == NULL) ||
902 ((existing_value == NULL) && (existing_link == NULL)))
903 return G_TYPE_INVALID;
904 else {
905 cmd_change_meta_data (GNM_WBC (state->wbcg), NULL,
906 g_slist_prepend (NULL, g_strdup (name)));
907 return G_TYPE_INVALID;
911 if (existing_prop != NULL) {
912 gboolean link_changed;
913 gboolean value_changed = TRUE;
915 if (existing_link!= NULL && *existing_link == 0)
916 existing_link = NULL;
917 if (lnk == existing_link)
918 link_changed = FALSE;
919 else if (lnk == NULL || existing_link == NULL)
920 link_changed = TRUE;
921 else
922 link_changed = (0 != strcmp (lnk, existing_link));
924 if (existing_value == NULL)
925 value_changed = (value != NULL);
926 else if (G_VALUE_HOLDS_STRING (existing_value) && (type == 0 || type == G_TYPE_STRING)) {
927 char const * existing_val_str = g_value_get_string (existing_value);
928 if (existing_val_str != NULL && *existing_val_str == 0)
929 existing_val_str = NULL;
930 if (value == existing_val_str)
931 value_changed = FALSE;
932 else if (value == NULL || existing_val_str == NULL)
933 value_changed = TRUE;
934 else
935 value_changed = (0 != strcmp (value, existing_val_str));
936 if (!link_changed && !value_changed)
937 return G_TYPE_STRING;
942 /* Create a new GsfDocProp */
943 doc_prop = gsf_doc_prop_new (g_strdup (name));
945 if (type == 0)
946 val_type = dialog_doc_metadata_get_gsf_prop_val_type (state, name);
947 else
948 val_type = type;
950 if (val_type != G_TYPE_INVALID) {
951 GValue *doc_prop_value;
953 /* Create a new Value */
954 doc_prop_value = g_new0 (GValue, 1);
956 g_value_init (doc_prop_value, val_type);
957 dialog_doc_metadata_set_gsf_prop_val (state, doc_prop_value, value);
958 gsf_doc_prop_set_val (doc_prop, doc_prop_value);
961 if (lnk != NULL)
962 gsf_doc_prop_set_link (doc_prop, g_strdup (lnk));
964 cmd_change_meta_data (GNM_WBC (state->wbcg),
965 g_slist_prepend (NULL, doc_prop), NULL);
967 return val_type;
971 * dialog_doc_metadata_set_prop
972 * @state: dialog main struct
973 * @prop_name: property name
974 * @prop_value: property value
976 * Tries to update the property value in all the dialog and in the GsfDocMetaData
977 * struct. If the property was not found, creates a new one.
980 static void
981 dialog_doc_metadata_set_prop (DialogDocMetaData *state,
982 const gchar *prop_name,
983 const gchar *prop_value,
984 const gchar *link_value,
985 GType type)
987 GtkTreeIter tree_iter;
988 GValue *value;
989 gboolean ret;
990 gboolean found;
991 GType val_type;
992 GsfDocProp *updated_prop;
993 gchar *new_prop_value = NULL;
995 g_return_if_fail (state->metadata != NULL);
997 val_type = dialog_doc_metadata_set_gsf_prop (state, prop_name, prop_value,
998 link_value, type);
1000 /* Due to changes in type, prop_value may have changed */
1001 updated_prop = gsf_doc_meta_data_lookup (state->metadata, prop_name);
1002 if (updated_prop != NULL) {
1003 GValue *new_value = (GValue *) gsf_doc_prop_get_val (updated_prop);
1004 if (new_value != NULL)
1005 new_prop_value = dialog_doc_metadata_get_prop_val (state, prop_name, new_value);
1006 if (new_prop_value == NULL)
1007 new_prop_value = g_strdup ("");
1010 found = FALSE;
1012 /* Search tree view for property */
1013 value = g_new0 (GValue, 1);
1015 ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->properties_store),
1016 &tree_iter);
1018 while (ret == TRUE) {
1020 gtk_tree_model_get_value (GTK_TREE_MODEL (state->properties_store),
1021 &tree_iter,
1023 value);
1025 if (strcmp (prop_name, g_value_get_string (value)) == 0) {
1026 if (updated_prop != NULL) {
1027 /* Set new value */
1028 gtk_tree_store_set (state->properties_store,
1029 &tree_iter,
1030 1, new_prop_value,
1031 -1);
1033 if (link_value != NULL)
1034 gtk_tree_store_set (state->properties_store,
1035 &tree_iter,
1036 2, link_value,
1037 -1);
1038 } else
1039 /* Clear value */
1040 gtk_tree_store_remove (state->properties_store,
1041 &tree_iter);
1043 g_value_unset (value);
1045 found = TRUE;
1046 break;
1049 g_value_unset (value);
1050 ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (state->properties_store),
1051 &tree_iter);
1054 if (val_type != G_TYPE_INVALID && found == FALSE)
1055 dialog_doc_metadata_add_prop (state, prop_name, new_prop_value, "", val_type);
1057 /* Free all data */
1058 g_free (value);
1059 g_free (new_prop_value );
1063 * CALLBACKS for 'Description' page entries
1065 static gboolean
1066 cb_dialog_doc_metadata_title_changed (GtkEntry *entry,
1067 G_GNUC_UNUSED GdkEventFocus *event,
1068 DialogDocMetaData *state)
1070 dialog_doc_metadata_set_prop (state,
1071 GSF_META_NAME_TITLE,
1072 gtk_entry_get_text (entry),
1073 NULL, G_TYPE_STRING);
1074 return FALSE;
1077 static gboolean
1078 cb_dialog_doc_metadata_subject_changed (GtkEntry *entry,
1079 G_GNUC_UNUSED GdkEventFocus *event,
1080 DialogDocMetaData *state)
1082 dialog_doc_metadata_set_prop (state,
1083 GSF_META_NAME_SUBJECT,
1084 gtk_entry_get_text (entry),
1085 NULL, G_TYPE_STRING);
1086 return FALSE;
1089 static gboolean
1090 cb_dialog_doc_metadata_author_changed (GtkEntry *entry,
1091 G_GNUC_UNUSED GdkEventFocus *event,
1092 DialogDocMetaData *state)
1094 dialog_doc_metadata_set_prop (state,
1095 GSF_META_NAME_INITIAL_CREATOR,
1096 gtk_entry_get_text (entry),
1097 NULL, G_TYPE_STRING);
1098 return FALSE;
1101 static gboolean
1102 cb_dialog_doc_metadata_manager_changed (GtkEntry *entry,
1103 G_GNUC_UNUSED GdkEventFocus *event,
1104 DialogDocMetaData *state)
1106 dialog_doc_metadata_set_prop (state,
1107 GSF_META_NAME_MANAGER,
1108 gtk_entry_get_text (entry),
1109 NULL, G_TYPE_STRING);
1110 return FALSE;
1113 static gboolean
1114 cb_dialog_doc_metadata_company_changed (GtkEntry *entry,
1115 G_GNUC_UNUSED GdkEventFocus *event,
1116 DialogDocMetaData *state)
1118 dialog_doc_metadata_set_prop (state,
1119 GSF_META_NAME_COMPANY,
1120 gtk_entry_get_text (entry),
1121 NULL, G_TYPE_STRING);
1122 return FALSE;
1125 static gboolean
1126 cb_dialog_doc_metadata_category_changed (GtkEntry *entry,
1127 G_GNUC_UNUSED GdkEventFocus *event,
1128 DialogDocMetaData *state)
1130 dialog_doc_metadata_set_prop (state,
1131 GSF_META_NAME_CATEGORY,
1132 gtk_entry_get_text (entry),
1133 NULL, G_TYPE_STRING );
1134 return FALSE;
1137 static gboolean
1138 cb_dialog_doc_metadata_comments_changed (GtkTextView *view,
1139 G_GNUC_UNUSED GdkEventFocus *event,
1140 DialogDocMetaData *state)
1142 GtkTextIter start_iter;
1143 GtkTextIter end_iter;
1144 gchar *text;
1145 GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
1147 gtk_text_buffer_get_start_iter (buffer, &start_iter);
1148 gtk_text_buffer_get_end_iter (buffer, &end_iter);
1150 text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, TRUE);
1152 dialog_doc_metadata_set_prop (state,
1153 GSF_META_NAME_DESCRIPTION,
1154 text,
1155 NULL, G_TYPE_STRING);
1156 return FALSE;
1160 * dialog_doc_metadata_init_description_page
1161 * @state: dialog main struct
1163 * Initializes the widgets and signals for the 'Description' page.
1166 static void
1167 dialog_doc_metadata_init_description_page (DialogDocMetaData *state)
1169 g_return_if_fail (state->metadata != NULL);
1171 /* At this point, the entry values were already filled */
1173 /* Set up the signals */
1174 g_signal_connect (G_OBJECT (state->title),
1175 "focus-out-event",
1176 G_CALLBACK (cb_dialog_doc_metadata_title_changed),
1177 state);
1179 g_signal_connect (G_OBJECT (state->subject),
1180 "focus-out-event",
1181 G_CALLBACK (cb_dialog_doc_metadata_subject_changed),
1182 state);
1184 g_signal_connect (G_OBJECT (state->author),
1185 "focus-out-event",
1186 G_CALLBACK (cb_dialog_doc_metadata_author_changed),
1187 state);
1189 g_signal_connect (G_OBJECT (state->manager),
1190 "focus-out-event",
1191 G_CALLBACK (cb_dialog_doc_metadata_manager_changed),
1192 state);
1194 g_signal_connect (G_OBJECT (state->company),
1195 "focus-out-event",
1196 G_CALLBACK (cb_dialog_doc_metadata_company_changed),
1197 state);
1199 g_signal_connect (G_OBJECT (state->category),
1200 "focus-out-event",
1201 G_CALLBACK (cb_dialog_doc_metadata_category_changed),
1202 state);
1204 g_signal_connect (G_OBJECT (state->comments),
1205 "focus-out-event",
1206 G_CALLBACK (cb_dialog_doc_metadata_comments_changed),
1207 state);
1210 /******************************************************************************
1211 * FUNCTIONS RELATED TO 'KEYWORDS' PAGE
1212 ******************************************************************************/
1214 static void
1215 dialog_doc_metadata_update_keywords_changed (DialogDocMetaData *state)
1217 GValue val = G_VALUE_INIT;
1218 GtkTreeIter iter;
1219 GsfDocPropVector *vector = gsf_docprop_vector_new ();
1221 g_value_init (&val, GSF_DOCPROP_VECTOR_TYPE);
1223 if (gtk_tree_model_get_iter_first
1224 (GTK_TREE_MODEL (state->key_store), &iter)) {
1225 do {
1226 GValue *value = g_new0 (GValue, 1);
1227 gtk_tree_model_get_value
1228 (GTK_TREE_MODEL (state->key_store), &iter,
1229 0, value);
1230 gsf_docprop_vector_append (vector, value);
1231 g_value_unset (value);
1232 g_free (value);
1233 } while (gtk_tree_model_iter_next
1234 (GTK_TREE_MODEL (state->key_store), &iter));
1236 g_value_set_object (&val, vector);
1237 g_object_unref (vector);
1239 dialog_doc_metadata_set_prop
1240 (state, GSF_META_NAME_KEYWORDS,
1241 dialog_doc_metadata_get_prop_val (state, GSF_META_NAME_KEYWORDS, &val),
1242 NULL, GSF_DOCPROP_VECTOR_TYPE);
1244 g_value_unset (&val);
1247 static void
1248 cb_dialog_doc_metadata_keywords_sel_changed (GtkTreeSelection *treeselection,
1249 DialogDocMetaData *state)
1251 gtk_widget_set_sensitive
1252 (GTK_WIDGET (state->key_remove_button),
1253 gtk_tree_selection_get_selected (treeselection, NULL, NULL));
1256 static void
1257 dialog_doc_metadata_update_keyword_list (DialogDocMetaData *state, GsfDocProp *prop)
1259 guint i;
1260 GtkTreeSelection *sel;
1262 gtk_list_store_clear (state->key_store);
1264 if (prop != NULL) {
1265 GValueArray *array;
1266 array = gsf_value_get_docprop_varray (gsf_doc_prop_get_val (prop));
1267 if (array != NULL) {
1268 for (i = 0; i < array->n_values; i++) {
1269 GValue *val = g_value_array_get_nth (array, i);
1270 gtk_list_store_insert_with_values
1271 (state->key_store, NULL, G_MAXINT,
1272 0, g_value_get_string (val), -1);
1277 sel = gtk_tree_view_get_selection (state->key_tree_view);
1278 cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
1281 static void
1282 cb_dialog_doc_metadata_keywords_add_clicked (G_GNUC_UNUSED GtkWidget *w,
1283 DialogDocMetaData *state)
1285 gtk_list_store_insert_with_values (state->key_store, NULL, G_MAXINT,
1286 0, "<?>", -1);
1287 dialog_doc_metadata_update_keywords_changed (state);
1290 static void
1291 cb_dialog_doc_metadata_keywords_remove_clicked (G_GNUC_UNUSED GtkWidget *w,
1292 DialogDocMetaData *state)
1294 GtkTreeIter iter;
1295 GtkTreeSelection *sel = gtk_tree_view_get_selection (state->key_tree_view);
1297 if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
1298 gtk_list_store_remove (state->key_store, &iter);
1299 dialog_doc_metadata_update_keywords_changed (state);
1303 static void
1304 cb_dialog_doc_metadata_keyword_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
1305 gchar *path,
1306 gchar *new_text,
1307 DialogDocMetaData *state)
1309 GtkTreeIter iter;
1310 if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (state->key_store), &iter, path)) {
1311 gtk_list_store_set (state->key_store, &iter, 0, new_text, -1);
1312 dialog_doc_metadata_update_keywords_changed (state);
1318 * dialog_doc_metadata_init_keywords_page
1319 * @state: dialog main struct
1321 * Initializes the widgets and signals for the 'Description' page.
1324 static void
1325 dialog_doc_metadata_init_keywords_page (DialogDocMetaData *state)
1327 GtkTreeViewColumn *column;
1328 GtkCellRenderer *renderer;
1329 GtkTreeSelection *sel;
1331 g_return_if_fail (state->metadata != NULL);
1333 renderer = gtk_cell_renderer_text_new ();
1334 g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
1335 column = gtk_tree_view_column_new_with_attributes (_("Keywords"),
1336 renderer,
1337 "text", 0,
1338 NULL);
1339 gtk_tree_view_insert_column (state->key_tree_view, column, -1);
1341 gtk_widget_set_sensitive (GTK_WIDGET (state->key_add_button), TRUE);
1342 gtk_widget_set_sensitive (GTK_WIDGET (state->key_remove_button), FALSE);
1344 sel = gtk_tree_view_get_selection (state->key_tree_view);
1345 g_signal_connect (G_OBJECT (sel),
1346 "changed",
1347 G_CALLBACK (cb_dialog_doc_metadata_keywords_sel_changed),
1348 state);
1350 g_signal_connect (G_OBJECT (state->key_add_button),
1351 "clicked",
1352 G_CALLBACK (cb_dialog_doc_metadata_keywords_add_clicked),
1353 state);
1354 g_signal_connect (G_OBJECT (state->key_remove_button),
1355 "clicked",
1356 G_CALLBACK (cb_dialog_doc_metadata_keywords_remove_clicked),
1357 state);
1358 g_signal_connect (G_OBJECT (renderer),
1359 "edited",
1360 G_CALLBACK (cb_dialog_doc_metadata_keyword_edited),
1361 state);
1363 cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
1366 /******************************************************************************
1367 * FUNCTIONS RELATED TO 'PROPERTIES' PAGE
1368 ******************************************************************************/
1370 static void
1371 cb_dialog_doc_metadata_value_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
1372 gchar *path,
1373 gchar *new_text,
1374 DialogDocMetaData *state)
1376 GtkTreeIter iter;
1377 if (gtk_tree_model_get_iter_from_string
1378 (GTK_TREE_MODEL (state->properties_store), &iter, path)) {
1379 gchar *prop_name;
1380 gchar *link_value;
1381 GType type;
1383 gtk_tree_model_get (GTK_TREE_MODEL (state->properties_store),
1384 &iter,
1385 0, &prop_name,
1386 2, &link_value,
1387 4, &type,
1388 -1);
1389 dialog_doc_metadata_set_prop (state, prop_name, new_text, link_value, type);
1390 g_free (prop_name);
1391 g_free (link_value);
1396 * cb_dialog_doc_metadata_add_clicked
1397 * @w: widget
1398 * @state: dialog main struct
1400 * Adds a new "empty" property to the tree view.
1403 static void
1404 cb_dialog_doc_metadata_add_clicked (G_GNUC_UNUSED GtkWidget *w,
1405 DialogDocMetaData *state)
1407 const gchar *name = gtk_entry_get_text (state->ppt_name);
1408 const gchar *value = gtk_entry_get_text (state->ppt_value);
1409 gchar *name_trimmed = trim_string (name);
1410 GType t;
1411 GtkTreeIter filter_iter;
1413 if (gtk_combo_box_get_active_iter (state->ppt_type, &filter_iter)) {
1414 GtkTreeIter child_iter;
1415 gtk_tree_model_filter_convert_iter_to_child_iter
1416 (state->type_store_filter, &child_iter, &filter_iter);
1417 gtk_tree_model_get (GTK_TREE_MODEL (state->type_store), &child_iter,
1418 1, &t, -1);
1419 } else
1420 t = dialog_doc_metadata_get_value_type_from_name (name_trimmed, G_TYPE_STRING);
1421 dialog_doc_metadata_set_prop (state, name_trimmed, value, NULL, t);
1423 g_free (name_trimmed);
1425 cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1426 gtk_label_set_text (state->warning, "");
1430 * dialog_doc_metadata_update_prop
1431 * @state: dialog main struct
1432 * @prop_name: property name
1433 * @prop_value: property value
1435 * Updates a label or a entry text with the new value.
1439 static void
1440 dialog_doc_metadata_update_prop (DialogDocMetaData *state,
1441 const gchar *prop_name,
1442 const gchar *prop_value,
1443 GsfDocProp *prop)
1445 /* Labels */
1446 if (strcmp (prop_name, GSF_META_NAME_DATE_CREATED) == 0) {
1447 dialog_doc_metadata_set_label (state,
1448 state->created,
1449 prop_value,
1450 TRUE);
1452 else if (strcmp (prop_name, GSF_META_NAME_DATE_MODIFIED) == 0) {
1453 dialog_doc_metadata_set_label (state,
1454 state->modified,
1455 prop_value,
1456 TRUE);
1458 else if (strcmp (prop_name, GSF_META_NAME_SPREADSHEET_COUNT) == 0) {
1459 dialog_doc_metadata_set_label (state,
1460 state->sheets,
1461 prop_value,
1462 TRUE);
1464 else if (strcmp (prop_name, GSF_META_NAME_CELL_COUNT) == 0) {
1465 dialog_doc_metadata_set_label (state,
1466 state->cells,
1467 prop_value,
1468 TRUE);
1470 else if (strcmp (prop_name, GSF_META_NAME_PAGE_COUNT) == 0) {
1471 dialog_doc_metadata_set_label (state,
1472 state->pages,
1473 prop_value,
1474 TRUE);
1477 /* Entries */
1478 if (prop_value == NULL)
1479 prop_value = "";
1481 if (strcmp (prop_name, GSF_META_NAME_TITLE) == 0) {
1482 gtk_entry_set_text (state->title, prop_value);
1485 else if (strcmp (prop_name, GSF_META_NAME_SUBJECT) == 0) {
1486 gtk_entry_set_text (state->subject, prop_value);
1489 else if (strcmp (prop_name, GSF_META_NAME_INITIAL_CREATOR) == 0) {
1490 gtk_entry_set_text (state->author, prop_value);
1493 else if (strcmp (prop_name, GSF_META_NAME_MANAGER) == 0) {
1494 gtk_entry_set_text (state->manager, prop_value);
1497 else if (strcmp (prop_name, GSF_META_NAME_COMPANY) == 0) {
1498 gtk_entry_set_text (state->company, prop_value);
1501 else if (strcmp (prop_name, GSF_META_NAME_CATEGORY) == 0) {
1502 gtk_entry_set_text (state->category, prop_value);
1505 else if (strcmp (prop_name, GSF_META_NAME_KEYWORDS) == 0) {
1506 dialog_doc_metadata_update_keyword_list (state, prop);
1509 else if (strcmp (prop_name, GSF_META_NAME_DESCRIPTION) == 0) {
1510 gtk_text_buffer_set_text (gtk_text_view_get_buffer (state->comments),
1511 prop_value,
1512 -1);
1517 * cb_dialog_doc_metadata_remove_clicked
1518 * @remove_bt: widget
1519 * @state: dialog main struct
1521 * Removes a property from the tree view and updates all the dialog and
1522 * the GsfDocMetaData struct.
1525 static void
1526 cb_dialog_doc_metadata_remove_clicked (GtkWidget *remove_bt,
1527 DialogDocMetaData *state)
1529 GtkTreeIter tree_iter;
1530 GValue *prop_name;
1531 GtkTreeSelection *sel = gtk_tree_view_get_selection (state->properties);
1533 g_return_if_fail (state->metadata != NULL);
1535 if (gtk_tree_selection_get_selected (sel, NULL, &tree_iter)) {
1537 /* Get the property name */
1538 prop_name = g_new0 (GValue, 1);
1539 gtk_tree_model_get_value (GTK_TREE_MODEL (state->properties_store),
1540 &tree_iter,
1542 prop_name);
1544 /* Update other pages */
1545 dialog_doc_metadata_update_prop (state,
1546 g_value_get_string (prop_name),
1547 NULL, NULL);
1549 /* Remove property from GsfMetadata */
1550 cmd_change_meta_data (GNM_WBC (state->wbcg), NULL,
1551 g_slist_prepend (NULL, g_value_dup_string (prop_name)));
1553 /* Remove from Tree View */
1554 gtk_tree_store_remove (state->properties_store,
1555 &tree_iter);
1557 /* Free all data */
1558 g_value_unset (prop_name);
1559 g_free (prop_name);
1562 /* Set remove button insensitive */
1563 gtk_widget_set_sensitive (remove_bt, FALSE);
1568 * cb_dialog_doc_metadata_tree_prop_selected
1569 * @combo_box: widget
1570 * @state: dialog main struct
1572 * Update the highlited item in the 'Properties' page combo box.
1575 static void
1576 cb_dialog_doc_metadata_tree_prop_selected (GtkTreeSelection *selection,
1577 DialogDocMetaData *state)
1579 GtkTreeIter iter;
1580 gboolean selected;
1581 gchar const *text = "";
1583 g_return_if_fail (state->metadata != NULL);
1585 selected = gtk_tree_selection_get_selected (selection, NULL, &iter);
1587 /* Set remove button sensitive */
1588 gtk_widget_set_sensitive (GTK_WIDGET (state->remove_button), selected);
1590 if (selected) {
1591 GType val_type = G_TYPE_INVALID;
1592 gchar *prop_name = NULL;
1593 gtk_tree_model_get (GTK_TREE_MODEL (state->properties_store),
1594 &iter,
1595 0, &prop_name,
1596 4, &val_type,
1597 -1);
1598 switch (val_type) {
1599 case G_TYPE_STRING:
1600 text = _("Edit string value directly in above listing.");
1601 break;
1602 case G_TYPE_UINT:
1603 text = _("Edit positive integer value directly in above listing.");
1604 break;
1605 case G_TYPE_INT:
1606 text = _("Edit integer value directly in above listing.");
1607 break;
1608 case G_TYPE_FLOAT:
1609 case G_TYPE_DOUBLE:
1610 text = _("Edit decimal number value directly in above listing.");
1611 break;
1612 case G_TYPE_BOOLEAN:
1613 text = _("Edit TRUE/FALSE value directly in above listing.");
1614 break;
1615 default:
1616 if (val_type == GSF_DOCPROP_VECTOR_TYPE) {
1617 if (0 == strcmp (prop_name, "dc:keywords"))
1618 text = _("To edit, use the keywords tab.");
1619 else
1620 text = _("This property value cannot be edited.");
1621 } else if (val_type == GSF_TIMESTAMP_TYPE)
1622 text= _("Edit timestamp directly in above listing.");
1623 break;
1625 g_free (prop_name);
1628 gtk_label_set_text (state->instruction, text);
1632 * dialog_doc_metadata_get_prop_val:
1633 * @state:
1634 * @prop_name: property name
1635 * @prop_value: property value
1637 * Retrieves an arbitrary property value always as string.
1640 static gchar *
1641 dialog_doc_metadata_get_prop_val (G_GNUC_UNUSED DialogDocMetaData *state,
1642 char const *prop_name,
1643 GValue *prop_value)
1645 GValue str_value = G_VALUE_INIT;
1646 gboolean ret = FALSE;
1648 g_return_val_if_fail (prop_value != NULL, NULL);
1650 g_value_init (&str_value, G_TYPE_STRING);
1652 ret = g_value_transform (prop_value, &str_value);
1654 if (ret == FALSE) {
1655 g_warning ("Metadata tag '%s' holds unrecognized value type.", prop_name);
1656 return NULL;
1659 return g_value_dup_string (&str_value);
1663 * dialog_doc_metadata_populate_tree_view
1664 * @name: property name
1665 * @prop: property stored in GsfDocMetaData
1666 * @state: dialog main struct
1668 * Populates the tree view in 'Properties' page.
1671 static void
1672 dialog_doc_metadata_populate_tree_view (gchar *name,
1673 GsfDocProp *prop,
1674 DialogDocMetaData *state)
1676 gchar *str_value;
1677 char *link_value;
1678 GValue *value;
1680 g_return_if_fail (state->metadata != NULL);
1682 value = (GValue *) gsf_doc_prop_get_val (prop);
1683 /* Get the prop value as string */
1684 str_value = dialog_doc_metadata_get_prop_val (state, name, value);
1686 link_value = (char *) gsf_doc_prop_get_link (prop);
1688 dialog_doc_metadata_add_prop
1689 (state,
1690 gsf_doc_prop_get_name (prop),
1691 str_value == NULL ? "" : str_value,
1692 link_value == NULL ? "" : link_value,
1693 dialog_doc_metadata_get_value_type (value));
1695 dialog_doc_metadata_update_prop (state, gsf_doc_prop_get_name (prop), str_value, prop);
1697 g_free (str_value);
1700 static gboolean
1701 dialog_doc_metadata_show_all_types (GtkTreeModel *model,
1702 G_GNUC_UNUSED GtkTreePath *path,
1703 GtkTreeIter *iter,
1704 G_GNUC_UNUSED gpointer data)
1706 gtk_list_store_set (GTK_LIST_STORE (model), iter, 2, TRUE, -1);
1707 return FALSE;
1710 static gboolean
1711 dialog_doc_metadata_show_this_type (GtkTreeModel *model,
1712 G_GNUC_UNUSED GtkTreePath *path,
1713 GtkTreeIter *iter,
1714 gpointer data)
1716 GType t, type = GPOINTER_TO_INT (data);
1718 gtk_tree_model_get (model, iter, 1, &t, -1);
1719 gtk_list_store_set (GTK_LIST_STORE (model), iter, 2, t == type, -1);
1720 return FALSE;
1723 static gboolean
1724 cb_dialog_doc_metadata_ppt_changed (G_GNUC_UNUSED GtkEntry *entry,
1725 G_GNUC_UNUSED GdkEventFocus *event,
1726 DialogDocMetaData *state)
1728 const gchar *name;
1729 const gchar *value;
1730 gchar *name_trimmed;
1731 gboolean enable = FALSE;
1732 gchar *str = NULL;
1733 GsfDocProp *prop = NULL;
1734 GtkTreeIter iter;
1736 name = gtk_entry_get_text (state->ppt_name);
1737 value = gtk_entry_get_text (state->ppt_value);
1738 name_trimmed = trim_string (name);
1740 enable = strlen (name_trimmed) > 0 && strlen (value) > 0;
1742 if (enable)
1743 enable = gtk_combo_box_get_active_iter (state->ppt_type, &iter);
1745 if (enable) {
1746 prop = gsf_doc_meta_data_lookup (state->metadata, name_trimmed);
1747 if (prop != NULL) {
1748 str = g_strdup_printf
1749 (_("A document property with the name \'%s\' already exists."),
1750 name_trimmed);
1751 enable = FALSE;
1754 g_free (name_trimmed);
1755 gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), enable);
1756 gtk_label_set_text (state->warning, str ? str : "");
1757 g_free (str);
1758 return FALSE;
1761 static void
1762 cb_dialog_doc_metadata_ppt_type_changed (G_GNUC_UNUSED GtkComboBox *widget,
1763 DialogDocMetaData *state)
1765 cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1768 static gboolean
1769 cb_dialog_doc_metadata_ppt_name_changed (G_GNUC_UNUSED GtkEntry *entry,
1770 G_GNUC_UNUSED GdkEventFocus *event,
1771 DialogDocMetaData *state)
1773 const gchar *name;
1774 gchar *name_trimmed;
1775 gboolean enable = FALSE;
1776 GtkTreeIter iter;
1777 gchar *str = NULL;
1779 name = gtk_entry_get_text (state->ppt_name);
1780 name_trimmed = trim_string (name);
1782 enable = strlen (name_trimmed) > 0;
1784 if (enable) {
1785 GType t = dialog_doc_metadata_get_value_type_from_name (name_trimmed, G_TYPE_INVALID);
1787 if (t == GSF_DOCPROP_VECTOR_TYPE) {
1788 str = g_strdup_printf
1789 (_("Use the keywords tab to create this property."));
1790 enable = FALSE;
1793 if (t == G_TYPE_INVALID) {
1794 g_signal_handlers_block_by_func(G_OBJECT (state->ppt_type),
1795 cb_dialog_doc_metadata_ppt_type_changed,
1796 state);
1797 gtk_tree_model_foreach (GTK_TREE_MODEL (state->type_store),
1798 dialog_doc_metadata_show_all_types, NULL);
1799 gtk_tree_model_filter_refilter (state->type_store_filter);
1800 g_signal_handlers_unblock_by_func(G_OBJECT (state->ppt_type),
1801 cb_dialog_doc_metadata_ppt_type_changed,
1802 state);
1803 } else {
1804 gtk_combo_box_set_active_iter (state->ppt_type, NULL);
1805 g_signal_handlers_block_by_func(G_OBJECT (state->ppt_type),
1806 cb_dialog_doc_metadata_ppt_type_changed,
1807 state);
1808 gtk_tree_model_foreach (GTK_TREE_MODEL (state->type_store),
1809 dialog_doc_metadata_show_this_type,
1810 GINT_TO_POINTER (t));
1811 gtk_tree_model_filter_refilter (state->type_store_filter);
1812 g_signal_handlers_unblock_by_func(G_OBJECT (state->ppt_type),
1813 cb_dialog_doc_metadata_ppt_type_changed,
1814 state);
1815 if (gtk_tree_model_get_iter_first
1816 (GTK_TREE_MODEL (state->type_store_filter), &iter))
1817 gtk_combo_box_set_active_iter (state->ppt_type, &iter);
1820 g_free (name_trimmed);
1822 if (enable)
1823 return cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1824 else {
1825 gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), FALSE);
1826 gtk_label_set_text (state->warning, str ? str : "");
1827 g_free (str);
1830 return FALSE;
1836 * dialog_doc_metadata_init_properties_page
1837 * @state: dialog main struct
1839 * Initializes the widgets and signals for the 'Properties' page.
1842 static void
1843 dialog_doc_metadata_init_properties_page (DialogDocMetaData *state)
1845 GtkTreeSelection *sel;
1846 GtkCellRenderer *cell;
1847 guint i;
1849 struct types {
1850 char const *type_name;
1851 GType type;
1852 } ppt_types[] = {
1853 {N_("String"), G_TYPE_STRING},
1854 {N_("Integer"), G_TYPE_INT},
1855 {N_("Decimal Number"), G_TYPE_FLOAT},
1856 {N_("TRUE/FALSE"), G_TYPE_BOOLEAN}
1859 g_return_if_fail (state->metadata != NULL);
1860 g_return_if_fail (state->properties != NULL);
1862 /* Set Remove and Apply buttons insensitive */
1863 gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), FALSE);
1864 gtk_widget_set_sensitive (GTK_WIDGET (state->remove_button), FALSE);
1866 /* Intialize Combo Box */
1867 /* gtk_combo_box_set_id_column (state->ppt_type, 0); */
1868 cell = gtk_cell_renderer_text_new();
1869 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(state->ppt_type), cell, TRUE);
1870 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(state->ppt_type), cell, "text", 0, NULL);
1872 for (i = 0; i < G_N_ELEMENTS (ppt_types); i++)
1873 gtk_list_store_insert_with_values (state->type_store, NULL, G_MAXINT,
1874 0, _(ppt_types[i].type_name),
1875 1, ppt_types[i].type,
1876 2, TRUE,
1877 -1);
1878 gtk_list_store_insert_with_values (state->type_store, NULL, G_MAXINT,
1879 0, _("Date & Time"),
1880 1, GSF_TIMESTAMP_TYPE,
1881 2, TRUE,
1882 -1);
1883 gtk_tree_model_filter_set_visible_column (state->type_store_filter, 2);
1884 gtk_tree_model_filter_refilter (state->type_store_filter);
1886 /* Populate Treeview */
1887 state->properties_store = gtk_tree_store_new (5,
1888 G_TYPE_STRING,
1889 G_TYPE_STRING,
1890 G_TYPE_STRING,
1891 G_TYPE_BOOLEAN,
1892 G_TYPE_INT);
1894 gtk_tree_view_set_model (state->properties,
1895 GTK_TREE_MODEL (state->properties_store));
1896 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (state->properties_store),
1897 0, GTK_SORT_ASCENDING);
1898 g_object_unref (state->properties_store);
1900 /* Append Columns */
1901 gtk_tree_view_insert_column_with_attributes (state->properties,
1902 0, _("Name"),
1903 gtk_cell_renderer_text_new(),
1904 "text", 0,
1905 NULL);
1907 cell = gtk_cell_renderer_text_new();
1908 gtk_tree_view_insert_column_with_attributes (state->properties,
1909 1, _("Value"),
1910 cell,
1911 "text", 1,
1912 "editable", 3,
1913 NULL);
1914 g_signal_connect (G_OBJECT (cell),
1915 "edited",
1916 G_CALLBACK (cb_dialog_doc_metadata_value_edited),
1917 state);
1919 gtk_tree_view_insert_column_with_attributes (state->properties,
1920 2, _("Linked To"),
1921 gtk_cell_renderer_text_new(),
1922 "text", 2,
1923 NULL);
1925 /* Read all metadata */
1926 gsf_doc_meta_data_foreach (state->metadata,
1927 (GHFunc) dialog_doc_metadata_populate_tree_view,
1928 state);
1930 /* Set up signals */
1931 /* Tree View */
1932 sel = gtk_tree_view_get_selection (state->properties);
1933 g_signal_connect (G_OBJECT (sel),
1934 "changed",
1935 G_CALLBACK (cb_dialog_doc_metadata_tree_prop_selected),
1936 state);
1938 /* Entries */
1939 g_signal_connect (G_OBJECT (state->ppt_name),
1940 "focus-out-event",
1941 G_CALLBACK (cb_dialog_doc_metadata_ppt_name_changed),
1942 state);
1943 g_signal_connect (G_OBJECT (state->ppt_value),
1944 "focus-out-event",
1945 G_CALLBACK (cb_dialog_doc_metadata_ppt_changed),
1946 state);
1947 /* ComboBox */
1948 g_signal_connect (G_OBJECT (state->ppt_type),
1949 "changed",
1950 G_CALLBACK (cb_dialog_doc_metadata_ppt_type_changed),
1951 state);
1953 /* 'Add', 'Remove' and 'Apply' Button Signals */
1954 g_signal_connect (G_OBJECT (state->add_button),
1955 "clicked",
1956 G_CALLBACK (cb_dialog_doc_metadata_add_clicked),
1957 state);
1959 g_signal_connect (G_OBJECT (state->remove_button),
1960 "clicked",
1961 G_CALLBACK (cb_dialog_doc_metadata_remove_clicked),
1962 state);
1964 cb_dialog_doc_metadata_tree_prop_selected (sel, state);
1965 gtk_combo_box_set_active (state->ppt_type, 0);
1968 /******************************************************************************
1969 * FUNCTIONS RELATED TO 'STATISTICS' PAGE
1970 ******************************************************************************/
1973 * dialog_doc_metadata_init_statistics_page
1974 * @state: dialog main struct
1976 * Initializes the widgets and signals for the 'Statistics' page.
1979 static void
1980 dialog_doc_metadata_init_statistics_page (DialogDocMetaData *state)
1982 g_return_if_fail (state->metadata != NULL);
1984 /* Set up the labels */
1985 dialog_doc_metadata_set_label (state, state->sheets, NULL, TRUE);
1986 dialog_doc_metadata_set_label (state, state->cells, NULL, TRUE);
1987 dialog_doc_metadata_set_label (state, state->pages, NULL, TRUE);
1990 /******************************************************************************
1991 * FUNCTIONS RELATED TO 'CALCULATIONS' PAGE
1992 ******************************************************************************/
1994 static gboolean
1995 cb_dialog_doc_metadata_recalc_max_changed (GtkEntry *entry,
1996 G_GNUC_UNUSED GdkEventFocus *event,
1997 DialogDocMetaData *state)
1999 int val;
2000 if (!entry_to_int (entry, &val, TRUE))
2001 /* FIXME: make undoable */
2002 workbook_iteration_max_number (state->wb, val);
2003 return FALSE;
2006 static gboolean
2007 cb_dialog_doc_metadata_recalc_tolerance_changed (GtkEntry *entry,
2008 G_GNUC_UNUSED GdkEventFocus *event,
2009 DialogDocMetaData *state)
2011 gnm_float val;
2012 if (!entry_to_float (entry, &val, TRUE))
2013 /* FIXME: make undoable */
2014 workbook_iteration_tolerance (state->wb, val);
2015 return FALSE;
2018 static void
2019 cb_dialog_doc_metadata_recalc_auto_changed (GtkWidget *widget, DialogDocMetaData *state)
2021 /* FIXME: make undoable */
2022 workbook_set_recalcmode (state->wb, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
2025 static void
2026 cb_dialog_doc_metadata_recalc_iteration_changed (G_GNUC_UNUSED GtkWidget *widget, DialogDocMetaData *state)
2028 /* FIXME: make undoable */
2029 workbook_iteration_enabled (state->wb, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
2030 gtk_widget_set_sensitive (state->recalc_iteration_grid, state->wb->iteration.enabled);
2034 * dialog_doc_metadata_init_calculations_page
2035 * @state: dialog main struct
2037 * Initializes the widgets and signals for the 'Calculations' page.
2040 static void
2041 dialog_doc_metadata_init_calculations_page (DialogDocMetaData *state)
2043 char *buf;
2045 gtk_toggle_button_set_active
2046 (GTK_TOGGLE_BUTTON ( workbook_get_recalcmode (state->wb) ? state->recalc_auto : state->recalc_manual),
2047 TRUE);
2048 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->recalc_iteration),
2049 state->wb->iteration.enabled);
2050 gtk_widget_set_sensitive (state->recalc_iteration_grid, state->wb->iteration.enabled);
2052 buf = g_strdup_printf ("%d", state->wb->iteration.max_number);
2053 gtk_entry_set_text (state->recalc_max, buf);
2054 g_free (buf);
2055 buf = g_strdup_printf ("%g", state->wb->iteration.tolerance);
2056 gtk_entry_set_text (state->recalc_tolerance, buf);
2057 g_free (buf);
2059 g_signal_connect (G_OBJECT (state->recalc_auto),
2060 "toggled",
2061 G_CALLBACK (cb_dialog_doc_metadata_recalc_auto_changed), state);
2062 g_signal_connect (G_OBJECT (state->recalc_iteration),
2063 "toggled",
2064 G_CALLBACK (cb_dialog_doc_metadata_recalc_iteration_changed), state);
2065 g_signal_connect (G_OBJECT (state->recalc_max),
2066 "focus-out-event",
2067 G_CALLBACK (cb_dialog_doc_metadata_recalc_max_changed),
2068 state);
2069 g_signal_connect (G_OBJECT (state->recalc_tolerance),
2070 "focus-out-event",
2071 G_CALLBACK (cb_dialog_doc_metadata_recalc_tolerance_changed),
2072 state);
2076 /******************************************************************************
2077 * DIALOG INITIALIZE/FINALIZE FUNCTIONS
2078 ******************************************************************************/
2081 * dialog_doc_metadata_set_file_permissions
2082 * @state: dialog main struct
2084 * Writes the new file permissions if there were any changes.
2087 static void
2088 dialog_doc_metadata_set_file_permissions (DialogDocMetaData *state)
2090 if (state->file_permissions != NULL &&
2091 state->permissions_changed == TRUE)
2092 go_set_file_permissions (go_doc_get_uri (state->doc),
2093 state->file_permissions);
2096 static void
2097 dialog_doc_metadata_free (DialogDocMetaData *state)
2099 WorkbookControl *wbc = GNM_WBC (state->wbcg);
2101 wb_view_selection_desc (wb_control_view (wbc), TRUE, wbc);
2103 if (state->gui != NULL) {
2104 dialog_doc_metadata_set_file_permissions (state);
2106 g_object_unref (state->gui);
2107 state->gui = NULL;
2110 g_free (state->file_permissions);
2111 state->file_permissions = NULL;
2113 wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
2115 state->dialog = NULL;
2117 g_free (state);
2120 static void
2121 dialog_doc_metadata_init_widgets (DialogDocMetaData *state)
2123 state->dialog = go_gtk_builder_get_widget (state->gui, "GOMetadataDialog");
2125 state->notebook = GTK_NOTEBOOK (go_gtk_builder_get_widget (state->gui, "notebook"));
2126 state->help_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "help_button"));
2127 state->close_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "close_button"));
2129 /* File Information Page */
2130 state->file_name = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "file_name"));
2131 state->location = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "location"));
2132 state->created = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "created"));
2133 state->modified = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "modified"));
2134 state->accessed = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "accessed"));
2135 state->owner = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "owner"));
2136 state->group = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "group"));
2138 state->owner_read = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "owner_read"));
2139 state->owner_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "owner_write"));
2141 state->group_read = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "group_read"));
2142 state->group_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "group_write"));
2144 state->others_read = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "others_read"));
2145 state->others_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "others_write"));
2147 /* Description Page */
2148 state->title = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "title"));
2149 state->subject = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "subject"));
2150 state->author = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "author"));
2151 state->manager = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "manager"));
2152 state->company = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "company"));
2153 state->category = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "category"));
2155 state->comments = GTK_TEXT_VIEW (go_gtk_builder_get_widget (state->gui, "comments"));
2157 /* Properties Page */
2158 state->properties = GTK_TREE_VIEW (go_gtk_builder_get_widget (state->gui, "properties"));
2160 state->ppt_name = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "property-name"));
2161 state->ppt_value = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "property-value"));
2162 state->ppt_type = GTK_COMBO_BOX (go_gtk_builder_get_widget (state->gui, "type-combo"));
2163 state->type_store = GTK_LIST_STORE (gtk_builder_get_object (state->gui, "typestore"));
2164 state->type_store_filter = GTK_TREE_MODEL_FILTER (gtk_combo_box_get_model (state->ppt_type));
2166 state->add_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "add_button"));
2167 state->remove_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "remove_button"));
2168 state->instruction = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "instruction-label"));
2169 state->warning = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "warning"));
2171 /* Keyword Page */
2172 state->key_tree_view = GTK_TREE_VIEW (go_gtk_builder_get_widget (state->gui, "keyview"));
2173 state->key_store = GTK_LIST_STORE (gtk_tree_view_get_model (state->key_tree_view));
2174 state->key_add_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "key-add-button"));
2175 state->key_remove_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "key-remove-button"));
2177 /* Statistics Page */
2178 state->sheets = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "sheets"));
2179 state->cells = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "cells"));
2180 state->pages = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "pages"));
2182 /* Calculations Page */
2183 state->recalc_auto = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "recalc_auto"));
2184 state->recalc_manual = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "recalc_manual"));
2185 state->recalc_iteration = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "iteration_enabled"));
2186 state->recalc_max = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "max_iterations"));
2187 state->recalc_tolerance = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "iteration_tolerance"));
2188 state->recalc_iteration_grid = go_gtk_builder_get_widget (state->gui, "iteration-grid");
2191 static void
2192 dialog_doc_meta_data_add_item (DialogDocMetaData *state, char const *page_name,
2193 char const *icon_name,
2194 int page, char const* parent_path)
2196 GtkTreeIter iter, parent;
2197 GdkPixbuf * icon = NULL;
2199 if (icon_name != NULL)
2200 icon = gtk_widget_render_icon_pixbuf (state->dialog, icon_name,
2201 GTK_ICON_SIZE_MENU);
2202 if ((parent_path != NULL) && gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (state->store),
2203 &parent, parent_path))
2204 gtk_tree_store_append (state->store, &iter, &parent);
2205 else
2206 gtk_tree_store_append (state->store, &iter, NULL);
2208 gtk_tree_store_set (state->store, &iter,
2209 ITEM_ICON, icon,
2210 ITEM_NAME, _(page_name),
2211 PAGE_NUMBER, page,
2212 -1);
2213 if (icon != NULL)
2214 g_object_unref (icon);
2217 typedef struct {
2218 char const *page_name;
2219 char const *icon_name;
2220 char const *parent_path;
2221 int const page;
2222 void (*page_initializer) (DialogDocMetaData *state);
2223 } page_info_t;
2225 static page_info_t const page_info[] = {
2226 /* IMPORTANT: OBEY THE ORDER 0 - 3 - 2 - 1 */
2227 {N_("File"), GTK_STOCK_FILE, NULL, 0, &dialog_doc_metadata_init_file_page },
2228 {N_("Statistics"), "gnumeric-graphguru", NULL, 3 ,&dialog_doc_metadata_init_statistics_page },
2229 {N_("Properties"), GTK_STOCK_PROPERTIES, NULL, 2, &dialog_doc_metadata_init_properties_page },
2230 {N_("Description"), GTK_STOCK_ABOUT, NULL, 1, &dialog_doc_metadata_init_description_page },
2231 {N_("Keywords"), GTK_STOCK_INDEX, NULL, 5, &dialog_doc_metadata_init_keywords_page },
2232 {N_("Calculation"), GTK_STOCK_EXECUTE, NULL, 4, &dialog_doc_metadata_init_calculations_page },
2233 {NULL, NULL, NULL, -1, NULL},
2236 typedef struct {
2237 int const page;
2238 GtkTreePath *path;
2239 } page_search_t;
2241 static gboolean
2242 dialog_doc_metadata_select_page_search (GtkTreeModel *model,
2243 GtkTreePath *path,
2244 GtkTreeIter *iter,
2245 page_search_t *pst)
2247 int page;
2248 gtk_tree_model_get (model, iter, PAGE_NUMBER, &page, -1);
2249 if (page == pst->page) {
2250 pst->path = gtk_tree_path_copy (path);
2251 return TRUE;
2252 } else
2253 return FALSE;
2256 static void
2257 dialog_doc_metadata_select_page (DialogDocMetaData *state, int page)
2259 page_search_t pst = {page, NULL};
2261 if (page >= 0)
2262 gtk_tree_model_foreach (GTK_TREE_MODEL (state->store),
2263 (GtkTreeModelForeachFunc) dialog_doc_metadata_select_page_search,
2264 &pst);
2266 if (pst.path == NULL)
2267 pst.path = gtk_tree_path_new_from_string ("0");
2269 if (pst.path != NULL) {
2270 gtk_tree_view_set_cursor (state->view, pst.path, NULL, FALSE);
2271 gtk_tree_view_expand_row (state->view, pst.path, TRUE);
2272 gtk_tree_path_free (pst.path);
2276 static void
2277 cb_dialog_doc_metadata_selection_changed (GtkTreeSelection *selection,
2278 DialogDocMetaData *state)
2280 GtkTreeIter iter;
2281 int page;
2283 if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
2284 gtk_tree_model_get (GTK_TREE_MODEL (state->store), &iter,
2285 PAGE_NUMBER, &page,
2286 -1);
2287 gtk_notebook_set_current_page (state->notebook, page);
2288 } else {
2289 dialog_doc_metadata_select_page (state, 0);
2293 static gboolean
2294 dialog_doc_metadata_init (DialogDocMetaData *state,
2295 WBCGtk *wbcg)
2297 GtkTreeViewColumn *column;
2298 GtkTreeSelection *selection;
2299 int i;
2301 state->wbcg = wbcg;
2302 state->wb = wb_control_get_workbook (GNM_WBC(wbcg));
2303 state->doc = GO_DOC (state->wb);
2304 state->metadata = go_doc_get_meta_data (wb_control_get_doc (GNM_WBC (state->wbcg)));
2306 g_return_val_if_fail (state->metadata != NULL, TRUE);
2308 state->gui = gnm_gtk_builder_load ("res:ui/doc-meta-data.ui", NULL,
2309 GO_CMD_CONTEXT (wbcg));
2311 if (state->gui == NULL)
2312 return TRUE;
2314 dialog_doc_metadata_init_widgets (state);
2316 state->view = GTK_TREE_VIEW(go_gtk_builder_get_widget (state->gui, "itemlist"));
2317 state->store = gtk_tree_store_new (NUM_COLUMNS,
2318 GDK_TYPE_PIXBUF,
2319 G_TYPE_STRING,
2320 G_TYPE_INT);
2321 gtk_tree_view_set_model (state->view, GTK_TREE_MODEL(state->store));
2322 g_object_unref (state->store);
2323 selection = gtk_tree_view_get_selection (state->view);
2324 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2325 column = gtk_tree_view_column_new_with_attributes ("",
2326 gtk_cell_renderer_pixbuf_new (),
2327 "pixbuf", ITEM_ICON,
2328 NULL);
2329 gtk_tree_view_append_column (state->view, column);
2330 column = gtk_tree_view_column_new_with_attributes ("",
2331 gtk_cell_renderer_text_new (),
2332 "text", ITEM_NAME,
2333 NULL);
2334 gtk_tree_view_append_column (state->view, column);
2335 gtk_tree_view_set_expander_column (state->view, column);
2337 g_signal_connect (selection,
2338 "changed",
2339 G_CALLBACK (cb_dialog_doc_metadata_selection_changed), state);
2342 /* Register g_value_transform functions */
2343 g_value_register_transform_func (G_TYPE_STRING,
2344 GSF_TIMESTAMP_TYPE,
2345 dialog_doc_metadata_transform_str_to_timestamp);
2347 g_value_register_transform_func (G_TYPE_STRING,
2348 G_TYPE_FLOAT,
2349 dialog_doc_metadata_transform_str_to_float);
2351 g_value_register_transform_func (G_TYPE_STRING,
2352 GSF_DOCPROP_VECTOR_TYPE,
2353 dialog_doc_metadata_transform_str_to_docprop_vect);
2355 g_value_register_transform_func (G_TYPE_STRING,
2356 G_TYPE_BOOLEAN,
2357 dialog_doc_metadata_transform_str_to_boolean);
2359 g_value_register_transform_func (GSF_TIMESTAMP_TYPE,
2360 G_TYPE_STRING,
2361 dialog_doc_metadata_transform_timestamp_to_str);
2363 g_value_register_transform_func (GSF_DOCPROP_VECTOR_TYPE,
2364 G_TYPE_STRING,
2365 dialog_doc_metadata_transform_docprop_vect_to_str);
2367 g_value_register_transform_func (G_TYPE_FLOAT,
2368 G_TYPE_STRING,
2369 dialog_doc_metadata_transform_float_to_str);
2371 g_value_register_transform_func (G_TYPE_BOOLEAN,
2372 G_TYPE_STRING,
2373 dialog_doc_metadata_transform_boolean_to_str);
2375 for (i = 0; page_info[i].page > -1; i++) {
2376 const page_info_t *this_page = &page_info[i];
2377 this_page->page_initializer (state);
2378 dialog_doc_meta_data_add_item (state, this_page->page_name, this_page->icon_name,
2379 this_page->page, this_page->parent_path);
2382 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (state->store), ITEM_NAME, GTK_SORT_ASCENDING);
2384 /* A candidate for merging into attach guru */
2385 gnm_keyed_dialog (state->wbcg,
2386 GTK_WINDOW (state->dialog),
2387 DOC_METADATA_KEY);
2390 go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
2391 GTK_WINDOW (state->dialog));
2393 wbc_gtk_attach_guru (state->wbcg, state->dialog);
2394 g_object_set_data_full (G_OBJECT (state->dialog), "state",
2395 state, (GDestroyNotify) dialog_doc_metadata_free);
2397 /* Help and Close buttons */
2398 gnm_init_help_button (GTK_WIDGET (state->help_button),
2399 GNUMERIC_HELP_LINK_METADATA);
2401 g_signal_connect_swapped (G_OBJECT (state->close_button),
2402 "clicked",
2403 G_CALLBACK (gtk_widget_destroy),
2404 state->dialog);
2406 gtk_widget_show_all (GTK_WIDGET (state->dialog));
2408 return FALSE;
2411 /******************************************************************************
2412 * EXPORTED FUNCTIONS
2413 ******************************************************************************/
2416 * dialog_doc_metadata_new:
2417 * @wbcg: WBCGtk
2419 * Creates a new instance of the dialog.
2421 void
2422 dialog_doc_metadata_new (WBCGtk *wbcg, int page)
2424 DialogDocMetaData *state;
2426 g_return_if_fail (wbcg != NULL);
2428 /* Only one guru per workbook. */
2429 if (wbc_gtk_get_guru (wbcg))
2430 return;
2432 /* Only pop up one copy per workbook */
2433 if (gnm_dialog_raise_if_exists (wbcg, DOC_METADATA_KEY))
2434 return;
2436 state = g_new0 (DialogDocMetaData, 1);
2438 if (dialog_doc_metadata_init (state, wbcg)) {
2439 go_gtk_notice_dialog (wbcg_toplevel (wbcg),
2440 GTK_MESSAGE_ERROR,
2441 _("Could not create the Properties dialog."));
2443 g_free (state);
2444 return;
2447 dialog_doc_metadata_select_page (state, page);