dialog-doc-metadata.c 69.9 KB
Newer Older
1 2 3 4
/*
 * dialog-doc-metadata.c: Edit document metadata
 *
 * Copyright (C) 2005 Jody Goldberg (jody@gnome.org)
5
 * Copyright (C) 2011 Andreas J. Guelzow (aguelzow@pyrshep.ca)
6 7
 *
 * This program is free software; you can redistribute it and/or
8 9 10
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) version 3.
11 12 13 14 15 16 17 18
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
20 21 22 23 24 25 26 27 28
 * USA
 */
#include <gnumeric-config.h>
#include <gnumeric.h>
#include "dialogs.h"
#include "help.h"

#include <workbook.h>
#include <workbook-control.h>
29
#include <wbc-gtk.h>
30
#include <workbook-view.h>
31
#include <workbook-priv.h>
32 33 34
#include <gui-util.h>
#include <parse-util.h>
#include <value.h>
35
#include <expr.h>
36
#include <commands.h>
37
#include <number-match.h>
38
#include <dead-kittens.h>
39

40 41
#include <gsf/gsf-doc-meta-data.h>
#include <gsf/gsf-meta-names.h>
42 43
#include <gsf/gsf-timestamp.h>
#include <gsf/gsf-docprop-vector.h>
44

45
#include <goffice/goffice.h>
46

47 48 49
#include <gtk/gtk.h>

#include <glib-object.h>
50
#include <glib/gi18n-lib.h>
51 52
#include <glib/gprintf.h>

53 54
#include <string.h>

55

56 57
#define DOC_METADATA_KEY "dialog-doc-metadata"

58 59 60 61 62 63 64
enum {
	ITEM_ICON,
	ITEM_NAME,
	PAGE_NUMBER,
	NUM_COLUMNS
};

65
typedef struct {
66
	GtkBuilder		*gui;
67 68
	GtkWidget		*dialog;

69
	/*pointer to the document metadata*/
70
	GsfDocMetaData		*metadata;
71

72 73
	gboolean		permissions_changed;
	GOFilePermissions	*file_permissions;
74

75
	WBCGtk	*wbcg;
76
	Workbook                *wb;
77
	GODoc			*doc;
78

79 80 81
	GtkTreeStore            *store;
	GtkTreeView             *view;

82 83 84 85 86 87 88 89 90 91 92 93 94 95
	/* Dialog Widgets */
	GtkNotebook		*notebook;
	GtkButton		*help_button;
	GtkButton		*close_button;

	/* File Information Page */
	GtkLabel		*file_name;
	GtkLabel		*location;
	GtkLabel		*created;
	GtkLabel		*modified;
	GtkLabel		*accessed;
	GtkLabel		*owner;
	GtkLabel		*group;

96
	GtkCheckButton		*owner_read;
97 98 99 100
	GtkCheckButton		*owner_write;

	GtkCheckButton		*group_read;
	GtkCheckButton		*group_write;
Morten Welinder's avatar
Morten Welinder committed
101

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
	GtkCheckButton		*others_read;
	GtkCheckButton		*others_write;

	/* Description Page */
	GtkEntry		*title;
	GtkEntry		*subject;
	GtkEntry		*author;
	GtkEntry		*manager;
	GtkEntry		*company;
	GtkEntry		*category;

	GtkTextView		*comments;

	/* Properties Page */
	GtkTreeView		*properties;
	GtkTreeStore		*properties_store;

119
	GtkEntry	        *ppt_name;
120
	GtkEntry	        *ppt_value;
121 122
	GtkComboBox		*ppt_type;
	GtkListStore            *type_store;
123
	GtkTreeModelFilter      *type_store_filter;
Morten Welinder's avatar
Morten Welinder committed
124

125 126 127
	GtkButton		*add_button;
	GtkButton		*remove_button;

128
	GtkLabel                *instruction;
129
	GtkLabel                *warning;
130

131 132 133 134 135 136
	/* Keyword Page */
	GtkTreeView             *key_tree_view;
	GtkListStore            *key_store;
	GtkButton               *key_add_button;
	GtkButton               *key_remove_button;

137 138 139 140
	/* Statistics Page */
	GtkLabel		*sheets;
	GtkLabel		*cells;
	GtkLabel		*pages;
141 142 143 144 145 146 147

	/* Calculation Page */
	GtkCheckButton		*recalc_auto;
	GtkCheckButton		*recalc_manual;
	GtkCheckButton		*recalc_iteration;
	GtkEntry		*recalc_max;
	GtkEntry		*recalc_tolerance;
148
	GtkWidget               *recalc_iteration_grid;
149

150 151
} DialogDocMetaData;

152 153
#define trim_string(s_) g_strstrip (g_strdup ((s_)))

154
static gchar *dialog_doc_metadata_get_prop_val (DialogDocMetaData *state, char const *prop_name,
155
						GValue *prop_value);
156

157 158 159 160 161
static gboolean cb_dialog_doc_metadata_ppt_changed (G_GNUC_UNUSED GtkEntry      *entry,
						    G_GNUC_UNUSED GdkEventFocus *event,
						    DialogDocMetaData *state);


