GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / dialogs / dialog-doc-metadata.c
blob699f886c54cfe9f0143db7957a1ad1bdb86ed182
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * dialog-doc-metadata.c: Edit document metadata
5 * Copyright (C) 2005 Jody Goldberg (jody@gnome.org)
6 * Copyright (C) 2011 Andreas J. Guelzow (aguelzow@pyrshep.ca)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
23 #include <gnumeric-config.h>
24 #include <gnumeric.h>
25 #include "dialogs.h"
26 #include "help.h"
28 #include <workbook.h>
29 #include <workbook-control.h>
30 #include <wbc-gtk.h>
31 #include <workbook-view.h>
32 #include <workbook-priv.h>
33 #include <gui-util.h>
34 #include <parse-util.h>
35 #include <value.h>
36 #include <expr.h>
37 #include <commands.h>
38 #include <number-match.h>
39 #include <dead-kittens.h>
41 #include <gsf/gsf-doc-meta-data.h>
42 #include <gsf/gsf-meta-names.h>
43 #include <gsf/gsf-timestamp.h>
44 #include <gsf/gsf-docprop-vector.h>
46 #include <goffice/goffice.h>
48 #include <gtk/gtk.h>
50 #include <glib-object.h>
51 #include <glib/gi18n-lib.h>
52 #include <glib/gprintf.h>
54 #include <string.h>
57 #define DOC_METADATA_KEY "dialog-doc-metadata"
59 enum {
60 ITEM_ICON,
61 ITEM_NAME,
62 PAGE_NUMBER,
63 NUM_COLUMNS
66 typedef struct {
67 GtkBuilder *gui;
68 GtkWidget *dialog;
70 /*pointer to the document metadata*/
71 GsfDocMetaData *metadata;
73 gboolean permissions_changed;
74 GOFilePermissions *file_permissions;
76 WBCGtk *wbcg;
77 Workbook *wb;
78 GODoc *doc;
80 GtkTreeStore *store;
81 GtkTreeView *view;
83 /* Dialog Widgets */
84 GtkNotebook *notebook;
85 GtkButton *help_button;
86 GtkButton *close_button;
88 /* File Information Page */
89 GtkLabel *file_name;
90 GtkLabel *location;
91 GtkLabel *created;
92 GtkLabel *modified;
93 GtkLabel *accessed;
94 GtkLabel *owner;
95 GtkLabel *group;
97 GtkCheckButton *owner_read;
98 GtkCheckButton *owner_write;
100 GtkCheckButton *group_read;
101 GtkCheckButton *group_write;
103 GtkCheckButton *others_read;
104 GtkCheckButton *others_write;
106 /* Description Page */
107 GtkEntry *title;
108 GtkEntry *subject;
109 GtkEntry *author;
110 GtkEntry *manager;
111 GtkEntry *company;
112 GtkEntry *category;
114 GtkTextView *comments;
116 /* Properties Page */
117 GtkTreeView *properties;
118 GtkTreeStore *properties_store;
120 GtkEntry *ppt_name;
121 GtkEntry *ppt_value;
122 GtkComboBox *ppt_type;
123 GtkListStore *type_store;
124 GtkTreeModelFilter *type_store_filter;
126 GtkButton *add_button;
127 GtkButton *remove_button;
129 GtkLabel *instruction;
130 GtkLabel *warning;
132 /* Keyword Page */
133 GtkTreeView *key_tree_view;
134 GtkListStore *key_store;
135 GtkButton *key_add_button;
136 GtkButton *key_remove_button;
138 /* Statistics Page */
139 GtkLabel *sheets;
140 GtkLabel *cells;
141 GtkLabel *pages;
143 /* Calculation Page */
144 GtkCheckButton *recalc_auto;
145 GtkCheckButton *recalc_manual;
146 GtkCheckButton *recalc_iteration;
147 GtkEntry *recalc_max;
148 GtkEntry *recalc_tolerance;
149 GtkWidget *recalc_iteration_grid;
151 } DialogDocMetaData;
153 #define trim_string(s_) g_strstrip (g_strdup ((s_)))
155 static gchar *dialog_doc_metadata_get_prop_val (DialogDocMetaData *state, char const *prop_name,
156 GValue *prop_value);
158 static gboolean cb_dialog_doc_metadata_ppt_changed (G_GNUC_UNUSED GtkEntry *entry,
159 G_GNUC_UNUSED GdkEventFocus *event,
160 DialogDocMetaData *state);
163 static GType
164 dialog_doc_metadata_get_value_type_from_name (gchar const *name, GType def)
166 /* shared by all instances and never freed */
167 static GHashTable *dialog_doc_metadata_name_to_type = NULL;
168 gpointer res;
170 if (NULL == dialog_doc_metadata_name_to_type) {
171 static struct {
172 char const *name;
173 GType type;
174 } const map [] = {
175 {GSF_META_NAME_GENERATOR, G_TYPE_STRING},
176 {GSF_META_NAME_INITIAL_CREATOR, G_TYPE_STRING},
177 {GSF_META_NAME_CREATOR, G_TYPE_STRING},
178 {GSF_META_NAME_TITLE, G_TYPE_STRING},
179 {GSF_META_NAME_SUBJECT, G_TYPE_STRING},
180 {GSF_META_NAME_MANAGER, G_TYPE_STRING},
181 {GSF_META_NAME_COMPANY, G_TYPE_STRING},
182 {GSF_META_NAME_CATEGORY, G_TYPE_STRING},
183 {GSF_META_NAME_DESCRIPTION, G_TYPE_STRING},
184 {GSF_META_NAME_LAST_SAVED_BY, G_TYPE_STRING},
185 {GSF_META_NAME_TEMPLATE, G_TYPE_STRING},
186 {GSF_META_NAME_EDITING_DURATION, G_TYPE_STRING}, /* special */
187 {GSF_META_NAME_SPREADSHEET_COUNT, G_TYPE_INT},
188 {GSF_META_NAME_TABLE_COUNT, G_TYPE_INT},
189 {GSF_META_NAME_CELL_COUNT, G_TYPE_INT},
190 {GSF_META_NAME_CHARACTER_COUNT, G_TYPE_INT},
191 {GSF_META_NAME_BYTE_COUNT, G_TYPE_INT},
192 {GSF_META_NAME_SECURITY, G_TYPE_INT},
193 {GSF_META_NAME_HIDDEN_SLIDE_COUNT, G_TYPE_INT},
194 {GSF_META_NAME_LINE_COUNT, G_TYPE_INT},
195 {GSF_META_NAME_SLIDE_COUNT, G_TYPE_INT},
196 {GSF_META_NAME_WORD_COUNT, G_TYPE_INT},
197 {GSF_META_NAME_MM_CLIP_COUNT, G_TYPE_INT},
198 {GSF_META_NAME_NOTE_COUNT, G_TYPE_INT},
199 {GSF_META_NAME_PARAGRAPH_COUNT, G_TYPE_INT},
200 {GSF_META_NAME_PAGE_COUNT, G_TYPE_INT},
201 {GSF_META_NAME_CODEPAGE, G_TYPE_INT},
202 {GSF_META_NAME_LOCALE_SYSTEM_DEFAULT,G_TYPE_INT},
203 {GSF_META_NAME_OBJECT_COUNT, G_TYPE_INT},
204 {"xlsx:HyperlinksChanged", G_TYPE_BOOLEAN},
205 {GSF_META_NAME_LINKS_DIRTY, G_TYPE_BOOLEAN},
206 {"xlsx:SharedDoc", G_TYPE_BOOLEAN},
207 {GSF_META_NAME_SCALE, G_TYPE_BOOLEAN}
209 static char const *map_vector[] =
210 {GSF_META_NAME_KEYWORDS,
211 GSF_META_NAME_DOCUMENT_PARTS,
212 GSF_META_NAME_HEADING_PAIRS};
213 static char const *map_timestamps[] =
214 {GSF_META_NAME_DATE_CREATED,
215 GSF_META_NAME_DATE_MODIFIED};
217 /*Missing:GSF_META_NAME_THUMBNAIL */
219 int i = G_N_ELEMENTS (map);
220 dialog_doc_metadata_name_to_type = g_hash_table_new (g_str_hash, g_str_equal);
221 while (i-- > 0)
222 g_hash_table_insert (dialog_doc_metadata_name_to_type,
223 (gpointer)map[i].name,
224 GINT_TO_POINTER (map[i].type));
226 i = G_N_ELEMENTS (map_vector);
227 while (i-- > 0)
228 g_hash_table_insert (dialog_doc_metadata_name_to_type,
229 (gpointer)map_vector[i],
230 GINT_TO_POINTER (GSF_DOCPROP_VECTOR_TYPE));
232 i = G_N_ELEMENTS (map_timestamps);
233 while (i-- > 0)
234 g_hash_table_insert (dialog_doc_metadata_name_to_type,
235 (gpointer)map_timestamps[i],
236 GINT_TO_POINTER (GSF_TIMESTAMP_TYPE));
240 res = g_hash_table_lookup (dialog_doc_metadata_name_to_type, name);
242 if (res != NULL)
243 return GPOINTER_TO_INT (res);
244 else
245 return def;
248 static GType
249 dialog_doc_metadata_get_value_type (GValue *value)
251 GType val_type = G_VALUE_TYPE (value);
253 switch (val_type) {
254 case G_TYPE_INT:
255 case G_TYPE_UINT:
256 case G_TYPE_FLOAT:
257 case G_TYPE_DOUBLE:
258 case G_TYPE_STRING:
259 case G_TYPE_BOOLEAN:
260 /* Just leave it as is */
261 break;
263 default:
264 /* Check if it is a GsfDocPropVector or GsfTimeStamp */
266 if (VAL_IS_GSF_TIMESTAMP (value))
267 val_type = GSF_TIMESTAMP_TYPE;
268 else if (VAL_IS_GSF_DOCPROP_VECTOR (value))
269 val_type = GSF_DOCPROP_VECTOR_TYPE;
270 else {
271 g_printerr ("GType %s (%i) not handled in metadata dialog.\n",
272 g_type_name (val_type), (int) val_type);
273 val_type = G_TYPE_INVALID;
276 break;
279 return val_type;
283 /******************************************************************************
284 * G_VALUE TRANSFORM FUNCTIONS
285 ******************************************************************************/
288 * G_TYPE_STRING TO OTHER
291 static void
292 dialog_doc_metadata_transform_str_to_timestamp (const GValue *string_value,
293 GValue *timestamp_value)
295 time_t s;
296 gnm_float serial;
297 gint int_serial;
298 GsfTimestamp *gt;
299 gchar const *str;
300 GnmValue *conversion;
301 GOFormat *fmt;
303 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
304 g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
306 fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
307 str = g_value_get_string (string_value);
308 conversion = format_match_number (str, fmt, NULL);
309 go_format_unref (fmt);
310 if (conversion) {
311 serial = value_get_as_float (conversion);
312 value_release (conversion);
314 /* Convert from Gnumeric time to unix time */
315 int_serial = (int)serial;
316 s = go_date_serial_to_timet (int_serial, NULL);
318 if (gnm_abs (serial - int_serial) >= 1.0 || s == (time_t)-1) {
319 s = time (NULL);
320 } else
321 s += (gnm_fake_round (3600 * 24 * (serial - int_serial)));
323 } else
324 s = time (NULL);
326 gt = gsf_timestamp_new ();
327 gsf_timestamp_set_time (gt, s);
328 gsf_timestamp_to_value (gt, timestamp_value);
331 static void
332 dialog_doc_metadata_transform_str_to_float (const GValue *string_value,
333 GValue *float_value)
335 gnm_float x;
336 gchar const *str;
337 GnmValue *conversion;
339 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
340 g_return_if_fail (G_VALUE_HOLDS_FLOAT (float_value));
342 str = g_value_get_string (string_value);
343 conversion = format_match_number (str, NULL, NULL);
344 if (conversion) {
345 x = value_get_as_float (conversion);
346 value_release (conversion);
347 } else
348 x = 0.;
350 g_value_set_float (float_value, x);
353 static void
354 dialog_doc_metadata_transform_str_to_boolean (const GValue *string_value,
355 GValue *b_value)
357 gboolean x, err;
358 gchar const *str;
359 GnmValue *conversion;
361 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
362 g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (b_value));
364 str = g_value_get_string (string_value);
365 conversion = format_match_number (str, NULL, NULL);
366 if (conversion) {
367 x = value_get_as_bool (conversion, &err);
368 value_release (conversion);
369 if (err)
370 x = FALSE;
371 } else
372 x = FALSE;
374 g_value_set_boolean (b_value, x);
377 static void
378 dialog_doc_metadata_transform_str_to_docprop_vect (const GValue *string_value,
379 GValue *docprop_value)
381 char const *str, *s;
382 GsfDocPropVector *gdpv;
383 GValue *value;
385 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
386 g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
388 gdpv = gsf_docprop_vector_new ();
389 str = g_value_get_string (string_value);
391 while (*str == ' ') {str++;}
393 while (*str == '"') {
394 char *key;
395 s = str += 1;
396 while (*s != '"' && *s != '\0') {
397 if (*(s++) == '\\') {
398 if (*s == '\0')
399 goto str_done;
400 s++;
403 if (*s == '\0')
404 goto str_done;
405 /* s == '"' */
406 key = g_strndup (str, s - str);
407 value = g_new0 (GValue, 1);
408 g_value_take_string (g_value_init (value, G_TYPE_STRING),
409 g_strcompress (key));
410 gsf_docprop_vector_append (gdpv, value);
411 g_free (key);
412 str = s + 1;
413 while (*str == ' ') {str++;}
414 if (str[0] != ',')
415 goto str_done;
416 str++;
417 while (*str == ' ') {str++;}
419 str_done:
420 g_value_set_object (docprop_value, gdpv);
421 g_object_unref (gdpv);
424 static char *
425 time2str (time_t t)
427 char buffer[4000];
428 gsize len;
429 char const *format = "%c";
431 if (t == -1)
432 return NULL;
434 len = strftime (buffer, sizeof (buffer), format, localtime (&t));
435 if (len == 0)
436 return NULL;
438 return g_locale_to_utf8 (buffer, len, NULL, NULL, NULL);
441 static char *
442 time2str_go (time_t t)
444 /* We need to create a format that is also parsable */
445 char *str;
446 GOFormat *fmt;
447 gnm_float t_gnm;
449 if (t == -1)
450 return NULL;
452 t_gnm = go_date_timet_to_serial_raw (t, NULL);
454 fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
455 str = go_format_value (fmt, t_gnm);
456 go_format_unref (fmt);
457 return str;
461 * OTHER TO G_TYPE_STRING
463 static void
464 dialog_doc_metadata_transform_timestamp_to_str (const GValue *timestamp_value,
465 GValue *string_value)
467 GsfTimestamp const *timestamp = NULL;
469 g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
470 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
472 timestamp = g_value_get_boxed (timestamp_value);
473 if (timestamp != NULL)
474 g_value_take_string (string_value,
475 time2str_go (timestamp->timet));
478 static void
479 dialog_doc_metadata_transform_float_to_str (const GValue *float_value,
480 GValue *string_value)
482 gnm_float x;
483 char *str;
484 GOFormat *fmt;
486 g_return_if_fail (G_VALUE_HOLDS_FLOAT (float_value));
487 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
489 x = g_value_get_float (float_value);
491 fmt = go_format_general ();
492 str = go_format_value (fmt, x);
493 g_value_take_string (string_value, str);
496 static void
497 dialog_doc_metadata_transform_boolean_to_str (const GValue *b_value,
498 GValue *string_value)
500 gboolean x;
502 g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (b_value));
503 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
505 x = g_value_get_boolean (b_value);
507 g_value_set_static_string (string_value, x ? _("TRUE") : _("FALSE"));
510 static gchar*
511 gnm_docprop_vector_as_string (GsfDocPropVector *vector)
513 GString *rstring;
514 guint i;
515 guint num_values;
516 GValueArray *gva;
517 GValue vl = G_VALUE_INIT;
519 g_return_val_if_fail (vector != NULL, NULL);
521 g_value_set_object (g_value_init (&vl, GSF_DOCPROP_VECTOR_TYPE), vector);
522 gva = gsf_value_get_docprop_varray (&vl);
524 g_return_val_if_fail (gva != NULL, NULL);
526 num_values = gva->n_values;
527 rstring = g_string_sized_new (num_values * 8);
529 for (i = 0; i < num_values; i++) {
530 char *str;
531 GValue *v;
533 v = g_value_array_get_nth (gva, i);
535 if (G_VALUE_TYPE(v) == G_TYPE_STRING)
536 str = g_strescape (g_value_get_string (v), "");
537 else {
538 char *b_str = g_strdup_value_contents (v);
539 str = g_strescape (b_str, "");
540 g_free (b_str);
542 g_string_append_c (rstring, '"');
543 g_string_append (rstring, str);
544 g_string_append (rstring, "\", ");
545 g_free (str);
547 if (rstring->len > 0)
548 g_string_truncate (rstring, rstring->len - 2);
550 g_value_unset (&vl);
552 return g_string_free (rstring, FALSE);
555 static void
556 dialog_doc_metadata_transform_docprop_vect_to_str (const GValue *docprop_value,
557 GValue *string_value)
559 GsfDocPropVector * docprop_vector = NULL;
561 g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
562 g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
564 docprop_vector = gsf_value_get_docprop_vector (docprop_value);
566 if (docprop_vector != NULL)
567 g_value_set_string (string_value,
568 gnm_docprop_vector_as_string (docprop_vector));
571 /******************************************************************************
572 * FUNCTIONS RELATED TO 'FILE' PAGE
573 ******************************************************************************/
575 static void
576 cb_dialog_doc_metadata_change_permission (GtkCheckButton *bt,
577 DialogDocMetaData *state)
579 g_return_if_fail (state->file_permissions != NULL);
581 /* Check which button was toggled */
582 if (bt == state->owner_read)
583 state->file_permissions->owner_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
584 else if (bt == state->owner_write)
585 state->file_permissions->owner_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
586 else if (bt == state->group_read)
587 state->file_permissions->group_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
588 else if (bt == state->group_write)
589 state->file_permissions->group_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
590 else if (bt == state->others_read)
591 state->file_permissions->others_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
592 else if (bt == state->others_write)
593 state->file_permissions->others_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
594 else
595 return;
597 state->permissions_changed = TRUE;
600 static void
601 dialog_doc_metadata_set_up_permissions (DialogDocMetaData *state)
603 g_return_if_fail (state->metadata != NULL);
605 state->file_permissions = go_get_file_permissions (
606 go_doc_get_uri (state->doc));
608 if (state->file_permissions != NULL) {
609 /* Set Check buttons */
611 /* Owner */
612 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_read),
613 state->file_permissions->owner_read);
615 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_write),
616 state->file_permissions->owner_write);
618 /* Group */
619 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_read),
620 state->file_permissions->group_read);
622 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_write),
623 state->file_permissions->group_write);
625 /* Others */
626 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_read),
627 state->file_permissions->others_read);
629 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_write),
630 state->file_permissions->others_write);
633 /* At this moment we don't let user change file permissions */
634 gtk_widget_set_sensitive (GTK_WIDGET (state->owner_read), FALSE);
635 gtk_widget_set_sensitive (GTK_WIDGET (state->owner_write), FALSE);
636 gtk_widget_set_sensitive (GTK_WIDGET (state->group_read), FALSE);
637 gtk_widget_set_sensitive (GTK_WIDGET (state->group_write), FALSE);
638 gtk_widget_set_sensitive (GTK_WIDGET (state->others_read), FALSE);
639 gtk_widget_set_sensitive (GTK_WIDGET (state->others_write), FALSE);
642 /* @auto_fill : if TRUE and the text is NULL, try to set the label text with an automatic value. */
643 static void
644 dialog_doc_metadata_set_label (DialogDocMetaData *state,
645 GtkLabel *label,
646 char const *text,
647 gboolean auto_fill)
649 Workbook *wb = state->wb;
650 gchar *str_value = NULL;
652 g_return_if_fail (label != NULL);
654 if (text != NULL)
655 str_value = g_strdup (text);
657 if (str_value == NULL && auto_fill == TRUE) {
658 /* File Name */
659 if (label == state->file_name) {
660 str_value = go_basename_from_uri (go_doc_get_uri (state->doc));
663 /* File Location */
664 else if (label == state->location) {
665 str_value = go_dirname_from_uri (go_doc_get_uri (state->doc), TRUE);
668 /* Date Created */
669 else if (label == state->created) {
670 /* Nothing to do ATM */
673 /* Date Modified */
674 else if (label == state->modified) {
675 str_value = time2str (go_file_get_date_modified (go_doc_get_uri (state->doc)));
678 /* Date Accessed */
679 else if (label == state->accessed) {
680 str_value = time2str (go_file_get_date_accessed (go_doc_get_uri (state->doc)));
683 /* Owner */
684 else if (label == state->owner) {
685 str_value = go_file_get_owner_name (go_doc_get_uri (state->doc));
688 /* Group */
689 else if (label == state->group) {
690 str_value = go_file_get_group_name (go_doc_get_uri (state->doc));
693 /* Number of Sheets */
694 else if (label == state->sheets) {
695 str_value = g_strdup_printf ("%d", workbook_sheet_count (wb));
698 /* Number of cells */
699 else if (label == state->cells) {
700 /* Nothing to do ATM */
703 /* Number of pages */
704 else if (label == state->pages) {
705 /* Nothing to do ATM */
709 if (str_value != NULL) {
710 gtk_label_set_text (label, str_value);
711 g_free (str_value);
712 } else
713 gtk_label_set_text (label, _("Unknown"));
716 static void
717 dialog_doc_metadata_init_file_page (DialogDocMetaData *state)
719 g_return_if_fail (state->metadata != NULL);
721 /* Set up the labels */
722 dialog_doc_metadata_set_label (state, state->file_name, NULL, TRUE);
723 dialog_doc_metadata_set_label (state, state->location, NULL, TRUE);
724 dialog_doc_metadata_set_label (state, state->created, NULL, TRUE);
725 dialog_doc_metadata_set_label (state, state->modified, NULL, TRUE);
726 dialog_doc_metadata_set_label (state, state->accessed, NULL, TRUE);
727 dialog_doc_metadata_set_label (state, state->owner, NULL, TRUE);
728 dialog_doc_metadata_set_label (state, state->group, NULL, TRUE);
730 /* Set up check buttons */
731 state->permissions_changed = FALSE;
732 dialog_doc_metadata_set_up_permissions (state);
734 /* Signals */
736 /* Owner */
737 g_signal_connect (G_OBJECT (state->owner_read),
738 "toggled",
739 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
740 state);
742 g_signal_connect (G_OBJECT (state->owner_write),
743 "toggled",
744 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
745 state);
747 /* Group */
748 g_signal_connect (G_OBJECT (state->group_read),
749 "toggled",
750 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
751 state);
753 g_signal_connect (G_OBJECT (state->group_write),
754 "toggled",
755 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
756 state);
758 /* Others */
759 g_signal_connect (G_OBJECT (state->others_read),
760 "toggled",
761 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
762 state);
764 g_signal_connect (G_OBJECT (state->others_write),
765 "toggled",
766 G_CALLBACK (cb_dialog_doc_metadata_change_permission),
767 state);
771 /******************************************************************************
772 * FUNCTIONS RELATED TO 'DESCRIPTION' PAGE
773 ******************************************************************************/
775 /* @activate_property : if TRUE, sets the tree view row which was added active. */
776 static void
777 dialog_doc_metadata_add_prop (DialogDocMetaData *state,
778 const gchar *name,
779 const gchar *value,
780 const gchar *lnk,
781 GType val_type)
783 gboolean editable = (val_type != G_TYPE_INVALID)
784 && (val_type != GSF_DOCPROP_VECTOR_TYPE);
785 if (value == NULL)
786 value = "";
788 if (lnk == NULL)
789 lnk = "";
791 /* Append new values in tree view */
792 gtk_tree_store_insert_with_values (state->properties_store, NULL, NULL, G_MAXINT,
793 0, name,
794 1, value,
795 2, lnk,
796 3, editable,
797 4, val_type,
798 -1);
801 static GType
802 dialog_doc_metadata_get_gsf_prop_val_type (DialogDocMetaData *state,
803 const gchar *name)
805 GsfDocProp *prop = NULL;
806 GValue *value = NULL;
808 g_return_val_if_fail (state->metadata != NULL, G_TYPE_INVALID);
810 /* First we try the clean way */
811 prop = gsf_doc_meta_data_lookup (state->metadata, name);
813 if (prop != NULL)
814 value = (GValue *) gsf_doc_prop_get_val (prop);
816 if (value != NULL)
817 return dialog_doc_metadata_get_value_type (value);
818 else
819 return dialog_doc_metadata_get_value_type_from_name (name, G_TYPE_STRING);
822 static void
823 dialog_doc_metadata_set_gsf_prop_val (DialogDocMetaData *state,
824 GValue *prop_value,
825 const gchar *str_val)
827 GType t = G_VALUE_TYPE (prop_value);
829 /* The preinstalled transform functions do not handle simple transformations */
830 /* such as from string to double, so we do that ourselves */
831 switch (t) {
832 case G_TYPE_STRING:
833 g_value_set_string (prop_value, g_strdup (str_val));
834 break;
835 case G_TYPE_DOUBLE:
836 case G_TYPE_FLOAT: {
837 GnmParsePos pos;
838 GnmValue *val = NULL;
839 GnmExprTop const *texpr = NULL;
840 parse_pos_init_sheet (&pos, workbook_sheet_by_index (state->wb, 0));
841 parse_text_value_or_expr (&pos, str_val,
842 &val, &texpr);
843 if (val != NULL) {
844 gnm_float fl = value_get_as_float (val);
845 value_release (val);
846 if (t == G_TYPE_DOUBLE)
847 g_value_set_double (prop_value, fl);
848 else
849 g_value_set_float (prop_value, fl);
851 if (texpr)
852 gnm_expr_top_unref (texpr);
853 break;
855 default:
856 if (g_value_type_transformable (t, G_TYPE_STRING)) {
857 GValue string_value = G_VALUE_INIT;
858 g_value_init (&string_value, G_TYPE_STRING);
859 g_value_set_string (&string_value, g_strdup (str_val));
860 g_value_transform (&string_value, prop_value);
861 g_value_unset (&string_value);
862 } else
863 g_printerr (_("Transform function of G_TYPE_STRING to %s is required!\n"),
864 g_type_name (t));
865 break;
870 * dialog_doc_metadata_set_gsf_prop
872 * @state: dialog main struct
873 * @name: property name
874 * @value: property value
875 * @lnk: property linked to
877 * Sets a new value to the property in the GsfDocMetaData struct
880 static GType
881 dialog_doc_metadata_set_gsf_prop (DialogDocMetaData *state,
882 const gchar *name,
883 const gchar *value,
884 const gchar *lnk,
885 GType type)
887 GsfDocProp *existing_prop = NULL;
888 GsfDocProp *doc_prop;
889 GValue *existing_value = NULL;
890 char const *existing_link = NULL;
891 GType val_type;
893 existing_prop = gsf_doc_meta_data_lookup (state->metadata, name);
894 if (existing_prop != NULL) {
895 existing_value = (GValue *) gsf_doc_prop_get_val (existing_prop);
896 existing_link = gsf_doc_prop_get_link (existing_prop);
899 if (lnk != NULL && *lnk == 0)
900 lnk = NULL;
901 if (value != NULL && *value == 0)
902 value = NULL;
903 if ((value == NULL) && (lnk == NULL)) {
904 if ((existing_prop == NULL) ||
905 ((existing_value == NULL) && (existing_link == NULL)))
906 return G_TYPE_INVALID;
907 else {
908 cmd_change_meta_data (GNM_WBC (state->wbcg), NULL,
909 g_slist_prepend (NULL, g_strdup (name)));
910 return G_TYPE_INVALID;
914 if (existing_prop != NULL) {
915 gboolean link_changed;
916 gboolean value_changed = TRUE;
918 if (existing_link!= NULL && *existing_link == 0)
919 existing_link = NULL;
920 if (lnk == existing_link)
921 link_changed = FALSE;
922 else if (lnk == NULL || existing_link == NULL)
923 link_changed = TRUE;
924 else
925 link_changed = (0 != strcmp (lnk, existing_link));
927 if (existing_value == NULL)
928 value_changed = (value != NULL);
929 else if (G_VALUE_HOLDS_STRING (existing_value) && (type == 0 || type == G_TYPE_STRING)) {
930 char const * existing_val_str = g_value_get_string (existing_value);
931 if (existing_val_str != NULL && *existing_val_str == 0)
932 existing_val_str = NULL;
933 if (value == existing_val_str)
934 value_changed = FALSE;
935 else if (value == NULL || existing_val_str == NULL)
936 value_changed = TRUE;
937 else
938 value_changed = (0 != strcmp (value, existing_val_str));
939 if (!link_changed && !value_changed)
940 return G_TYPE_STRING;
945 /* Create a new GsfDocProp */
946 doc_prop = gsf_doc_prop_new (g_strdup (name));
948 if (type == 0)
949 val_type = dialog_doc_metadata_get_gsf_prop_val_type (state, name);
950 else
951 val_type = type;
953 if (val_type != G_TYPE_INVALID) {
954 GValue *doc_prop_value;
956 /* Create a new Value */
957 doc_prop_value = g_new0 (GValue, 1);
959 g_value_init (doc_prop_value, val_type);
960 dialog_doc_metadata_set_gsf_prop_val (state, doc_prop_value, value);
961 gsf_doc_prop_set_val (doc_prop, doc_prop_value);
964 if (lnk != NULL)
965 gsf_doc_prop_set_link (doc_prop, g_strdup (lnk));
967 cmd_change_meta_data (GNM_WBC (state->wbcg),
968 g_slist_prepend (NULL, doc_prop), NULL);
970 return val_type;
974 * dialog_doc_metadata_set_prop
976 * @state: dialog main struct
977 * @prop_name: property name
978 * @prop_value: property value
980 * Tries to update the property value in all the dialog and in the GsfDocMetaData
981 * struct. If the property was not found, creates a new one.
984 static void
985 dialog_doc_metadata_set_prop (DialogDocMetaData *state,
986 const gchar *prop_name,
987 const gchar *prop_value,
988 const gchar *link_value,
989 GType type)
991 GtkTreeIter tree_iter;
992 GValue *value;
993 gboolean ret;
994 gboolean found;
995 GType val_type;
996 GsfDocProp *updated_prop;
997 gchar *new_prop_value = NULL;
999 g_return_if_fail (state->metadata != NULL);
1001 val_type = dialog_doc_metadata_set_gsf_prop (state, prop_name, prop_value,
1002 link_value, type);
1004 /* Due to changes in type, prop_value may have changed */
1005 updated_prop = gsf_doc_meta_data_lookup (state->metadata, prop_name);
1006 if (updated_prop != NULL) {
1007 GValue *new_value = (GValue *) gsf_doc_prop_get_val (updated_prop);
1008 if (new_value != NULL)
1009 new_prop_value = dialog_doc_metadata_get_prop_val (state, prop_name, new_value);
1010 if (new_prop_value == NULL)
1011 new_prop_value = g_strdup ("");
1014 found = FALSE;
1016 /* Search tree view for property */
1017 value = g_new0 (GValue, 1);
1019 ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->properties_store),
1020 &tree_iter);
1022 while (ret == TRUE) {
1024 gtk_tree_model_get_value (GTK_TREE_MODEL (state->properties_store),
1025 &tree_iter,
1027 value);
1029 if (strcmp (prop_name, g_value_get_string (value)) == 0) {
1030 if (updated_prop != NULL) {
1031 /* Set new value */
1032 gtk_tree_store_set (state->properties_store,
1033 &tree_iter,
1034 1, new_prop_value,
1035 -1);
1037 if (link_value != NULL)
1038 gtk_tree_store_set (state->properties_store,
1039 &tree_iter,
1040 2, link_value,
1041 -1);
1042 } else
1043 /* Clear value */
1044 gtk_tree_store_remove (state->properties_store,
1045 &tree_iter);
1047 g_value_unset (value);
1049 found = TRUE;
1050 break;
1053 g_value_unset (value);
1054 ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (state->properties_store),
1055 &tree_iter);
1058 if (val_type != G_TYPE_INVALID && found == FALSE)
1059 dialog_doc_metadata_add_prop (state, prop_name, new_prop_value, "", val_type);
1061 /* Free all data */
1062 g_free (value);
1063 g_free (new_prop_value );
1067 * CALLBACKS for 'Description' page entries
1069 static gboolean
1070 cb_dialog_doc_metadata_title_changed (GtkEntry *entry,
1071 G_GNUC_UNUSED GdkEventFocus *event,
1072 DialogDocMetaData *state)
1074 dialog_doc_metadata_set_prop (state,
1075 GSF_META_NAME_TITLE,
1076 gtk_entry_get_text (entry),
1077 NULL, G_TYPE_STRING);
1078 return FALSE;
1081 static gboolean
1082 cb_dialog_doc_metadata_subject_changed (GtkEntry *entry,
1083 G_GNUC_UNUSED GdkEventFocus *event,
1084 DialogDocMetaData *state)
1086 dialog_doc_metadata_set_prop (state,
1087 GSF_META_NAME_SUBJECT,
1088 gtk_entry_get_text (entry),
1089 NULL, G_TYPE_STRING);
1090 return FALSE;
1093 static gboolean
1094 cb_dialog_doc_metadata_author_changed (GtkEntry *entry,
1095 G_GNUC_UNUSED GdkEventFocus *event,
1096 DialogDocMetaData *state)
1098 dialog_doc_metadata_set_prop (state,
1099 GSF_META_NAME_INITIAL_CREATOR,
1100 gtk_entry_get_text (entry),
1101 NULL, G_TYPE_STRING);
1102 return FALSE;
1105 static gboolean
1106 cb_dialog_doc_metadata_manager_changed (GtkEntry *entry,
1107 G_GNUC_UNUSED GdkEventFocus *event,
1108 DialogDocMetaData *state)
1110 dialog_doc_metadata_set_prop (state,
1111 GSF_META_NAME_MANAGER,
1112 gtk_entry_get_text (entry),
1113 NULL, G_TYPE_STRING);
1114 return FALSE;
1117 static gboolean
1118 cb_dialog_doc_metadata_company_changed (GtkEntry *entry,
1119 G_GNUC_UNUSED GdkEventFocus *event,
1120 DialogDocMetaData *state)
1122 dialog_doc_metadata_set_prop (state,
1123 GSF_META_NAME_COMPANY,
1124 gtk_entry_get_text (entry),
1125 NULL, G_TYPE_STRING);
1126 return FALSE;
1129 static gboolean
1130 cb_dialog_doc_metadata_category_changed (GtkEntry *entry,
1131 G_GNUC_UNUSED GdkEventFocus *event,
1132 DialogDocMetaData *state)
1134 dialog_doc_metadata_set_prop (state,
1135 GSF_META_NAME_CATEGORY,
1136 gtk_entry_get_text (entry),
1137 NULL, G_TYPE_STRING );
1138 return FALSE;
1141 static gboolean
1142 cb_dialog_doc_metadata_comments_changed (GtkTextView *view,
1143 G_GNUC_UNUSED GdkEventFocus *event,
1144 DialogDocMetaData *state)
1146 GtkTextIter start_iter;
1147 GtkTextIter end_iter;
1148 gchar *text;
1149 GtkTextBuffer *buffer = gtk_text_view_get_buffer (view);
1151 gtk_text_buffer_get_start_iter (buffer, &start_iter);
1152 gtk_text_buffer_get_end_iter (buffer, &end_iter);
1154 text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, TRUE);
1156 dialog_doc_metadata_set_prop (state,
1157 GSF_META_NAME_DESCRIPTION,
1158 text,
1159 NULL, G_TYPE_STRING);
1160 return FALSE;
1164 * dialog_doc_metadata_init_description_page
1166 * @state: dialog main struct
1168 * Initializes the widgets and signals for the 'Description' page.
1171 static void
1172 dialog_doc_metadata_init_description_page (DialogDocMetaData *state)
1174 g_return_if_fail (state->metadata != NULL);
1176 /* At this point, the entry values were already filled */
1178 /* Set up the signals */
1179 g_signal_connect (G_OBJECT (state->title),
1180 "focus-out-event",
1181 G_CALLBACK (cb_dialog_doc_metadata_title_changed),
1182 state);
1184 g_signal_connect (G_OBJECT (state->subject),
1185 "focus-out-event",
1186 G_CALLBACK (cb_dialog_doc_metadata_subject_changed),
1187 state);
1189 g_signal_connect (G_OBJECT (state->author),
1190 "focus-out-event",
1191 G_CALLBACK (cb_dialog_doc_metadata_author_changed),
1192 state);
1194 g_signal_connect (G_OBJECT (state->manager),
1195 "focus-out-event",
1196 G_CALLBACK (cb_dialog_doc_metadata_manager_changed),
1197 state);
1199 g_signal_connect (G_OBJECT (state->company),
1200 "focus-out-event",
1201 G_CALLBACK (cb_dialog_doc_metadata_company_changed),
1202 state);
1204 g_signal_connect (G_OBJECT (state->category),
1205 "focus-out-event",
1206 G_CALLBACK (cb_dialog_doc_metadata_category_changed),
1207 state);
1209 g_signal_connect (G_OBJECT (state->comments),
1210 "focus-out-event",
1211 G_CALLBACK (cb_dialog_doc_metadata_comments_changed),
1212 state);
1215 /******************************************************************************
1216 * FUNCTIONS RELATED TO 'KEYWORDS' PAGE
1217 ******************************************************************************/
1219 static void
1220 dialog_doc_metadata_update_keywords_changed (DialogDocMetaData *state)
1222 GValue val = G_VALUE_INIT;
1223 GtkTreeIter iter;
1224 GsfDocPropVector *vector = gsf_docprop_vector_new ();
1226 g_value_init (&val, GSF_DOCPROP_VECTOR_TYPE);
1228 if (gtk_tree_model_get_iter_first
1229 (GTK_TREE_MODEL (state->key_store), &iter)) {
1230 do {
1231 GValue *value = g_new0 (GValue, 1);
1232 gtk_tree_model_get_value
1233 (GTK_TREE_MODEL (state->key_store), &iter,
1234 0, value);
1235 gsf_docprop_vector_append (vector, value);
1236 g_value_unset (value);
1237 g_free (value);
1238 } while (gtk_tree_model_iter_next
1239 (GTK_TREE_MODEL (state->key_store), &iter));
1241 g_value_set_object (&val, vector);
1242 g_object_unref (vector);
1244 dialog_doc_metadata_set_prop
1245 (state, GSF_META_NAME_KEYWORDS,
1246 dialog_doc_metadata_get_prop_val (state, GSF_META_NAME_KEYWORDS, &val),
1247 NULL, GSF_DOCPROP_VECTOR_TYPE);
1249 g_value_unset (&val);
1252 static void
1253 cb_dialog_doc_metadata_keywords_sel_changed (GtkTreeSelection *treeselection,
1254 DialogDocMetaData *state)
1256 gtk_widget_set_sensitive
1257 (GTK_WIDGET (state->key_remove_button),
1258 gtk_tree_selection_get_selected (treeselection, NULL, NULL));
1261 static void
1262 dialog_doc_metadata_update_keyword_list (DialogDocMetaData *state, GsfDocProp *prop)
1264 guint i;
1265 GtkTreeSelection *sel;
1267 gtk_list_store_clear (state->key_store);
1269 if (prop != NULL) {
1270 GValueArray *array;
1271 array = gsf_value_get_docprop_varray (gsf_doc_prop_get_val (prop));
1272 if (array != NULL) {
1273 for (i = 0; i < array->n_values; i++) {
1274 GValue *val = g_value_array_get_nth (array, i);
1275 gtk_list_store_insert_with_values
1276 (state->key_store, NULL, G_MAXINT,
1277 0, g_value_get_string (val), -1);
1282 sel = gtk_tree_view_get_selection (state->key_tree_view);
1283 cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
1286 static void
1287 cb_dialog_doc_metadata_keywords_add_clicked (G_GNUC_UNUSED GtkWidget *w,
1288 DialogDocMetaData *state)
1290 gtk_list_store_insert_with_values (state->key_store, NULL, G_MAXINT,
1291 0, "<?>", -1);
1292 dialog_doc_metadata_update_keywords_changed (state);
1295 static void
1296 cb_dialog_doc_metadata_keywords_remove_clicked (G_GNUC_UNUSED GtkWidget *w,
1297 DialogDocMetaData *state)
1299 GtkTreeIter iter;
1300 GtkTreeSelection *sel = gtk_tree_view_get_selection (state->key_tree_view);
1302 if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
1303 gtk_list_store_remove (state->key_store, &iter);
1304 dialog_doc_metadata_update_keywords_changed (state);
1308 static void
1309 cb_dialog_doc_metadata_keyword_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
1310 gchar *path,
1311 gchar *new_text,
1312 DialogDocMetaData *state)
1314 GtkTreeIter iter;
1315 if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (state->key_store), &iter, path)) {
1316 gtk_list_store_set (state->key_store, &iter, 0, new_text, -1);
1317 dialog_doc_metadata_update_keywords_changed (state);
1323 * dialog_doc_metadata_init_keywords_page
1325 * @state: dialog main struct
1327 * Initializes the widgets and signals for the 'Description' page.
1330 static void
1331 dialog_doc_metadata_init_keywords_page (DialogDocMetaData *state)
1333 GtkTreeViewColumn *column;
1334 GtkCellRenderer *renderer;
1335 GtkTreeSelection *sel;
1337 g_return_if_fail (state->metadata != NULL);
1339 renderer = gtk_cell_renderer_text_new ();
1340 g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
1341 column = gtk_tree_view_column_new_with_attributes (_("Keywords"),
1342 renderer,
1343 "text", 0,
1344 NULL);
1345 gtk_tree_view_insert_column (state->key_tree_view, column, -1);
1347 gtk_widget_set_sensitive (GTK_WIDGET (state->key_add_button), TRUE);
1348 gtk_widget_set_sensitive (GTK_WIDGET (state->key_remove_button), FALSE);
1350 sel = gtk_tree_view_get_selection (state->key_tree_view);
1351 g_signal_connect (G_OBJECT (sel),
1352 "changed",
1353 G_CALLBACK (cb_dialog_doc_metadata_keywords_sel_changed),
1354 state);
1356 g_signal_connect (G_OBJECT (state->key_add_button),
1357 "clicked",
1358 G_CALLBACK (cb_dialog_doc_metadata_keywords_add_clicked),
1359 state);
1360 g_signal_connect (G_OBJECT (state->key_remove_button),
1361 "clicked",
1362 G_CALLBACK (cb_dialog_doc_metadata_keywords_remove_clicked),
1363 state);
1364 g_signal_connect (G_OBJECT (renderer),
1365 "edited",
1366 G_CALLBACK (cb_dialog_doc_metadata_keyword_edited),
1367 state);
1369 cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
1372 /******************************************************************************
1373 * FUNCTIONS RELATED TO 'PROPERTIES' PAGE
1374 ******************************************************************************/
1376 static void
1377 cb_dialog_doc_metadata_value_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
1378 gchar *path,
1379 gchar *new_text,
1380 DialogDocMetaData *state)
1382 GtkTreeIter iter;
1383 if (gtk_tree_model_get_iter_from_string
1384 (GTK_TREE_MODEL (state->properties_store), &iter, path)) {
1385 gchar *prop_name;
1386 gchar *link_value;
1387 GType type;
1389 gtk_tree_model_get (GTK_TREE_MODEL (state->properties_store),
1390 &iter,
1391 0, &prop_name,
1392 2, &link_value,
1393 4, &type,
1394 -1);
1395 dialog_doc_metadata_set_prop (state, prop_name, new_text, link_value, type);
1396 g_free (prop_name);
1397 g_free (link_value);
1402 * cb_dialog_doc_metadata_add_clicked
1404 * @w: widget
1405 * @state: dialog main struct
1407 * Adds a new "empty" property to the tree view.
1410 static void
1411 cb_dialog_doc_metadata_add_clicked (G_GNUC_UNUSED GtkWidget *w,
1412 DialogDocMetaData *state)
1414 const gchar *name = gtk_entry_get_text (state->ppt_name);
1415 const gchar *value = gtk_entry_get_text (state->ppt_value);
1416 gchar *name_trimmed = trim_string (name);
1417 GType t;
1418 GtkTreeIter filter_iter;
1420 if (gtk_combo_box_get_active_iter (state->ppt_type, &filter_iter)) {
1421 GtkTreeIter child_iter;
1422 gtk_tree_model_filter_convert_iter_to_child_iter
1423 (state->type_store_filter, &child_iter, &filter_iter);
1424 gtk_tree_model_get (GTK_TREE_MODEL (state->type_store), &child_iter,
1425 1, &t, -1);
1426 } else
1427 t = dialog_doc_metadata_get_value_type_from_name (name_trimmed, G_TYPE_STRING);
1428 dialog_doc_metadata_set_prop (state, name_trimmed, value, NULL, t);
1430 g_free (name_trimmed);
1432 cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1433 gtk_label_set_text (state->warning, "");
1437 * dialog_doc_metadata_update_prop
1439 * @state: dialog main struct
1440 * @prop_name: property name
1441 * @prop_value: property value
1443 * Updates a label or a entry text with the new value.
1447 static void
1448 dialog_doc_metadata_update_prop (DialogDocMetaData *state,
1449 const gchar *prop_name,
1450 const gchar *prop_value,
1451 GsfDocProp *prop)
1453 /* Labels */
1454 if (strcmp (prop_name, GSF_META_NAME_DATE_CREATED) == 0) {
1455 dialog_doc_metadata_set_label (state,
1456 state->created,
1457 prop_value,
1458 TRUE);
1460 else if (strcmp (prop_name, GSF_META_NAME_DATE_MODIFIED) == 0) {
1461 dialog_doc_metadata_set_label (state,
1462 state->modified,
1463 prop_value,
1464 TRUE);
1466 else if (strcmp (prop_name, GSF_META_NAME_SPREADSHEET_COUNT) == 0) {
1467 dialog_doc_metadata_set_label (state,
1468 state->sheets,
1469 prop_value,
1470 TRUE);
1472 else if (strcmp (prop_name, GSF_META_NAME_CELL_COUNT) == 0) {
1473 dialog_doc_metadata_set_label (state,
1474 state->cells,
1475 prop_value,
1476 TRUE);
1478 else if (strcmp (prop_name, GSF_META_NAME_PAGE_COUNT) == 0) {
1479 dialog_doc_metadata_set_label (state,
1480 state->pages,
1481 prop_value,
1482 TRUE);
1485 /* Entries */
1486 if (prop_value == NULL)
1487 prop_value = "";
1489 if (strcmp (prop_name, GSF_META_NAME_TITLE) == 0) {
1490 gtk_entry_set_text (state->title, prop_value);
1493 else if (strcmp (prop_name, GSF_META_NAME_SUBJECT) == 0) {
1494 gtk_entry_set_text (state->subject, prop_value);
1497 else if (strcmp (prop_name, GSF_META_NAME_INITIAL_CREATOR) == 0) {
1498 gtk_entry_set_text (state->author, prop_value);
1501 else if (strcmp (prop_name, GSF_META_NAME_MANAGER) == 0) {
1502 gtk_entry_set_text (state->manager, prop_value);
1505 else if (strcmp (prop_name, GSF_META_NAME_COMPANY) == 0) {
1506 gtk_entry_set_text (state->company, prop_value);
1509 else if (strcmp (prop_name, GSF_META_NAME_CATEGORY) == 0) {
1510 gtk_entry_set_text (state->category, prop_value);
1513 else if (strcmp (prop_name, GSF_META_NAME_KEYWORDS) == 0) {
1514 dialog_doc_metadata_update_keyword_list (state, prop);
1517 else if (strcmp (prop_name, GSF_META_NAME_DESCRIPTION) == 0) {
1518 gtk_text_buffer_set_text (gtk_text_view_get_buffer (state->comments),
1519 prop_value,
1520 -1);
1525 * cb_dialog_doc_metadata_remove_clicked
1527 * @remove_bt: widget
1528 * @state: dialog main struct
1530 * Removes a property from the tree view and updates all the dialog and
1531 * the GsfDocMetaData struct.
1534 static void
1535 cb_dialog_doc_metadata_remove_clicked (GtkWidget *remove_bt,
1536 DialogDocMetaData *state)
1538 GtkTreeIter tree_iter;
1539 GValue *prop_name;
1540 GtkTreeSelection *sel = gtk_tree_view_get_selection (state->properties);
1542 g_return_if_fail (state->metadata != NULL);
1544 if (gtk_tree_selection_get_selected (sel, NULL, &tree_iter)) {
1546 /* Get the property name */
1547 prop_name = g_new0 (GValue, 1);
1548 gtk_tree_model_get_value (GTK_TREE_MODEL (state->properties_store),
1549 &tree_iter,
1551 prop_name);
1553 /* Update other pages */
1554 dialog_doc_metadata_update_prop (state,
1555 g_value_get_string (prop_name),
1556 NULL, NULL);
1558 /* Remove property from GsfMetadata */
1559 cmd_change_meta_data (GNM_WBC (state->wbcg), NULL,
1560 g_slist_prepend (NULL, g_value_dup_string (prop_name)));
1562 /* Remove from Tree View */
1563 gtk_tree_store_remove (state->properties_store,
1564 &tree_iter);
1566 /* Free all data */
1567 g_value_unset (prop_name);
1568 g_free (prop_name);
1571 /* Set remove button insensitive */
1572 gtk_widget_set_sensitive (remove_bt, FALSE);
1577 * cb_dialog_doc_metadata_tree_prop_selected
1579 * @combo_box: widget
1580 * @state: dialog main struct
1582 * Update the highlited item in the 'Properties' page combo box.
1585 static void
1586 cb_dialog_doc_metadata_tree_prop_selected (GtkTreeSelection *selection,
1587 DialogDocMetaData *state)
1589 GtkTreeIter iter;
1590 gboolean selected;
1591 gchar const *text = "";
1593 g_return_if_fail (state->metadata != NULL);
1595 selected = gtk_tree_selection_get_selected (selection, NULL, &iter);
1597 /* Set remove button sensitive */
1598 gtk_widget_set_sensitive (GTK_WIDGET (state->remove_button), selected);
1600 if (selected) {
1601 GType val_type = G_TYPE_INVALID;
1602 gchar *prop_name = NULL;
1603 gtk_tree_model_get (GTK_TREE_MODEL (state->properties_store),
1604 &iter,
1605 0, &prop_name,
1606 4, &val_type,
1607 -1);
1608 switch (val_type) {
1609 case G_TYPE_STRING:
1610 text = _("Edit string value directly in above listing.");
1611 break;
1612 case G_TYPE_UINT:
1613 text = _("Edit positive integer value directly in above listing.");
1614 break;
1615 case G_TYPE_INT:
1616 text = _("Edit integer value directly in above listing.");
1617 break;
1618 case G_TYPE_FLOAT:
1619 case G_TYPE_DOUBLE:
1620 text = _("Edit decimal number value directly in above listing.");
1621 break;
1622 case G_TYPE_BOOLEAN:
1623 text = _("Edit TRUE/FALSE value directly in above listing.");
1624 break;
1625 default:
1626 if (val_type == GSF_DOCPROP_VECTOR_TYPE) {
1627 if (0 == strcmp (prop_name, "dc:keywords"))
1628 text = _("To edit, use the keywords tab.");
1629 else
1630 text = _("This property value cannot be edited.");
1631 } else if (val_type == GSF_TIMESTAMP_TYPE)
1632 text= _("Edit timestamp directly in above listing.");
1633 break;
1635 g_free (prop_name);
1638 gtk_label_set_text (state->instruction, text);
1642 * dialog_doc_metadata_get_prop_val
1644 * @prop_name: property name
1645 * @prop_value: property value
1647 * Retrieves an arbitrary property value always as string.
1650 static gchar *
1651 dialog_doc_metadata_get_prop_val (G_GNUC_UNUSED DialogDocMetaData *state,
1652 char const *prop_name,
1653 GValue *prop_value)
1655 GValue str_value = G_VALUE_INIT;
1656 gboolean ret = FALSE;
1658 g_return_val_if_fail (prop_value != NULL, NULL);
1660 g_value_init (&str_value, G_TYPE_STRING);
1662 ret = g_value_transform (prop_value, &str_value);
1664 if (ret == FALSE) {
1665 g_warning ("Metadata tag '%s' holds unrecognized value type.", prop_name);
1666 return NULL;
1669 return g_value_dup_string (&str_value);
1673 * dialog_doc_metadata_populate_tree_view
1675 * @name: property name
1676 * @prop: property stored in GsfDocMetaData
1677 * @state: dialog main struct
1679 * Populates the tree view in 'Properties' page.
1682 static void
1683 dialog_doc_metadata_populate_tree_view (gchar *name,
1684 GsfDocProp *prop,
1685 DialogDocMetaData *state)
1687 gchar *str_value;
1688 char *link_value;
1689 GValue *value;
1691 g_return_if_fail (state->metadata != NULL);
1693 value = (GValue *) gsf_doc_prop_get_val (prop);
1694 /* Get the prop value as string */
1695 str_value = dialog_doc_metadata_get_prop_val (state, name, value);
1697 link_value = (char *) gsf_doc_prop_get_link (prop);
1699 dialog_doc_metadata_add_prop
1700 (state,
1701 gsf_doc_prop_get_name (prop),
1702 str_value == NULL ? "" : str_value,
1703 link_value == NULL ? "" : link_value,
1704 dialog_doc_metadata_get_value_type (value));
1706 dialog_doc_metadata_update_prop (state, gsf_doc_prop_get_name (prop), str_value, prop);
1708 g_free (str_value);
1711 static gboolean
1712 dialog_doc_metadata_show_all_types (GtkTreeModel *model,
1713 G_GNUC_UNUSED GtkTreePath *path,
1714 GtkTreeIter *iter,
1715 G_GNUC_UNUSED gpointer data)
1717 gtk_list_store_set (GTK_LIST_STORE (model), iter, 2, TRUE, -1);
1718 return FALSE;
1721 static gboolean
1722 dialog_doc_metadata_show_this_type (GtkTreeModel *model,
1723 G_GNUC_UNUSED GtkTreePath *path,
1724 GtkTreeIter *iter,
1725 gpointer data)
1727 GType t, type = GPOINTER_TO_INT (data);
1729 gtk_tree_model_get (model, iter, 1, &t, -1);
1730 gtk_list_store_set (GTK_LIST_STORE (model), iter, 2, t == type, -1);
1731 return FALSE;
1734 static gboolean
1735 cb_dialog_doc_metadata_ppt_changed (G_GNUC_UNUSED GtkEntry *entry,
1736 G_GNUC_UNUSED GdkEventFocus *event,
1737 DialogDocMetaData *state)
1739 const gchar *name;
1740 const gchar *value;
1741 gchar *name_trimmed;
1742 gboolean enable = FALSE;
1743 gchar *str = NULL;
1744 GsfDocProp *prop = NULL;
1745 GtkTreeIter iter;
1747 name = gtk_entry_get_text (state->ppt_name);
1748 value = gtk_entry_get_text (state->ppt_value);
1749 name_trimmed = trim_string (name);
1751 enable = strlen (name_trimmed) > 0 && strlen (value) > 0;
1753 if (enable)
1754 enable = gtk_combo_box_get_active_iter (state->ppt_type, &iter);
1756 if (enable) {
1757 prop = gsf_doc_meta_data_lookup (state->metadata, name_trimmed);
1758 if (prop != NULL) {
1759 str = g_strdup_printf
1760 (_("A document property with the name \'%s\' already exists."),
1761 name_trimmed);
1762 enable = FALSE;
1765 g_free (name_trimmed);
1766 gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), enable);
1767 gtk_label_set_text (state->warning, str ? str : "");
1768 g_free (str);
1769 return FALSE;
1772 static void
1773 cb_dialog_doc_metadata_ppt_type_changed (G_GNUC_UNUSED GtkComboBox *widget,
1774 DialogDocMetaData *state)
1776 cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1779 static gboolean
1780 cb_dialog_doc_metadata_ppt_name_changed (G_GNUC_UNUSED GtkEntry *entry,
1781 G_GNUC_UNUSED GdkEventFocus *event,
1782 DialogDocMetaData *state)
1784 const gchar *name;
1785 gchar *name_trimmed;
1786 gboolean enable = FALSE;
1787 GtkTreeIter iter;
1788 gchar *str = NULL;
1790 name = gtk_entry_get_text (state->ppt_name);
1791 name_trimmed = trim_string (name);
1793 enable = strlen (name_trimmed) > 0;
1795 if (enable) {
1796 GType t = dialog_doc_metadata_get_value_type_from_name (name_trimmed, G_TYPE_INVALID);
1798 if (t == GSF_DOCPROP_VECTOR_TYPE) {
1799 str = g_strdup_printf
1800 (_("Use the keywords tab to create this property."));
1801 enable = FALSE;
1804 if (t == G_TYPE_INVALID) {
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_all_types, NULL);
1810 gtk_tree_model_filter_refilter (state->type_store_filter);
1811 g_signal_handlers_unblock_by_func(G_OBJECT (state->ppt_type),
1812 cb_dialog_doc_metadata_ppt_type_changed,
1813 state);
1814 } else {
1815 gtk_combo_box_set_active_iter (state->ppt_type, NULL);
1816 g_signal_handlers_block_by_func(G_OBJECT (state->ppt_type),
1817 cb_dialog_doc_metadata_ppt_type_changed,
1818 state);
1819 gtk_tree_model_foreach (GTK_TREE_MODEL (state->type_store),
1820 dialog_doc_metadata_show_this_type,
1821 GINT_TO_POINTER (t));
1822 gtk_tree_model_filter_refilter (state->type_store_filter);
1823 g_signal_handlers_unblock_by_func(G_OBJECT (state->ppt_type),
1824 cb_dialog_doc_metadata_ppt_type_changed,
1825 state);
1826 if (gtk_tree_model_get_iter_first
1827 (GTK_TREE_MODEL (state->type_store_filter), &iter))
1828 gtk_combo_box_set_active_iter (state->ppt_type, &iter);
1831 g_free (name_trimmed);
1833 if (enable)
1834 return cb_dialog_doc_metadata_ppt_changed (NULL, NULL, state);
1835 else {
1836 gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), FALSE);
1837 gtk_label_set_text (state->warning, str ? str : "");
1838 g_free (str);
1841 return FALSE;
1847 * dialog_doc_metadata_init_properties_page
1849 * @state: dialog main struct
1851 * Initializes the widgets and signals for the 'Properties' page.
1854 static void
1855 dialog_doc_metadata_init_properties_page (DialogDocMetaData *state)
1857 GtkTreeSelection *sel;
1858 GtkCellRenderer *cell;
1859 guint i;
1861 struct types {
1862 char const *type_name;
1863 GType type;
1864 } ppt_types[] = {
1865 {N_("String"), G_TYPE_STRING},
1866 {N_("Integer"), G_TYPE_INT},
1867 {N_("Decimal Number"), G_TYPE_FLOAT},
1868 {N_("TRUE/FALSE"), G_TYPE_BOOLEAN}
1871 g_return_if_fail (state->metadata != NULL);
1872 g_return_if_fail (state->properties != NULL);
1874 /* Set Remove and Apply buttons insensitive */
1875 gtk_widget_set_sensitive (GTK_WIDGET (state->add_button), FALSE);
1876 gtk_widget_set_sensitive (GTK_WIDGET (state->remove_button), FALSE);
1878 /* Intialize Combo Box */
1879 /* gtk_combo_box_set_id_column (state->ppt_type, 0); */
1880 cell = gtk_cell_renderer_text_new();
1881 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(state->ppt_type), cell, TRUE);
1882 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(state->ppt_type), cell, "text", 0, NULL);
1884 for (i = 0; i < G_N_ELEMENTS (ppt_types); i++)
1885 gtk_list_store_insert_with_values (state->type_store, NULL, G_MAXINT,
1886 0, _(ppt_types[i].type_name),
1887 1, ppt_types[i].type,
1888 2, TRUE,
1889 -1);
1890 gtk_list_store_insert_with_values (state->type_store, NULL, G_MAXINT,
1891 0, _("Date & Time"),
1892 1, GSF_TIMESTAMP_TYPE,
1893 2, TRUE,
1894 -1);
1895 gtk_tree_model_filter_set_visible_column (state->type_store_filter, 2);
1896 gtk_tree_model_filter_refilter (state->type_store_filter);
1898 /* Populate Treeview */
1899 state->properties_store = gtk_tree_store_new (5,
1900 G_TYPE_STRING,
1901 G_TYPE_STRING,
1902 G_TYPE_STRING,
1903 G_TYPE_BOOLEAN,
1904 G_TYPE_INT);
1906 gtk_tree_view_set_model (state->properties,
1907 GTK_TREE_MODEL (state->properties_store));
1908 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (state->properties_store),
1909 0, GTK_SORT_ASCENDING);
1910 g_object_unref (state->properties_store);
1912 /* Append Columns */
1913 gtk_tree_view_insert_column_with_attributes (state->properties,
1914 0, _("Name"),
1915 gtk_cell_renderer_text_new(),
1916 "text", 0,
1917 NULL);
1919 cell = gtk_cell_renderer_text_new();
1920 gtk_tree_view_insert_column_with_attributes (state->properties,
1921 1, _("Value"),
1922 cell,
1923 "text", 1,
1924 "editable", 3,
1925 NULL);
1926 g_signal_connect (G_OBJECT (cell),
1927 "edited",
1928 G_CALLBACK (cb_dialog_doc_metadata_value_edited),
1929 state);
1931 gtk_tree_view_insert_column_with_attributes (state->properties,
1932 2, _("Linked To"),
1933 gtk_cell_renderer_text_new(),
1934 "text", 2,
1935 NULL);
1937 /* Read all metadata */
1938 gsf_doc_meta_data_foreach (state->metadata,
1939 (GHFunc) dialog_doc_metadata_populate_tree_view,
1940 state);
1942 /* Set up signals */
1943 /* Tree View */
1944 sel = gtk_tree_view_get_selection (state->properties);
1945 g_signal_connect (G_OBJECT (sel),
1946 "changed",
1947 G_CALLBACK (cb_dialog_doc_metadata_tree_prop_selected),
1948 state);
1950 /* Entries */
1951 g_signal_connect (G_OBJECT (state->ppt_name),
1952 "focus-out-event",
1953 G_CALLBACK (cb_dialog_doc_metadata_ppt_name_changed),
1954 state);
1955 g_signal_connect (G_OBJECT (state->ppt_value),
1956 "focus-out-event",
1957 G_CALLBACK (cb_dialog_doc_metadata_ppt_changed),
1958 state);
1959 /* ComboBox */
1960 g_signal_connect (G_OBJECT (state->ppt_type),
1961 "changed",
1962 G_CALLBACK (cb_dialog_doc_metadata_ppt_type_changed),
1963 state);
1965 /* 'Add', 'Remove' and 'Apply' Button Signals */
1966 g_signal_connect (G_OBJECT (state->add_button),
1967 "clicked",
1968 G_CALLBACK (cb_dialog_doc_metadata_add_clicked),
1969 state);
1971 g_signal_connect (G_OBJECT (state->remove_button),
1972 "clicked",
1973 G_CALLBACK (cb_dialog_doc_metadata_remove_clicked),
1974 state);
1976 cb_dialog_doc_metadata_tree_prop_selected (sel, state);
1977 gtk_combo_box_set_active (state->ppt_type, 0);
1980 /******************************************************************************
1981 * FUNCTIONS RELATED TO 'STATISTICS' PAGE
1982 ******************************************************************************/
1985 * dialog_doc_metadata_init_statistics_page
1987 * @state: dialog main struct
1989 * Initializes the widgets and signals for the 'Statistics' page.
1992 static void
1993 dialog_doc_metadata_init_statistics_page (DialogDocMetaData *state)
1995 g_return_if_fail (state->metadata != NULL);
1997 /* Set up the labels */
1998 dialog_doc_metadata_set_label (state, state->sheets, NULL, TRUE);
1999 dialog_doc_metadata_set_label (state, state->cells, NULL, TRUE);
2000 dialog_doc_metadata_set_label (state, state->pages, NULL, TRUE);
2003 /******************************************************************************
2004 * FUNCTIONS RELATED TO 'CALCULATIONS' PAGE
2005 ******************************************************************************/
2007 static gboolean
2008 cb_dialog_doc_metadata_recalc_max_changed (GtkEntry *entry,
2009 G_GNUC_UNUSED GdkEventFocus *event,
2010 DialogDocMetaData *state)
2012 int val;
2013 if (!entry_to_int (entry, &val, TRUE))
2014 /* FIXME: make undoable */
2015 workbook_iteration_max_number (state->wb, val);
2016 return FALSE;
2019 static gboolean
2020 cb_dialog_doc_metadata_recalc_tolerance_changed (GtkEntry *entry,
2021 G_GNUC_UNUSED GdkEventFocus *event,
2022 DialogDocMetaData *state)
2024 gnm_float val;
2025 if (!entry_to_float (entry, &val, TRUE))
2026 /* FIXME: make undoable */
2027 workbook_iteration_tolerance (state->wb, val);
2028 return FALSE;
2031 static void
2032 cb_dialog_doc_metadata_recalc_auto_changed (GtkWidget *widget, DialogDocMetaData *state)
2034 /* FIXME: make undoable */
2035 workbook_set_recalcmode (state->wb, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
2038 static void
2039 cb_dialog_doc_metadata_recalc_iteration_changed (G_GNUC_UNUSED GtkWidget *widget, DialogDocMetaData *state)
2041 /* FIXME: make undoable */
2042 workbook_iteration_enabled (state->wb, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
2043 gtk_widget_set_sensitive (state->recalc_iteration_grid, state->wb->iteration.enabled);
2047 * dialog_doc_metadata_init_calculations_page
2049 * @state: dialog main struct
2051 * Initializes the widgets and signals for the 'Calculations' page.
2054 static void
2055 dialog_doc_metadata_init_calculations_page (DialogDocMetaData *state)
2057 char *buf;
2059 gtk_toggle_button_set_active
2060 (GTK_TOGGLE_BUTTON ( workbook_get_recalcmode (state->wb) ? state->recalc_auto : state->recalc_manual),
2061 TRUE);
2062 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->recalc_iteration),
2063 state->wb->iteration.enabled);
2064 gtk_widget_set_sensitive (state->recalc_iteration_grid, state->wb->iteration.enabled);
2066 buf = g_strdup_printf ("%d", state->wb->iteration.max_number);
2067 gtk_entry_set_text (state->recalc_max, buf);
2068 g_free (buf);
2069 buf = g_strdup_printf ("%g", state->wb->iteration.tolerance);
2070 gtk_entry_set_text (state->recalc_tolerance, buf);
2071 g_free (buf);
2073 g_signal_connect (G_OBJECT (state->recalc_auto),
2074 "toggled",
2075 G_CALLBACK (cb_dialog_doc_metadata_recalc_auto_changed), state);
2076 g_signal_connect (G_OBJECT (state->recalc_iteration),
2077 "toggled",
2078 G_CALLBACK (cb_dialog_doc_metadata_recalc_iteration_changed), state);
2079 g_signal_connect (G_OBJECT (state->recalc_max),
2080 "focus-out-event",
2081 G_CALLBACK (cb_dialog_doc_metadata_recalc_max_changed),
2082 state);
2083 g_signal_connect (G_OBJECT (state->recalc_tolerance),
2084 "focus-out-event",
2085 G_CALLBACK (cb_dialog_doc_metadata_recalc_tolerance_changed),
2086 state);
2090 /******************************************************************************
2091 * DIALOG INITIALIZE/FINALIZE FUNCTIONS
2092 ******************************************************************************/
2095 * dialog_doc_metadata_set_file_permissions
2097 * @state: dialog main struct
2099 * Writes the new file permissions if there were any changes.
2102 static void
2103 dialog_doc_metadata_set_file_permissions (DialogDocMetaData *state)
2105 if (state->file_permissions != NULL &&
2106 state->permissions_changed == TRUE)
2107 go_set_file_permissions (go_doc_get_uri (state->doc),
2108 state->file_permissions);
2111 static void
2112 dialog_doc_metadata_free (DialogDocMetaData *state)
2114 WorkbookControl *wbc = GNM_WBC (state->wbcg);
2116 wb_view_selection_desc (wb_control_view (wbc), TRUE, wbc);
2118 if (state->gui != NULL) {
2119 dialog_doc_metadata_set_file_permissions (state);
2121 g_object_unref (state->gui);
2122 state->gui = NULL;
2125 g_free (state->file_permissions);
2126 state->file_permissions = NULL;
2128 wbcg_edit_finish (state->wbcg, WBC_EDIT_REJECT, NULL);
2130 state->dialog = NULL;
2132 g_free (state);
2135 static void
2136 dialog_doc_metadata_init_widgets (DialogDocMetaData *state)
2138 state->dialog = go_gtk_builder_get_widget (state->gui, "GOMetadataDialog");
2140 state->notebook = GTK_NOTEBOOK (go_gtk_builder_get_widget (state->gui, "notebook"));
2141 state->help_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "help_button"));
2142 state->close_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "close_button"));
2144 /* File Information Page */
2145 state->file_name = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "file_name"));
2146 state->location = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "location"));
2147 state->created = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "created"));
2148 state->modified = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "modified"));
2149 state->accessed = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "accessed"));
2150 state->owner = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "owner"));
2151 state->group = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "group"));
2153 state->owner_read = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "owner_read"));
2154 state->owner_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "owner_write"));
2156 state->group_read = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "group_read"));
2157 state->group_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "group_write"));
2159 state->others_read = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "others_read"));
2160 state->others_write = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "others_write"));
2162 /* Description Page */
2163 state->title = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "title"));
2164 state->subject = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "subject"));
2165 state->author = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "author"));
2166 state->manager = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "manager"));
2167 state->company = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "company"));
2168 state->category = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "category"));
2170 state->comments = GTK_TEXT_VIEW (go_gtk_builder_get_widget (state->gui, "comments"));
2172 /* Properties Page */
2173 state->properties = GTK_TREE_VIEW (go_gtk_builder_get_widget (state->gui, "properties"));
2175 state->ppt_name = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "property-name"));
2176 state->ppt_value = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "property-value"));
2177 state->ppt_type = GTK_COMBO_BOX (go_gtk_builder_get_widget (state->gui, "type-combo"));
2178 state->type_store = GTK_LIST_STORE (gtk_builder_get_object (state->gui, "typestore"));
2179 state->type_store_filter = GTK_TREE_MODEL_FILTER (gtk_combo_box_get_model (state->ppt_type));
2181 state->add_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "add_button"));
2182 state->remove_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "remove_button"));
2183 state->instruction = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "instruction-label"));
2184 state->warning = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "warning"));
2186 /* Keyword Page */
2187 state->key_tree_view = GTK_TREE_VIEW (go_gtk_builder_get_widget (state->gui, "keyview"));
2188 state->key_store = GTK_LIST_STORE (gtk_tree_view_get_model (state->key_tree_view));
2189 state->key_add_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "key-add-button"));
2190 state->key_remove_button = GTK_BUTTON (go_gtk_builder_get_widget (state->gui, "key-remove-button"));
2192 /* Statistics Page */
2193 state->sheets = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "sheets"));
2194 state->cells = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "cells"));
2195 state->pages = GTK_LABEL (go_gtk_builder_get_widget (state->gui, "pages"));
2197 /* Calculations Page */
2198 state->recalc_auto = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "recalc_auto"));
2199 state->recalc_manual = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "recalc_manual"));
2200 state->recalc_iteration = GTK_CHECK_BUTTON (go_gtk_builder_get_widget (state->gui, "iteration_enabled"));
2201 state->recalc_max = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "max_iterations"));
2202 state->recalc_tolerance = GTK_ENTRY (go_gtk_builder_get_widget (state->gui, "iteration_tolerance"));
2203 state->recalc_iteration_grid = go_gtk_builder_get_widget (state->gui, "iteration-grid");
2206 static void
2207 dialog_doc_meta_data_add_item (DialogDocMetaData *state, char const *page_name,
2208 char const *icon_name,
2209 int page, char const* parent_path)
2211 GtkTreeIter iter, parent;
2212 GdkPixbuf * icon = NULL;
2214 if (icon_name != NULL)
2215 icon = gtk_widget_render_icon_pixbuf (state->dialog, icon_name,
2216 GTK_ICON_SIZE_MENU);
2217 if ((parent_path != NULL) && gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (state->store),
2218 &parent, parent_path))
2219 gtk_tree_store_append (state->store, &iter, &parent);
2220 else
2221 gtk_tree_store_append (state->store, &iter, NULL);
2223 gtk_tree_store_set (state->store, &iter,
2224 ITEM_ICON, icon,
2225 ITEM_NAME, _(page_name),
2226 PAGE_NUMBER, page,
2227 -1);
2228 if (icon != NULL)
2229 g_object_unref (icon);
2232 typedef struct {
2233 char const *page_name;
2234 char const *icon_name;
2235 char const *parent_path;
2236 int const page;
2237 void (*page_initializer) (DialogDocMetaData *state);
2238 } page_info_t;
2240 static page_info_t const page_info[] = {
2241 /* IMPORTANT: OBEY THE ORDER 0 - 3 - 2 - 1 */
2242 {N_("File"), GTK_STOCK_FILE, NULL, 0, &dialog_doc_metadata_init_file_page },
2243 {N_("Statistics"), "gnumeric-graphguru", NULL, 3 ,&dialog_doc_metadata_init_statistics_page },
2244 {N_("Properties"), GTK_STOCK_PROPERTIES, NULL, 2, &dialog_doc_metadata_init_properties_page },
2245 {N_("Description"), GTK_STOCK_ABOUT, NULL, 1, &dialog_doc_metadata_init_description_page },
2246 {N_("Keywords"), GTK_STOCK_INDEX, NULL, 5, &dialog_doc_metadata_init_keywords_page },
2247 {N_("Calculation"), GTK_STOCK_EXECUTE, NULL, 4, &dialog_doc_metadata_init_calculations_page },
2248 {NULL, NULL, NULL, -1, NULL},
2251 typedef struct {
2252 int const page;
2253 GtkTreePath *path;
2254 } page_search_t;
2256 static gboolean
2257 dialog_doc_metadata_select_page_search (GtkTreeModel *model,
2258 GtkTreePath *path,
2259 GtkTreeIter *iter,
2260 page_search_t *pst)
2262 int page;
2263 gtk_tree_model_get (model, iter, PAGE_NUMBER, &page, -1);
2264 if (page == pst->page) {
2265 pst->path = gtk_tree_path_copy (path);
2266 return TRUE;
2267 } else
2268 return FALSE;
2271 static void
2272 dialog_doc_metadata_select_page (DialogDocMetaData *state, int page)
2274 page_search_t pst = {page, NULL};
2276 if (page >= 0)
2277 gtk_tree_model_foreach (GTK_TREE_MODEL (state->store),
2278 (GtkTreeModelForeachFunc) dialog_doc_metadata_select_page_search,
2279 &pst);
2281 if (pst.path == NULL)
2282 pst.path = gtk_tree_path_new_from_string ("0");
2284 if (pst.path != NULL) {
2285 gtk_tree_view_set_cursor (state->view, pst.path, NULL, FALSE);
2286 gtk_tree_view_expand_row (state->view, pst.path, TRUE);
2287 gtk_tree_path_free (pst.path);
2291 static void
2292 cb_dialog_doc_metadata_selection_changed (GtkTreeSelection *selection,
2293 DialogDocMetaData *state)
2295 GtkTreeIter iter;
2296 int page;
2298 if (gtk_tree_selection_get_selected (selection, NULL, &iter)) {
2299 gtk_tree_model_get (GTK_TREE_MODEL (state->store), &iter,
2300 PAGE_NUMBER, &page,
2301 -1);
2302 gtk_notebook_set_current_page (state->notebook, page);
2303 } else {
2304 dialog_doc_metadata_select_page (state, 0);
2308 static gboolean
2309 dialog_doc_metadata_init (DialogDocMetaData *state,
2310 WBCGtk *wbcg)
2312 GtkTreeViewColumn *column;
2313 GtkTreeSelection *selection;
2314 int i;
2316 state->wbcg = wbcg;
2317 state->wb = wb_control_get_workbook (GNM_WBC(wbcg));
2318 state->doc = GO_DOC (state->wb);
2319 state->metadata = go_doc_get_meta_data (wb_control_get_doc (GNM_WBC (state->wbcg)));
2321 g_return_val_if_fail (state->metadata != NULL, TRUE);
2323 state->gui = gnm_gtk_builder_load ("res:ui/doc-meta-data.ui", NULL,
2324 GO_CMD_CONTEXT (wbcg));
2326 if (state->gui == NULL)
2327 return TRUE;
2329 dialog_doc_metadata_init_widgets (state);
2331 state->view = GTK_TREE_VIEW(go_gtk_builder_get_widget (state->gui, "itemlist"));
2332 state->store = gtk_tree_store_new (NUM_COLUMNS,
2333 GDK_TYPE_PIXBUF,
2334 G_TYPE_STRING,
2335 G_TYPE_INT);
2336 gtk_tree_view_set_model (state->view, GTK_TREE_MODEL(state->store));
2337 g_object_unref (state->store);
2338 selection = gtk_tree_view_get_selection (state->view);
2339 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
2340 column = gtk_tree_view_column_new_with_attributes ("",
2341 gtk_cell_renderer_pixbuf_new (),
2342 "pixbuf", ITEM_ICON,
2343 NULL);
2344 gtk_tree_view_append_column (state->view, column);
2345 column = gtk_tree_view_column_new_with_attributes ("",
2346 gtk_cell_renderer_text_new (),
2347 "text", ITEM_NAME,
2348 NULL);
2349 gtk_tree_view_append_column (state->view, column);
2350 gtk_tree_view_set_expander_column (state->view, column);
2352 g_signal_connect (selection,
2353 "changed",
2354 G_CALLBACK (cb_dialog_doc_metadata_selection_changed), state);
2357 /* Register g_value_transform functions */
2358 g_value_register_transform_func (G_TYPE_STRING,
2359 GSF_TIMESTAMP_TYPE,
2360 dialog_doc_metadata_transform_str_to_timestamp);
2362 g_value_register_transform_func (G_TYPE_STRING,
2363 G_TYPE_FLOAT,
2364 dialog_doc_metadata_transform_str_to_float);
2366 g_value_register_transform_func (G_TYPE_STRING,
2367 GSF_DOCPROP_VECTOR_TYPE,
2368 dialog_doc_metadata_transform_str_to_docprop_vect);
2370 g_value_register_transform_func (G_TYPE_STRING,
2371 G_TYPE_BOOLEAN,
2372 dialog_doc_metadata_transform_str_to_boolean);
2374 g_value_register_transform_func (GSF_TIMESTAMP_TYPE,
2375 G_TYPE_STRING,
2376 dialog_doc_metadata_transform_timestamp_to_str);
2378 g_value_register_transform_func (GSF_DOCPROP_VECTOR_TYPE,
2379 G_TYPE_STRING,
2380 dialog_doc_metadata_transform_docprop_vect_to_str);
2382 g_value_register_transform_func (G_TYPE_FLOAT,
2383 G_TYPE_STRING,
2384 dialog_doc_metadata_transform_float_to_str);
2386 g_value_register_transform_func (G_TYPE_BOOLEAN,
2387 G_TYPE_STRING,
2388 dialog_doc_metadata_transform_boolean_to_str);
2390 for (i = 0; page_info[i].page > -1; i++) {
2391 const page_info_t *this_page = &page_info[i];
2392 this_page->page_initializer (state);
2393 dialog_doc_meta_data_add_item (state, this_page->page_name, this_page->icon_name,
2394 this_page->page, this_page->parent_path);
2397 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (state->store), ITEM_NAME, GTK_SORT_ASCENDING);
2399 /* A candidate for merging into attach guru */
2400 gnm_keyed_dialog (state->wbcg,
2401 GTK_WINDOW (state->dialog),
2402 DOC_METADATA_KEY);
2405 go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
2406 GTK_WINDOW (state->dialog));
2408 wbc_gtk_attach_guru (state->wbcg, state->dialog);
2409 g_object_set_data_full (G_OBJECT (state->dialog), "state",
2410 state, (GDestroyNotify) dialog_doc_metadata_free);
2412 /* Help and Close buttons */
2413 gnm_init_help_button (GTK_WIDGET (state->help_button),
2414 GNUMERIC_HELP_LINK_METADATA);
2416 g_signal_connect_swapped (G_OBJECT (state->close_button),
2417 "clicked",
2418 G_CALLBACK (gtk_widget_destroy),
2419 state->dialog);
2421 gtk_widget_show_all (GTK_WIDGET (state->dialog));
2423 return FALSE;
2426 /******************************************************************************
2427 * EXPORTED FUNCTIONS
2428 ******************************************************************************/
2431 * dialog_doc_metadata_new
2433 * @wbcg: WBCGtk
2435 * Creates a new instance of the dialog.
2438 void
2439 dialog_doc_metadata_new (WBCGtk *wbcg, int page)
2441 DialogDocMetaData *state;
2443 g_return_if_fail (wbcg != NULL);
2445 /* Only one guru per workbook. */
2446 if (wbc_gtk_get_guru (wbcg))
2447 return;
2449 /* Only pop up one copy per workbook */
2450 if (gnm_dialog_raise_if_exists (wbcg, DOC_METADATA_KEY))
2451 return;
2453 state = g_new0 (DialogDocMetaData, 1);
2455 if (dialog_doc_metadata_init (state, wbcg)) {
2456 go_gtk_notice_dialog (wbcg_toplevel (wbcg),
2457 GTK_MESSAGE_ERROR,
2458 _("Could not create the Properties dialog."));
2460 g_free (state);
2461 return;
2464 dialog_doc_metadata_select_page (state, page);