162
static GType
163
dialog_doc_metadata_get_value_type_from_name (gchar const *name, GType def)
164
{
165 166 167 168 169 170 171 172 173
	/* shared by all instances and never freed */
	static GHashTable *dialog_doc_metadata_name_to_type = NULL;
	gpointer res;

	if (NULL == dialog_doc_metadata_name_to_type) {
		static struct {
			char const *name;
			GType type;
		} const map [] = {
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
			{GSF_META_NAME_GENERATOR,            G_TYPE_STRING},
			{GSF_META_NAME_INITIAL_CREATOR,      G_TYPE_STRING},
			{GSF_META_NAME_CREATOR,              G_TYPE_STRING},
			{GSF_META_NAME_TITLE,                G_TYPE_STRING},
			{GSF_META_NAME_SUBJECT,              G_TYPE_STRING},
			{GSF_META_NAME_MANAGER,              G_TYPE_STRING},
			{GSF_META_NAME_COMPANY,              G_TYPE_STRING},
			{GSF_META_NAME_CATEGORY,             G_TYPE_STRING},
			{GSF_META_NAME_DESCRIPTION,          G_TYPE_STRING},
			{GSF_META_NAME_LAST_SAVED_BY,        G_TYPE_STRING},
			{GSF_META_NAME_TEMPLATE,             G_TYPE_STRING},
			{GSF_META_NAME_EDITING_DURATION,     G_TYPE_STRING}, /* special */
			{GSF_META_NAME_SPREADSHEET_COUNT,    G_TYPE_INT},
			{GSF_META_NAME_TABLE_COUNT,          G_TYPE_INT},
			{GSF_META_NAME_CELL_COUNT,           G_TYPE_INT},
			{GSF_META_NAME_CHARACTER_COUNT,      G_TYPE_INT},
			{GSF_META_NAME_BYTE_COUNT,           G_TYPE_INT},
			{GSF_META_NAME_SECURITY,             G_TYPE_INT},
			{GSF_META_NAME_HIDDEN_SLIDE_COUNT,   G_TYPE_INT},
			{GSF_META_NAME_LINE_COUNT,           G_TYPE_INT},
			{GSF_META_NAME_SLIDE_COUNT,          G_TYPE_INT},
			{GSF_META_NAME_WORD_COUNT,           G_TYPE_INT},
			{GSF_META_NAME_MM_CLIP_COUNT,        G_TYPE_INT},
			{GSF_META_NAME_NOTE_COUNT,           G_TYPE_INT},
			{GSF_META_NAME_PARAGRAPH_COUNT,      G_TYPE_INT},
			{GSF_META_NAME_PAGE_COUNT,           G_TYPE_INT},
			{GSF_META_NAME_CODEPAGE,             G_TYPE_INT},
			{GSF_META_NAME_LOCALE_SYSTEM_DEFAULT,G_TYPE_INT},
			{GSF_META_NAME_OBJECT_COUNT,         G_TYPE_INT},
			{"xlsx:HyperlinksChanged",           G_TYPE_BOOLEAN},
			{GSF_META_NAME_LINKS_DIRTY,          G_TYPE_BOOLEAN},
			{"xlsx:SharedDoc",                   G_TYPE_BOOLEAN},
			{GSF_META_NAME_SCALE,                G_TYPE_BOOLEAN}
207 208
		};
		static char const *map_vector[] =
209 210 211
			{GSF_META_NAME_KEYWORDS,
			 GSF_META_NAME_DOCUMENT_PARTS,
			 GSF_META_NAME_HEADING_PAIRS};
212 213 214 215
		static char const *map_timestamps[] =
			{GSF_META_NAME_DATE_CREATED,
			 GSF_META_NAME_DATE_MODIFIED};

216 217
		/*Missing:GSF_META_NAME_THUMBNAIL */

218 219 220 221 222 223 224 225 226 227 228 229
		int i = G_N_ELEMENTS (map);
		dialog_doc_metadata_name_to_type = g_hash_table_new (g_str_hash, g_str_equal);
		while (i-- > 0)
			g_hash_table_insert (dialog_doc_metadata_name_to_type,
					     (gpointer)map[i].name,
					     GINT_TO_POINTER (map[i].type));

		i = G_N_ELEMENTS (map_vector);
		while (i-- > 0)
			g_hash_table_insert (dialog_doc_metadata_name_to_type,
					     (gpointer)map_vector[i],
					     GINT_TO_POINTER (GSF_DOCPROP_VECTOR_TYPE));
230

231 232 233 234 235
		i = G_N_ELEMENTS (map_timestamps);
		while (i-- > 0)
			g_hash_table_insert (dialog_doc_metadata_name_to_type,
					     (gpointer)map_timestamps[i],
					     GINT_TO_POINTER (GSF_TIMESTAMP_TYPE));
236

237
	}
238

239 240 241 242
	res = g_hash_table_lookup (dialog_doc_metadata_name_to_type, name);

	if (res != NULL)
		return GPOINTER_TO_INT (res);
243
	else
244
		return def;
245 246 247 248 249 250 251 252 253 254
}

static GType
dialog_doc_metadata_get_value_type (GValue *value)
{
	GType val_type = G_VALUE_TYPE (value);

	switch (val_type) {
	case G_TYPE_INT:
	case G_TYPE_UINT:
255 256
	case G_TYPE_FLOAT:
	case G_TYPE_DOUBLE:
257 258 259 260 261 262 263 264 265 266 267 268
	case G_TYPE_STRING:
	case G_TYPE_BOOLEAN:
		/* Just leave it as is */
		break;

	default:
		/* Check if it is a GsfDocPropVector or GsfTimeStamp */
		{
			if (VAL_IS_GSF_TIMESTAMP (value))
				val_type = GSF_TIMESTAMP_TYPE;
			else if (VAL_IS_GSF_DOCPROP_VECTOR (value))
				val_type = GSF_DOCPROP_VECTOR_TYPE;
269
			else {
270
				g_printerr ("GType %s (%i) not handled in metadata dialog.\n",
271
					    g_type_name (val_type), (int) val_type);
272
				val_type = G_TYPE_INVALID;
273
			}
274 275 276 277 278 279 280

			break;
		}
	}
	return val_type;
}

281

282
/******************************************************************************
Morten Welinder's avatar
Morten Welinder committed
283
 * G_VALUE TRANSFORM FUNCTIONS
284 285
 ******************************************************************************/

286
/*
287
 * G_TYPE_STRING TO OTHER
288
 */
289

290
static void
291 292
dialog_doc_metadata_transform_str_to_timestamp (const GValue *string_value,
						GValue       *timestamp_value)
293
{
294 295 296 297 298 299 300 301
	time_t s;
	gnm_float serial;
	gint int_serial;
	GsfTimestamp *gt;
	gchar const *str;
	GnmValue *conversion;
	GOFormat *fmt;

302 303
	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
	g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
304

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
	fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
	str = g_value_get_string (string_value);
	conversion = format_match_number (str, fmt, NULL);
	go_format_unref (fmt);
	if (conversion) {
		serial = value_get_as_float (conversion);
		value_release (conversion);

		/* Convert from Gnumeric time to unix time */
		int_serial = (int)serial;
		s = go_date_serial_to_timet (int_serial, NULL);

		if (gnm_abs (serial - int_serial) >= 1.0 || s == (time_t)-1) {
			s = time (NULL);
		} else
			s += (gnm_fake_round (3600 * 24 * (serial - int_serial)));

	} else
		s  = time (NULL);

	gt = gsf_timestamp_new ();
	gsf_timestamp_set_time (gt, s);
327
	gsf_timestamp_to_value (gt, timestamp_value);
328 329
}

330 331 332 333 334 335 336 337 338 339
static void
dialog_doc_metadata_transform_str_to_float (const GValue *string_value,
					    GValue       *float_value)
{
	gnm_float x;
	gchar const *str;
	GnmValue *conversion;

	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
	g_return_if_fail (G_VALUE_HOLDS_FLOAT (float_value));
340

341 342 343 344
	str = g_value_get_string (string_value);
	conversion = format_match_number (str, NULL, NULL);
	if (conversion) {
		x = value_get_as_float (conversion);
345
		value_release (conversion);
346 347 348 349 350 351
	} else
		x = 0.;

	g_value_set_float (float_value, x);
}

352 353 354 355 356 357 358 359 360 361
static void
dialog_doc_metadata_transform_str_to_boolean (const GValue *string_value,
					      GValue       *b_value)
{
	gboolean x, err;
	gchar const *str;
	GnmValue *conversion;

	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
	g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (b_value));
362

363 364 365 366 367 368 369 370 371 372 373 374 375
	str = g_value_get_string (string_value);
	conversion = format_match_number (str, NULL, NULL);
	if (conversion) {
		x = value_get_as_bool (conversion, &err);
		value_release (conversion);
		if (err)
			x = FALSE;
	} else
		x = FALSE;

	g_value_set_boolean (b_value, x);
}

376 377 378
static void
dialog_doc_metadata_transform_str_to_docprop_vect (const GValue *string_value,
						   GValue       *docprop_value)
379
{
380 381 382 383
	char const *str, *s;
	GsfDocPropVector *gdpv;
	GValue *value;

384 385
	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
	g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
386

387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
	gdpv = gsf_docprop_vector_new ();
	str = g_value_get_string (string_value);

	while (*str == ' ') {str++;}

	while (*str == '"') {
		char *key;
		s = str += 1;
		while (*s != '"' && *s != '\0') {
			if (*(s++) == '\\') {
				if (*s == '\0')
					goto str_done;
				s++;
			}
		}
		if (*s == '\0')
			goto str_done;
		/* s == '"' */
		key = g_strndup (str, s - str);
		value = g_new0 (GValue, 1);
		g_value_take_string (g_value_init (value, G_TYPE_STRING),
				     g_strcompress (key));
		gsf_docprop_vector_append (gdpv, value);
		g_free (key);
		str = s + 1;
		while (*str == ' ') {str++;}
		if (str[0] != ',')
			goto str_done;
		str++;
		while (*str == ' ') {str++;}
	}
 str_done:
	g_value_set_object (docprop_value, gdpv);
420
	g_object_unref (gdpv);
421
}
422

423 424 425 426 427 428
static char *
time2str (time_t t)
{
	char buffer[4000];
	gsize len;
	char const *format = "%c";
429

430 431
        if (t == -1)
                return NULL;
432

433 434 435 436 437 438 439
	len = strftime (buffer, sizeof (buffer), format, localtime (&t));
	if (len == 0)
		return NULL;

	return g_locale_to_utf8 (buffer, len, NULL, NULL, NULL);
}

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
static char *
time2str_go (time_t t)
{
	/* We need to create a format that is also parsable */
	char *str;
	GOFormat *fmt;
	gnm_float t_gnm;

	if (t == -1)
		return NULL;

	t_gnm = go_date_timet_to_serial_raw (t, NULL);

	fmt = go_format_new_from_XL ("yyyy-mm-dd hh:mm:ss");
	str = go_format_value (fmt, t_gnm);
	go_format_unref (fmt);
	return str;
}

459 460 461
/*
 * OTHER TO G_TYPE_STRING
 */
462
static void
463 464
dialog_doc_metadata_transform_timestamp_to_str (const GValue *timestamp_value,
						GValue       *string_value)
465
{
466
	GsfTimestamp const *timestamp = NULL;
467

468 469
	g_return_if_fail (VAL_IS_GSF_TIMESTAMP (timestamp_value));
	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
470

471
	timestamp = g_value_get_boxed (timestamp_value);
472
	if (timestamp != NULL)
473
		g_value_take_string (string_value,
474
				     time2str_go (timestamp->timet));
475
}
476

477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
static void
dialog_doc_metadata_transform_float_to_str (const GValue *float_value,
					    GValue       *string_value)
{
	gnm_float x;
	char *str;
	GOFormat *fmt;

	g_return_if_fail (G_VALUE_HOLDS_FLOAT (float_value));
	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));

	x = g_value_get_float (float_value);

	fmt = go_format_general ();
	str = go_format_value (fmt, x);
	g_value_take_string (string_value, str);
}

495 496 497 498 499 500 501 502 503 504 505 506 507 508
static void
dialog_doc_metadata_transform_boolean_to_str (const GValue *b_value,
					      GValue       *string_value)
{
	gboolean x;

	g_return_if_fail (G_VALUE_HOLDS_BOOLEAN (b_value));
	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));

	x = g_value_get_boolean (b_value);

	g_value_set_static_string (string_value, x ? _("TRUE") : _("FALSE"));
}

509 510 511 512 513 514 515
static gchar*
gnm_docprop_vector_as_string (GsfDocPropVector *vector)
{
	GString         *rstring;
	guint		 i;
	guint		 num_values;
	GValueArray	*gva;
516
	GValue           vl = G_VALUE_INIT;
517 518 519 520 521 522 523 524 525 526

	g_return_val_if_fail (vector != NULL, NULL);

	g_value_set_object (g_value_init (&vl, GSF_DOCPROP_VECTOR_TYPE), vector);
	gva = gsf_value_get_docprop_varray (&vl);

	g_return_val_if_fail (gva != NULL, NULL);

	num_values = gva->n_values;
	rstring = g_string_sized_new (num_values * 8);
527

528 529 530
	for (i = 0; i < num_values; i++) {
		char       *str;
		GValue	   *v;
531

532
		v = g_value_array_get_nth (gva, i);
533

534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
		if (G_VALUE_TYPE(v) == G_TYPE_STRING)
			str = g_strescape (g_value_get_string (v), "");
		else {
			char *b_str = g_strdup_value_contents (v);
			str = g_strescape (b_str, "");
			g_free (b_str);
		}
		g_string_append_c (rstring, '"');
		g_string_append (rstring, str);
		g_string_append (rstring, "\", ");
		g_free (str);
	}
	if (rstring->len > 0)
		g_string_truncate (rstring, rstring->len - 2);

	g_value_unset (&vl);

	return g_string_free (rstring, FALSE);
}

554
static void
555 556
dialog_doc_metadata_transform_docprop_vect_to_str (const GValue *docprop_value,
						   GValue       *string_value)
557
{
558
	GsfDocPropVector * docprop_vector = NULL;
559

560 561
	g_return_if_fail (VAL_IS_GSF_DOCPROP_VECTOR (docprop_value));
	g_return_if_fail (G_VALUE_HOLDS_STRING (string_value));
562

563
	docprop_vector = gsf_value_get_docprop_vector (docprop_value);
564

565 566
	if (docprop_vector != NULL)
		g_value_set_string (string_value,
567
				    gnm_docprop_vector_as_string (docprop_vector));
568
}
569

570 571 572
/******************************************************************************
 * FUNCTIONS RELATED TO 'FILE' PAGE
 ******************************************************************************/
573 574

static void
575 576
cb_dialog_doc_metadata_change_permission (GtkCheckButton    *bt,
					  DialogDocMetaData *state)
577
{
578
	g_return_if_fail (state->file_permissions != NULL);
579

580 581 582 583 584 585 586 587 588 589 590 591 592 593 594
	/* Check which button was toggled */
	if (bt == state->owner_read)
		state->file_permissions->owner_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
	else if (bt == state->owner_write)
		state->file_permissions->owner_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
	else if (bt == state->group_read)
		state->file_permissions->group_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
	else if (bt == state->group_write)
		state->file_permissions->group_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
	else if (bt == state->others_read)
		state->file_permissions->others_read = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
	else if (bt == state->others_write)
		state->file_permissions->others_write = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (bt));
	else
		return;
Morten Welinder's avatar
Morten Welinder committed
595

596
	state->permissions_changed = TRUE;
597
}
598

599
static void
600
dialog_doc_metadata_set_up_permissions (DialogDocMetaData *state)
601
{
602
	g_return_if_fail (state->metadata != NULL);
603

604 605
	state->file_permissions = go_get_file_permissions (
		go_doc_get_uri (state->doc));
606 607 608 609 610 611 612 613 614 615 616 617 618 619

	if (state->file_permissions != NULL) {
		/* Set Check buttons */

		/* Owner */
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_read),
					      state->file_permissions->owner_read);

		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->owner_write),
					      state->file_permissions->owner_write);

		/* Group */
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_read),
						state->file_permissions->group_read);
Morten Welinder's avatar
Morten Welinder committed
620

621 622 623 624 625 626 627 628 629
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->group_write),
					      state->file_permissions->group_write);

		/* Others */
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_read),
					      state->file_permissions->others_read);

		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (state->others_write),
					      state->file_permissions->others_write);
630
	}
Morten Welinder's avatar
Morten Welinder committed
631

632 633 634 635 636 637 638
	/* At this moment we don't let user change file permissions */
	gtk_widget_set_sensitive (GTK_WIDGET (state->owner_read), FALSE);
	gtk_widget_set_sensitive (GTK_WIDGET (state->owner_write), FALSE);
	gtk_widget_set_sensitive (GTK_WIDGET (state->group_read), FALSE);
	gtk_widget_set_sensitive (GTK_WIDGET (state->group_write), FALSE);
	gtk_widget_set_sensitive (GTK_WIDGET (state->others_read), FALSE);
	gtk_widget_set_sensitive (GTK_WIDGET (state->others_write), FALSE);
639 640
}

641
 /* @auto_fill : if TRUE and the text is NULL, try to set the label text with an automatic value. */
Morten Welinder's avatar
Morten Welinder committed
642
static void
643 644
dialog_doc_metadata_set_label (DialogDocMetaData *state,
			       GtkLabel          *label,
645
			       char        const *text,
646 647 648 649
			       gboolean          auto_fill)
{
	Workbook *wb = state->wb;
	gchar    *str_value = NULL;
650

651
	g_return_if_fail (label != NULL);
652

653 654
	if (text != NULL)
		str_value = g_strdup (text);
655

656 657 658
	if (str_value == NULL && auto_fill == TRUE) {
		/* File Name */
		if (label == state->file_name) {
659
			str_value = go_basename_from_uri (go_doc_get_uri (state->doc));
660
		}
661

662 663
		/* File Location */
		else if (label == state->location) {
664
			str_value = go_dirname_from_uri (go_doc_get_uri (state->doc), TRUE);
665 666 667 668 669 670 671 672 673
		}

		/* Date Created */
		else if (label == state->created) {
			/* Nothing to do ATM */
		}

		/* Date Modified */
		else if (label == state->modified) {
674
			str_value = time2str (go_file_get_date_modified (go_doc_get_uri (state->doc)));
675 676 677 678
		}

		/* Date Accessed */
		else if (label == state->accessed) {
679
			str_value = time2str (go_file_get_date_accessed (go_doc_get_uri (state->doc)));
680 681 682 683
		}

		/* Owner */
		else if (label == state->owner) {
684
			str_value = go_file_get_owner_name (go_doc_get_uri (state->doc));
685 686 687 688
		}

		/* Group */
		else if (label == state->group) {
689
			str_value = go_file_get_group_name (go_doc_get_uri (state->doc));
690 691 692 693
		}

		/* Number of Sheets */
		else if (label == state->sheets) {
694
			str_value = g_strdup_printf ("%d",  workbook_sheet_count (wb));
695
		}
Morten Welinder's avatar
Morten Welinder committed
696

697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
		/* Number of cells */
		else if (label == state->cells) {
			/* Nothing to do ATM */
		}

		/* Number of pages */
		else if (label == state->pages) {
			/* Nothing to do ATM */
		}
	}

	if (str_value != NULL) {
		gtk_label_set_text (label, str_value);
		g_free (str_value);
	} else
Morten Welinder's avatar
Morten Welinder committed
712
		gtk_label_set_text (label, _("Unknown"));
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
}

static void
dialog_doc_metadata_init_file_page (DialogDocMetaData *state)
{
	g_return_if_fail (state->metadata != NULL);

	/* Set up the labels */
	dialog_doc_metadata_set_label (state, state->file_name, NULL, TRUE);
	dialog_doc_metadata_set_label (state, state->location, NULL, TRUE);
	dialog_doc_metadata_set_label (state, state->created, NULL, TRUE);
	dialog_doc_metadata_set_label (state, state->modified, NULL, TRUE);
	dialog_doc_metadata_set_label (state, state->accessed, NULL, TRUE);
	dialog_doc_metadata_set_label (state, state->owner, NULL, TRUE);
	dialog_doc_metadata_set_label (state, state->group, NULL, TRUE);

	/* Set up check buttons */
	state->permissions_changed = FALSE;
	dialog_doc_metadata_set_up_permissions (state);

	/* Signals */

	/* Owner */
	g_signal_connect (G_OBJECT (state->owner_read),
			  "toggled",
			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
			  state);

	g_signal_connect (G_OBJECT (state->owner_write),
			  "toggled",
			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
			  state);

	/* Group */
	g_signal_connect (G_OBJECT (state->group_read),
			  "toggled",
			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
			  state);
Morten Welinder's avatar
Morten Welinder committed
751

752 753 754 755 756 757 758 759 760 761
	g_signal_connect (G_OBJECT (state->group_write),
			  "toggled",
			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
			  state);

	/* Others */
	g_signal_connect (G_OBJECT (state->others_read),
			  "toggled",
			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
			  state);
Morten Welinder's avatar
Morten Welinder committed
762

763
	g_signal_connect (G_OBJECT (state->others_write),
764 765
			  "toggled",
			  G_CALLBACK (cb_dialog_doc_metadata_change_permission),
766
			  state);
767

768
}
769

770
/******************************************************************************
Morten Welinder's avatar
Morten Welinder committed
771
 * FUNCTIONS RELATED TO 'DESCRIPTION' PAGE
772 773
 ******************************************************************************/

774
/* @activate_property : if TRUE, sets the tree view row which was added active. */
775
static void
776 777 778
dialog_doc_metadata_add_prop (DialogDocMetaData *state,
			      const gchar       *name,
			      const gchar       *value,
779
			      const gchar       *lnk,
780
			      GType              val_type)
781
{
782
	gboolean editable = (val_type != G_TYPE_INVALID)
783
		&& (val_type != GSF_DOCPROP_VECTOR_TYPE);
784 785 786
	if (value == NULL)
		value = "";

787 788
	if (lnk == NULL)
		lnk = "";
789 790

	/* Append new values in tree view */
791 792 793
	gtk_tree_store_insert_with_values (state->properties_store, NULL, NULL, G_MAXINT,
					   0, name,
					   1, value,
794
					   2, lnk,
795 796
					   3, editable,
					   4, val_type,
797
					   -1);
798 799 800 801 802 803 804 805 806 807 808 809 810 811
}

static GType
dialog_doc_metadata_get_gsf_prop_val_type (DialogDocMetaData *state,
					   const gchar       *name)
{
	GsfDocProp *prop = NULL;
	GValue     *value = NULL;

	g_return_val_if_fail (state->metadata != NULL, G_TYPE_INVALID);

	/* First we try the clean way */
	prop = gsf_doc_meta_data_lookup (state->metadata, name);

Morten Welinder's avatar
Morten Welinder committed
812
	if (prop != NULL)
813 814
		value = (GValue *) gsf_doc_prop_get_val (prop);

815 816 817 818
	if (value != NULL)
		return dialog_doc_metadata_get_value_type (value);
	else
		return dialog_doc_metadata_get_value_type_from_name (name, G_TYPE_STRING);
819 820 821
}

static void
822
dialog_doc_metadata_set_gsf_prop_val (DialogDocMetaData *state,
823 824 825
				      GValue            *prop_value,
				      const gchar       *str_val)
{
826
	GType t = G_VALUE_TYPE (prop_value);
827

828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
	/* The preinstalled transform functions do not handle simple transformations */
	/* such as from string to double, so we do that ourselves */
	switch (t) {
	case G_TYPE_STRING:
		g_value_set_string (prop_value, g_strdup (str_val));
		break;
	case G_TYPE_DOUBLE:
	case G_TYPE_FLOAT: {
		GnmParsePos pos;
		GnmValue *val = NULL;
		GnmExprTop const *texpr = NULL;
		parse_pos_init_sheet (&pos, workbook_sheet_by_index (state->wb, 0));
		parse_text_value_or_expr (&pos, str_val,
					  &val, &texpr);
		if (val != NULL) {
			gnm_float fl = value_get_as_float (val);
			value_release (val);
			if (t == G_TYPE_DOUBLE)
				g_value_set_double (prop_value, fl);
			else
				g_value_set_float (prop_value, fl);
		}
		if (texpr)
			gnm_expr_top_unref (texpr);
		break;
	}
	default:
		if (g_value_type_transformable (t, G_TYPE_STRING)) {
			GValue string_value = G_VALUE_INIT;
			g_value_init (&string_value, G_TYPE_STRING);
			g_value_set_string (&string_value, g_strdup (str_val));
			g_value_transform (&string_value, prop_value);
			g_value_unset (&string_value);
		} else
862
			g_printerr (_("Transform function of G_TYPE_STRING to %s is required!\n"),
863 864 865
				    g_type_name (t));
		break;
	}
866 867 868 869
}

/**
 * dialog_doc_metadata_set_gsf_prop
870 871 872
 * @state: dialog main struct
 * @name: property name
 * @value: property value
873
 * @lnk: property linked to
874 875 876 877
 *
 * Sets a new value to the property in the GsfDocMetaData struct
 *
 **/
878
static GType
879 880 881
dialog_doc_metadata_set_gsf_prop (DialogDocMetaData *state,
				  const gchar       *name,
				  const gchar       *value,
882
				  const gchar       *lnk,
883
				  GType              type)
884
{
885
	GsfDocProp *existing_prop = NULL;
886
	GsfDocProp *doc_prop;
887 888
	GValue     *existing_value = NULL;
	char const *existing_link  = NULL;
889
	GType       val_type;
890 891 892 893 894 895 896

	existing_prop = gsf_doc_meta_data_lookup (state->metadata, name);
	if (existing_prop != NULL) {
		existing_value = (GValue *) gsf_doc_prop_get_val (existing_prop);
		existing_link  = gsf_doc_prop_get_link (existing_prop);
	}

897 898
	if (lnk != NULL && *lnk == 0)
		lnk = NULL;
899 900
	if (value != NULL && *value == 0)
		value = NULL;
901
	if ((value == NULL) && (lnk == NULL)) {
902
		if ((existing_prop == NULL) ||
903 904
		    ((existing_value == NULL) && (existing_link == NULL)))
			return G_TYPE_INVALID;
905
		else {
Morten Welinder's avatar
Morten Welinder committed
906
			cmd_change_meta_data (GNM_WBC (state->wbcg), NULL,
907
					      g_slist_prepend (NULL, g_strdup (name)));
908
			return G_TYPE_INVALID;
909 910 911 912 913 914
		}
	}

	if (existing_prop != NULL) {
		gboolean    link_changed;
		gboolean    value_changed = TRUE;
Morten Welinder's avatar
Morten Welinder committed
915

916 917
		if (existing_link!= NULL && *existing_link == 0)
			existing_link = NULL;
918
		if (lnk == existing_link)
919
			link_changed = FALSE;
920
		else if (lnk == NULL || existing_link == NULL)
921 922
			link_changed = TRUE;
		else
923
			link_changed = (0 != strcmp (lnk, existing_link));
924

925
		if (existing_value == NULL)
926
			value_changed = (value != NULL);
927
		else if (G_VALUE_HOLDS_STRING (existing_value) && (type == 0 || type == G_TYPE_STRING)) {
928
			char const * existing_val_str = g_value_get_string (existing_value);
929 930 931 932 933 934 935 936
			if (existing_val_str != NULL && *existing_val_str == 0)
				existing_val_str = NULL;
			if (value == existing_val_str)
				value_changed = FALSE;
			else if (value == NULL || existing_val_str == NULL)
				value_changed = TRUE;
			else
				value_changed = (0 != strcmp (value, existing_val_str));
937
			if (!link_changed && !value_changed)
938
				return G_TYPE_STRING;
939 940 941
		}
	}

Morten Welinder's avatar
Morten Welinder committed
942

943 944 945
	/* Create a new GsfDocProp */
	doc_prop = gsf_doc_prop_new (g_strdup (name));

946 947 948 949
	if (type == 0)
		val_type = dialog_doc_metadata_get_gsf_prop_val_type (state, name);
	else
		val_type = type;
950 951

	if (val_type != G_TYPE_INVALID) {
952
		GValue     *doc_prop_value;
Morten Welinder's avatar
Morten Welinder committed
953

954 955 956
		/* Create a new Value */
		doc_prop_value = g_new0 (GValue, 1);

957 958 959 960 961
		g_value_init (doc_prop_value, val_type);
		dialog_doc_metadata_set_gsf_prop_val (state, doc_prop_value, value);
		gsf_doc_prop_set_val (doc_prop, doc_prop_value);
	}

962 963
	if (lnk != NULL)
		gsf_doc_prop_set_link (doc_prop, g_strdup (lnk));
964

Morten Welinder's avatar
Morten Welinder committed
965
	cmd_change_meta_data (GNM_WBC (state->wbcg),
966
			      g_slist_prepend (NULL, doc_prop), NULL);
967 968

	return val_type;
969 970 971 972
}

/**
 * dialog_doc_metadata_set_prop
973 974 975
 * @state: dialog main struct
 * @prop_name: property name
 * @prop_value: property value
976 977 978 979 980 981 982 983
 *
 * Tries to update the property value in all the dialog and in the GsfDocMetaData
 * struct. If the property was not found, creates a new one.
 *
 **/
static void
dialog_doc_metadata_set_prop (DialogDocMetaData *state,
			      const gchar       *prop_name,
Morten Welinder's avatar
Morten Welinder committed
984
			      const gchar       *prop_value,
985 986
			      const gchar       *link_value,
			      GType type)
987 988 989
{
	GtkTreeIter tree_iter;
	GValue      *value;
990 991
	gboolean    ret;
	gboolean    found;
992 993 994
	GType       val_type;
	GsfDocProp *updated_prop;
	gchar      *new_prop_value = NULL;
Morten Welinder's avatar
Morten Welinder committed
995

996
	g_return_if_fail (state->metadata != NULL);
997

998
	val_type = dialog_doc_metadata_set_gsf_prop (state, prop_name, prop_value,
999
						     link_value, type);
1000 1001 1002 1003 1004 1005

	/* Due to changes in type, prop_value may have changed */
	updated_prop = gsf_doc_meta_data_lookup (state->metadata, prop_name);
	if (updated_prop != NULL) {
		GValue *new_value = (GValue *) gsf_doc_prop_get_val (updated_prop);
		if (new_value != NULL)
1006
			new_prop_value = dialog_doc_metadata_get_prop_val (state, prop_name, new_value);
1007 1008
		if (new_prop_value == NULL)
			new_prop_value = g_strdup ("");
1009 1010
	}

1011
	found = FALSE;
1012

1013
	/* Search tree view for property */
1014
	value = g_new0 (GValue, 1);
1015 1016 1017 1018 1019

	ret = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->properties_store),
					     &tree_iter);

	while (ret == TRUE) {
Morten Welinder's avatar
Morten Welinder committed
1020

1021 1022 1023
		gtk_tree_model_get_value (GTK_TREE_MODEL (state->properties_store),
					  &tree_iter,
					  0,
1024
					  value);
1025

1026
		if (strcmp (prop_name, g_value_get_string (value)) == 0) {
1027
			if (updated_prop != NULL) {
1028
				/* Set new value */
Morten Welinder's avatar
Morten Welinder committed
1029
				gtk_tree_store_set (state->properties_store,
1030
						    &tree_iter,
1031 1032
						    1, new_prop_value,
					    -1);
1033

1034 1035 1036 1037 1038 1039 1040 1041 1042
				if (link_value != NULL)
					gtk_tree_store_set (state->properties_store,
							    &tree_iter,
							    2, link_value,
							    -1);
			} else
				/* Clear value */
				gtk_tree_store_remove (state->properties_store,
						       &tree_iter);
1043 1044

			g_value_unset (value);
1045 1046 1047 1048 1049

			found = TRUE;
			break;
		}

1050
		g_value_unset (value);
1051 1052 1053 1054
		ret = gtk_tree_model_iter_next (GTK_TREE_MODEL (state->properties_store),
						&tree_iter);
	}

1055
	if (val_type != G_TYPE_INVALID && found == FALSE)
1056
		dialog_doc_metadata_add_prop (state, prop_name, new_prop_value, "", val_type);
1057 1058

	/* Free all data */
1059
	g_free (value);
1060
	g_free (new_prop_value );
1061
}
1062

1063
/*
Morten Welinder's avatar
Morten Welinder committed
1064
 * CALLBACKS for 'Description' page entries
1065
 */
1066
static gboolean
1067
cb_dialog_doc_metadata_title_changed (GtkEntry          *entry,
1068
				      G_GNUC_UNUSED GdkEventFocus *event,
1069 1070
				      DialogDocMetaData *state)
{
1071
	dialog_doc_metadata_set_prop (state,
1072
				      GSF_META_NAME_TITLE,
1073
				      gtk_entry_get_text (entry),
1074
				      NULL, G_TYPE_STRING);
1075
	return FALSE;
1076 1077
}

1078
static gboolean
1079
cb_dialog_doc_metadata_subject_changed (GtkEntry          *entry,
1080
					G_GNUC_UNUSED GdkEventFocus *event,
1081 1082
					DialogDocMetaData *state)
{
1083 1084 1085
	dialog_doc_metadata_set_prop (state,
				      GSF_META_NAME_SUBJECT,
				      gtk_entry_get_text (entry),
1086
				      NULL, G_TYPE_STRING);
1087
	return FALSE;
1088 1089
}

1090
static gboolean
1091
cb_dialog_doc_metadata_author_changed (GtkEntry          *entry,
1092
				       G_GNUC_UNUSED GdkEventFocus *event,
1093 1094
				       DialogDocMetaData *state)
{
1095 1096 1097
	dialog_doc_metadata_set_prop (state,
				      GSF_META_NAME_INITIAL_CREATOR,
				      gtk_entry_get_text (entry),
1098
				      NULL,  G_TYPE_STRING);
1099
	return FALSE;
1100 1101
}

1102
static gboolean
1103
cb_dialog_doc_metadata_manager_changed (GtkEntry          *entry,
1104
					G_GNUC_UNUSED GdkEventFocus *event,
1105 1106
					DialogDocMetaData *state)
{
1107 1108 1109
	dialog_doc_metadata_set_prop (state,
				      GSF_META_NAME_MANAGER,
				      gtk_entry_get_text (entry),
1110
				      NULL,  G_TYPE_STRING);
1111
	return FALSE;
1112 1113
}

1114
static gboolean
1115
cb_dialog_doc_metadata_company_changed (GtkEntry          *entry,
1116
					G_GNUC_UNUSED GdkEventFocus *event,
1117 1118
					DialogDocMetaData *state)
{
1119 1120 1121
	dialog_doc_metadata_set_prop (state,
				      GSF_META_NAME_COMPANY,
				      gtk_entry_get_text (entry),
1122
				      NULL,  G_TYPE_STRING);
1123
	return FALSE;
1124 1125
}

1126
static gboolean
1127
cb_dialog_doc_metadata_category_changed (GtkEntry          *entry,
1128
					 G_GNUC_UNUSED GdkEventFocus *event,
1129 1130
					 DialogDocMetaData *state)
{
1131 1132 1133
	dialog_doc_metadata_set_prop (state,
				      GSF_META_NAME_CATEGORY,
				      gtk_entry_get_text (entry),
1134
				      NULL, G_TYPE_STRING );
1135
	return FALSE;
1136 1137
}

1138 1139
static gboolean
cb_dialog_doc_metadata_comments_changed (GtkTextView     *view,
1140
					 G_GNUC_UNUSED GdkEventFocus *event,
1141 1142 1143 1144 1145
					 DialogDocMetaData *state)
{
	GtkTextIter start_iter;
	GtkTextIter end_iter;
	gchar *text;
1146
	GtkTextBuffer     *buffer = gtk_text_view_get_buffer (view);
1147 1148 1149 1150 1151 1152 1153 1154 1155

	gtk_text_buffer_get_start_iter (buffer, &start_iter);
	gtk_text_buffer_get_end_iter   (buffer, &end_iter);

	text = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, TRUE);

	dialog_doc_metadata_set_prop (state,
				      GSF_META_NAME_DESCRIPTION,
				      text,
1156
				      NULL,  G_TYPE_STRING);
1157
	return FALSE;
1158 1159
}

1160 1161
/**
 * dialog_doc_metadata_init_description_page
1162
 * @state: dialog main struct
1163 1164 1165 1166
 *
 * Initializes the widgets and signals for the 'Description' page.
 *
 **/
1167
static void
1168
dialog_doc_metadata_init_description_page (DialogDocMetaData *state)
1169
{
1170 1171
	g_return_if_fail (state->metadata != NULL);

1172 1173 1174 1175
	/* At this point, the entry values were already filled */

	/* Set up the signals */
	g_signal_connect (G_OBJECT (state->title),
1176
			  "focus-out-event",
1177 1178 1179 1180
			  G_CALLBACK (cb_dialog_doc_metadata_title_changed),
			  state);

	g_signal_connect (G_OBJECT (state->subject),
1181
			  "focus-out-event",
1182 1183 1184 1185
			  G_CALLBACK (cb_dialog_doc_metadata_subject_changed),
			  state);

	g_signal_connect (G_OBJECT (state->author),
1186
			  "focus-out-event",
1187 1188 1189 1190
			  G_CALLBACK (cb_dialog_doc_metadata_author_changed),
			  state);

	g_signal_connect (G_OBJECT (state->manager),
1191
			  "focus-out-event",
1192 1193 1194 1195
			  G_CALLBACK (cb_dialog_doc_metadata_manager_changed),
			  state);

	g_signal_connect (G_OBJECT (state->company),
1196
			  "focus-out-event",
1197 1198 1199 1200
			  G_CALLBACK (cb_dialog_doc_metadata_company_changed),
			  state);

	g_signal_connect (G_OBJECT (state->category),
1201
			  "focus-out-event",
1202 1203 1204
			  G_CALLBACK (cb_dialog_doc_metadata_category_changed),
			  state);

1205 1206
	g_signal_connect (G_OBJECT (state->comments),
			  "focus-out-event",
1207 1208
			  G_CALLBACK (cb_dialog_doc_metadata_comments_changed),
			  state);
1209
}
1210

1211 1212 1213 1214 1215 1216 1217
/******************************************************************************
 * FUNCTIONS RELATED TO 'KEYWORDS' PAGE
 ******************************************************************************/

static void
dialog_doc_metadata_update_keywords_changed (DialogDocMetaData *state)
{
1218
	GValue val = G_VALUE_INIT;
1219 1220 1221 1222 1223 1224 1225 1226 1227
	GtkTreeIter iter;
	GsfDocPropVector *vector = gsf_docprop_vector_new ();

	g_value_init (&val, GSF_DOCPROP_VECTOR_TYPE);

	if (gtk_tree_model_get_iter_first
	    (GTK_TREE_MODEL (state->key_store), &iter)) {
		do {
			GValue *value = g_new0 (GValue, 1);
1228
			gtk_tree_model_get_value
1229 1230 1231 1232 1233
				(GTK_TREE_MODEL (state->key_store), &iter,
				 0, value);
			gsf_docprop_vector_append (vector, value);
			g_value_unset (value);
			g_free (value);
1234
		} while (gtk_tree_model_iter_next
1235 1236 1237
			 (GTK_TREE_MODEL (state->key_store), &iter));
	}
	g_value_set_object (&val, vector);
1238
	g_object_unref (vector);
1239

1240 1241 1242
	dialog_doc_metadata_set_prop
		(state, GSF_META_NAME_KEYWORDS,
		 dialog_doc_metadata_get_prop_val (state, GSF_META_NAME_KEYWORDS, &val),
1243
		 NULL, GSF_DOCPROP_VECTOR_TYPE);
1244 1245 1246 1247 1248 1249 1250 1251

	g_value_unset (&val);
}

static void
cb_dialog_doc_metadata_keywords_sel_changed (GtkTreeSelection *treeselection,
					     DialogDocMetaData *state)
{
1252 1253
	gtk_widget_set_sensitive
		(GTK_WIDGET (state->key_remove_button),
1254
		 gtk_tree_selection_get_selected (treeselection, NULL, NULL));
1255 1256 1257 1258 1259 1260
}

static void
dialog_doc_metadata_update_keyword_list (DialogDocMetaData *state, GsfDocProp *prop)
{
	guint i;
1261
	GtkTreeSelection *sel;
1262 1263 1264 1265 1266 1267 1268 1269 1270

	gtk_list_store_clear (state->key_store);

	if (prop != NULL) {
		GValueArray *array;
		array = gsf_value_get_docprop_varray (gsf_doc_prop_get_val (prop));
		if (array != NULL) {
			for (i = 0; i < array->n_values; i++) {
				GValue *val = g_value_array_get_nth (array, i);
1271 1272
				gtk_list_store_insert_with_values
					(state->key_store, NULL, G_MAXINT,
1273 1274 1275 1276 1277 1278 1279 1280 1281
					 0, g_value_get_string (val), -1);
			}
		}
	}

	sel = gtk_tree_view_get_selection (state->key_tree_view);
	cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
}

1282
static void
1283 1284
cb_dialog_doc_metadata_keywords_add_clicked (G_GNUC_UNUSED GtkWidget *w,
					     DialogDocMetaData       *state)
1285
{
1286
	gtk_list_store_insert_with_values (state->key_store, NULL, G_MAXINT,
1287 1288 1289 1290
					   0, "<?>", -1);
	dialog_doc_metadata_update_keywords_changed (state);
}

1291
static void
1292 1293
cb_dialog_doc_metadata_keywords_remove_clicked (G_GNUC_UNUSED GtkWidget *w,
						DialogDocMetaData       *state)
1294 1295 1296
{
	GtkTreeIter iter;
	GtkTreeSelection *sel = gtk_tree_view_get_selection (state->key_tree_view);
1297

1298 1299 1300 1301 1302 1303 1304
	if (gtk_tree_selection_get_selected (sel, NULL, &iter)) {
		gtk_list_store_remove (state->key_store, &iter);
		dialog_doc_metadata_update_keywords_changed (state);
	}
}

static void
1305 1306 1307 1308
cb_dialog_doc_metadata_keyword_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
				       gchar                             *path,
				       gchar                             *new_text,
				       DialogDocMetaData                 *state)
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319
{
	GtkTreeIter iter;
	if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (state->key_store), &iter, path)) {
		gtk_list_store_set (state->key_store, &iter, 0, new_text, -1);
		dialog_doc_metadata_update_keywords_changed (state);
	}
}


/**
 * dialog_doc_metadata_init_keywords_page
1320
 * @state: dialog main struct
1321 1322 1323 1324 1325 1326 1327 1328 1329 1330
 *
 * Initializes the widgets and signals for the 'Description' page.
 *
 **/
static void
dialog_doc_metadata_init_keywords_page (DialogDocMetaData *state)
{
	GtkTreeViewColumn *column;
	GtkCellRenderer   *renderer;
	GtkTreeSelection *sel;
1331

1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349
	g_return_if_fail (state->metadata != NULL);

	renderer = gtk_cell_renderer_text_new ();
	g_object_set (G_OBJECT (renderer), "editable", TRUE, NULL);
	column = gtk_tree_view_column_new_with_attributes (_("Keywords"),
							   renderer,
							   "text", 0,
							   NULL);
	gtk_tree_view_insert_column (state->key_tree_view, column, -1);

	gtk_widget_set_sensitive (GTK_WIDGET (state->key_add_button), TRUE);
	gtk_widget_set_sensitive (GTK_WIDGET (state->key_remove_button), FALSE);

	sel = gtk_tree_view_get_selection (state->key_tree_view);
	g_signal_connect (G_OBJECT (sel),
			  "changed",
			  G_CALLBACK (cb_dialog_doc_metadata_keywords_sel_changed),
			  state);
1350

1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
	g_signal_connect (G_OBJECT (state->key_add_button),
			  "clicked",
			  G_CALLBACK (cb_dialog_doc_metadata_keywords_add_clicked),
			  state);
	g_signal_connect (G_OBJECT (state->key_remove_button),
			  "clicked",
			  G_CALLBACK (cb_dialog_doc_metadata_keywords_remove_clicked),
			  state);
	g_signal_connect (G_OBJECT (renderer),
			  "edited",
			  G_CALLBACK (cb_dialog_doc_metadata_keyword_edited),
			  state);

	cb_dialog_doc_metadata_keywords_sel_changed (sel, state);
}

1367 1368 1369 1370
/******************************************************************************
 * FUNCTIONS RELATED TO 'PROPERTIES' PAGE
 ******************************************************************************/

1371
static void
1372 1373 1374 1375
cb_dialog_doc_metadata_value_edited (G_GNUC_UNUSED GtkCellRendererText *renderer,
				     gchar                             *path,
				     gchar                             *new_text,
				     DialogDocMetaData                 *state)
1376 1377
{
	GtkTreeIter iter;
1378
	if (gtk_tree_model_get_iter_from_string
1379 1380 1381
	    (GTK_TREE_MODEL (state->properties_store), &iter, path)) {
		gchar       *prop_name;
		gchar       *link_value;
1382
		GType        type;
1383

1384 1385 1386 1387
		gtk_tree_model_get (GTK_TREE_MODEL (state->properties_store),
				    &iter,
				    0, &prop_name,
				    2, &link_value,
1388
				    4, &type,
1389
				    -1);
1390
		dialog_doc_metadata_set_prop (state, prop_name, new_text, link_value, type);
1391 1392 1393 1394 1395
		g_free (prop_name);
		g_free (link_value);
	}
}

1396 1397
/**
 * cb_dialog_doc_metadata_add_clicked
1398 1399
 * @w: widget
 * @state: dialog main struct
1400 1401 1402 1403
 *
 * Adds a new "empty" property to the tree view.
 *
 **/
1404
static void