evolution-3.6.4/widgets/table/e-table-config.c

No issues found

   1 /*
   2  * This program is free software; you can redistribute it and/or
   3  * modify it under the terms of the GNU Lesser General Public
   4  * License as published by the Free Software Foundation; either
   5  * version 2 of the License, or (at your option) version 3.
   6  *
   7  * This program is distributed in the hope that it will be useful,
   8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  10  * Lesser General Public License for more details.
  11  *
  12  * You should have received a copy of the GNU Lesser General Public
  13  * License along with the program; if not, see <http://www.gnu.org/licenses/>
  14  *
  15  *
  16  * Authors:
  17  *		Chris Lahey <clahey@ximian.com>
  18  *		Miguel de Icaza <miguel@ximian.com>
  19  *
  20  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  21  *
  22  */
  23 
  24 /*
  25  * FIXME:
  26  *    Sort Dialog: when text is selected, the toggle button switches state.
  27  *    Make Clear all work.
  28  */
  29 
  30 #ifdef HAVE_CONFIG_H
  31 #include <config.h>
  32 #endif
  33 
  34 #include <stdlib.h>
  35 #include <string.h>
  36 
  37 #include <gtk/gtk.h>
  38 
  39 #include <glib/gi18n.h>
  40 #include "e-util/e-util-private.h"
  41 #include "e-util/e-util.h"
  42 #include "e-util/e-unicode.h"
  43 
  44 #include "e-table-config.h"
  45 #include "e-table-memory-store.h"
  46 #include "e-table-without.h"
  47 
  48 G_DEFINE_TYPE (ETableConfig, e_table_config, G_TYPE_OBJECT)
  49 
  50 enum {
  51 	CHANGED,
  52 	LAST_SIGNAL
  53 };
  54 
  55 enum {
  56 	PROP_0,
  57 	PROP_STATE
  58 };
  59 
  60 enum {
  61 	COLUMN_ITEM,
  62 	COLUMN_VALUE
  63 };
  64 
  65 static guint e_table_config_signals[LAST_SIGNAL] = { 0, };
  66 
  67 static void
  68 config_finalize (GObject *object)
  69 {
  70 	ETableConfig *config = E_TABLE_CONFIG (object);
  71 
  72 	if (config->state)
  73 		g_object_unref (config->state);
  74 	config->state = NULL;
  75 
  76 	if (config->source_state)
  77 		g_object_unref (config->source_state);
  78 	config->source_state = NULL;
  79 
  80 	if (config->source_spec)
  81 		g_object_unref (config->source_spec);
  82 	config->source_spec = NULL;
  83 
  84 	g_free (config->header);
  85 	config->header = NULL;
  86 
  87 	g_slist_free (config->column_names);
  88 	config->column_names = NULL;
  89 
  90 	g_free (config->domain);
  91 	config->domain = NULL;
  92 
  93 	G_OBJECT_CLASS (e_table_config_parent_class)->finalize (object);
  94 }
  95 
  96 static void
  97 e_table_config_changed (ETableConfig *config,
  98                         ETableState *state)
  99 {
 100 	g_return_if_fail (E_IS_TABLE_CONFIG (config));
 101 
 102 	g_signal_emit (config, e_table_config_signals[CHANGED], 0, state);
 103 }
 104 
 105 static void
 106 config_dialog_changed (ETableConfig *config)
 107 {
 108 	/* enable the apply/ok buttons */
 109 	gtk_dialog_set_response_sensitive (
 110 		GTK_DIALOG (config->dialog_toplevel),
 111 		GTK_RESPONSE_APPLY, TRUE);
 112 	gtk_dialog_set_response_sensitive (
 113 		GTK_DIALOG (config->dialog_toplevel),
 114 		GTK_RESPONSE_OK, TRUE);
 115 }
 116 
 117 static void
 118 config_get_property (GObject *object,
 119                      guint property_id,
 120                      GValue *value,
 121                      GParamSpec *pspec)
 122 {
 123 	ETableConfig *config = E_TABLE_CONFIG (object);
 124 
 125 	switch (property_id) {
 126 	case PROP_STATE:
 127 		g_value_set_object (value, config->state);
 128 		break;
 129 	default:
 130 		break;
 131 	}
 132 }
 133 
 134 static void
 135 e_table_config_class_init (ETableConfigClass *class)
 136 {
 137 	GObjectClass *object_class = G_OBJECT_CLASS (class);
 138 
 139 	class->changed        = NULL;
 140 
 141 	object_class->finalize = config_finalize;
 142 	object_class->get_property = config_get_property;
 143 
 144 	e_table_config_signals[CHANGED] = g_signal_new (
 145 		"changed",
 146 		G_TYPE_FROM_CLASS (object_class),
 147 		G_SIGNAL_RUN_LAST,
 148 		G_STRUCT_OFFSET (ETableConfigClass, changed),
 149 		(GSignalAccumulator) NULL, NULL,
 150 		g_cclosure_marshal_VOID__VOID,
 151 		G_TYPE_NONE, 0);
 152 
 153 	g_object_class_install_property (
 154 		object_class,
 155 		PROP_STATE,
 156 		g_param_spec_object (
 157 			"state",
 158 			"State",
 159 			NULL,
 160 			E_TYPE_TABLE_STATE,
 161 			G_PARAM_READABLE));
 162 }
 163 
 164 static void
 165 configure_combo_box_add (GtkComboBox *combo_box,
 166                          const gchar *item,
 167                          const gchar *value)
 168 {
 169 	GtkTreeRowReference *reference;
 170 	GtkTreeModel *model;
 171 	GtkTreePath *path;
 172 	GHashTable *index;
 173 	GtkTreeIter iter;
 174 
 175 	model = gtk_combo_box_get_model (combo_box);
 176 	gtk_list_store_append (GTK_LIST_STORE (model), &iter);
 177 
 178 	gtk_list_store_set (
 179 		GTK_LIST_STORE (model), &iter,
 180 		COLUMN_ITEM, item, COLUMN_VALUE, value, -1);
 181 
 182 	index = g_object_get_data (G_OBJECT (combo_box), "index");
 183 	g_return_if_fail (index != NULL);
 184 
 185 	/* Add an entry to the tree model index. */
 186 	path = gtk_tree_model_get_path (model, &iter);
 187 	reference = gtk_tree_row_reference_new (model, path);
 188 	g_return_if_fail (reference != NULL);
 189 	g_hash_table_insert (index, g_strdup (value), reference);
 190 	gtk_tree_path_free (path);
 191 }
 192 
 193 static gchar *
 194 configure_combo_box_get_active (GtkComboBox *combo_box)
 195 {
 196 	GtkTreeIter iter;
 197 	gchar *value = NULL;
 198 
 199 	if (gtk_combo_box_get_active_iter (combo_box, &iter))
 200 		gtk_tree_model_get (
 201 			gtk_combo_box_get_model (combo_box), &iter,
 202 			COLUMN_VALUE, &value, -1);
 203 
 204 	if (value != NULL && *value == '\0') {
 205 		g_free (value);
 206 		value = NULL;
 207 	}
 208 
 209 	return value;
 210 }
 211 
 212 static void
 213 configure_combo_box_set_active (GtkComboBox *combo_box,
 214                                 const gchar *value)
 215 {
 216 	GtkTreeRowReference *reference;
 217 	GHashTable *index;
 218 
 219 	index = g_object_get_data (G_OBJECT (combo_box), "index");
 220 	g_return_if_fail (index != NULL);
 221 
 222 	reference = g_hash_table_lookup (index, value);
 223 	if (reference != NULL) {
 224 		GtkTreeModel *model;
 225 		GtkTreePath *path;
 226 		GtkTreeIter iter;
 227 
 228 		model = gtk_tree_row_reference_get_model (reference);
 229 		path = gtk_tree_row_reference_get_path (reference);
 230 
 231 		if (path == NULL)
 232 			return;
 233 
 234 		if (gtk_tree_model_get_iter (model, &iter, path))
 235 			gtk_combo_box_set_active_iter (combo_box, &iter);
 236 
 237 		gtk_tree_path_free (path);
 238 	}
 239 }
 240 
 241 static ETableColumnSpecification *
 242 find_column_in_spec (ETableSpecification *spec,
 243                      gint model_col)
 244 {
 245 	ETableColumnSpecification **column;
 246 
 247 	for (column = spec->columns; *column; column++) {
 248 		if ((*column)->disabled)
 249 			continue;
 250 		if ((*column)->model_col != model_col)
 251 			continue;
 252 
 253 		return *column;
 254 	}
 255 
 256 	return NULL;
 257 }
 258 
 259 static gint
 260 find_model_column_by_name (ETableSpecification *spec,
 261                            const gchar *s)
 262 {
 263 	ETableColumnSpecification **column;
 264 
 265 	for (column = spec->columns; *column; column++) {
 266 
 267 		if ((*column)->disabled)
 268 			continue;
 269 		if (g_ascii_strcasecmp ((*column)->title, s) == 0)
 270 			return (*column)->model_col;
 271 	}
 272 	return -1;
 273 }
 274 
 275 static void
 276 update_sort_and_group_config_dialog (ETableConfig *config,
 277                                      gboolean is_sort)
 278 {
 279 	ETableConfigSortWidgets *widgets;
 280 	gint count, i;
 281 
 282 	if (is_sort) {
 283 		count = e_table_sort_info_sorting_get_count (
 284 			config->temp_state->sort_info);
 285 		widgets = &config->sort[0];
 286 	} else {
 287 		count = e_table_sort_info_grouping_get_count (
 288 			config->temp_state->sort_info);
 289 		widgets = &config->group[0];
 290 	}
 291 
 292 	for (i = 0; i < 4; i++) {
 293 		gboolean sensitive = (i <= count);
 294 		const gchar *text = "";
 295 
 296 		gtk_widget_set_sensitive (widgets[i].frames, sensitive);
 297 
 298 		/*
 299 		 * Sorting is set, auto select the text
 300 		 */
 301 		g_signal_handler_block (
 302 			widgets[i].radio_ascending,
 303 			widgets[i].toggled_id);
 304 		g_signal_handler_block (
 305 			widgets[i].combo,
 306 			widgets[i].changed_id);
 307 
 308 		if (i < count) {
 309 			GtkToggleButton *a, *d;
 310 			ETableSortColumn col =
 311 				is_sort
 312 				? e_table_sort_info_sorting_get_nth (
 313 					config->temp_state->sort_info,
 314 					i)
 315 				:  e_table_sort_info_grouping_get_nth (
 316 					config->temp_state->sort_info,
 317 					i);
 318 
 319 			ETableColumnSpecification *column =
 320 				find_column_in_spec (config->source_spec, col.column);
 321 
 322 			if (!column) {
 323 				/*
 324 				 * This is a bug in the programmer
 325 				 * stuff, but by the time we arrive
 326 				 * here, the user has been given a
 327 				 * warning
 328 				 */
 329 				continue;
 330 			}
 331 
 332 			text = column->title;
 333 
 334 			/*
 335 			 * Update radio buttons
 336 			 */
 337 			a = GTK_TOGGLE_BUTTON (
 338 				widgets[i].radio_ascending);
 339 			d = GTK_TOGGLE_BUTTON (
 340 				widgets[i].radio_descending);
 341 
 342 			gtk_toggle_button_set_active (col.ascending ? a : d, 1);
 343 		} else {
 344 			GtkToggleButton *t;
 345 
 346 			t = GTK_TOGGLE_BUTTON (
 347 				widgets[i].radio_ascending);
 348 
 349 			if (is_sort)
 350 				g_return_if_fail (
 351 					widgets[i].radio_ascending !=
 352 					config->group[i].radio_ascending);
 353 			else
 354 				g_return_if_fail (
 355 					widgets[i].radio_ascending !=
 356 					config->sort[i].radio_ascending);
 357 			gtk_toggle_button_set_active (t, 1);
 358 		}
 359 
 360 		/* Set the text */
 361 		configure_combo_box_set_active (
 362 			GTK_COMBO_BOX (widgets[i].combo), text);
 363 
 364 		g_signal_handler_unblock (
 365 			widgets[i].radio_ascending,
 366 			widgets[i].toggled_id);
 367 		g_signal_handler_unblock (
 368 			widgets[i].combo,
 369 			widgets[i].changed_id);
 370 	}
 371 }
 372 
 373 static void
 374 config_sort_info_update (ETableConfig *config)
 375 {
 376 	ETableSortInfo *info = config->state->sort_info;
 377 	GString *res;
 378 	gint count, i;
 379 
 380 	count = e_table_sort_info_sorting_get_count (info);
 381 	res = g_string_new ("");
 382 
 383 	for (i = 0; i < count; i++) {
 384 		ETableSortColumn col = e_table_sort_info_sorting_get_nth (info, i);
 385 		ETableColumnSpecification *column;
 386 
 387 		column = find_column_in_spec (config->source_spec, col.column);
 388 		if (!column) {
 389 			g_warning ("Could not find column model in specification");
 390 			continue;
 391 		}
 392 
 393 		g_string_append (res, dgettext (config->domain, (column)->title));
 394 		g_string_append_c (res, ' ');
 395 		g_string_append (
 396 			res,
 397 			col.ascending ?
 398 			_("(Ascending)") : _("(Descending)"));
 399 
 400 		if ((i + 1) != count)
 401 			g_string_append (res, ", ");
 402 	}
 403 
 404 	if (res->str[0] == 0)
 405 		g_string_append (res, _("Not sorted"));
 406 
 407 	gtk_label_set_text (GTK_LABEL (config->sort_label), res->str);
 408 
 409 	g_string_free (res, TRUE);
 410 }
 411 
 412 static void
 413 config_group_info_update (ETableConfig *config)
 414 {
 415 	ETableSortInfo *info = config->state->sort_info;
 416 	GString *res;
 417 	gint count, i;
 418 
 419 	if (!e_table_sort_info_get_can_group (info))
 420 		return;
 421 
 422 	count = e_table_sort_info_grouping_get_count (info);
 423 	res = g_string_new ("");
 424 
 425 	for (i = 0; i < count; i++) {
 426 		ETableSortColumn col = e_table_sort_info_grouping_get_nth (info, i);
 427 		ETableColumnSpecification *column;
 428 
 429 		column = find_column_in_spec (config->source_spec, col.column);
 430 		if (!column) {
 431 			g_warning ("Could not find model column in specification");
 432 			continue;
 433 		}
 434 
 435 		g_string_append (res, dgettext (config->domain, (column)->title));
 436 		g_string_append_c (res, ' ');
 437 		g_string_append (
 438 			res,
 439 			col.ascending ?
 440 			_("(Ascending)") : _("(Descending)"));
 441 
 442 		if ((i + 1) != count)
 443 			g_string_append (res, ", ");
 444 	}
 445 	if (res->str[0] == 0)
 446 		g_string_append (res, _("No grouping"));
 447 
 448 	gtk_label_set_text (GTK_LABEL (config->group_label), res->str);
 449 	g_string_free (res, TRUE);
 450 }
 451 
 452 static void
 453 setup_fields (ETableConfig *config)
 454 {
 455 	gint i;
 456 
 457 	e_table_model_freeze ((ETableModel *) config->available_model);
 458 	e_table_model_freeze ((ETableModel *) config->shown_model);
 459 	e_table_without_show_all (config->available_model);
 460 	e_table_subset_variable_clear (config->shown_model);
 461 
 462 	if (config->temp_state) {
 463 		for (i = 0; i < config->temp_state->col_count; i++) {
 464 			gint j, idx;
 465 			for (j = 0, idx = 0; j < config->temp_state->columns[i]; j++)
 466 				if (!config->source_spec->columns[j]->disabled)
 467 					idx++;
 468 
 469 			e_table_subset_variable_add (config->shown_model, idx);
 470 			e_table_without_hide (config->available_model, GINT_TO_POINTER (idx));
 471 		}
 472 	}
 473 	e_table_model_thaw ((ETableModel *) config->available_model);
 474 	e_table_model_thaw ((ETableModel *) config->shown_model);
 475 }
 476 
 477 static void
 478 config_fields_info_update (ETableConfig *config)
 479 {
 480 	ETableColumnSpecification **column;
 481 	GString *res = g_string_new ("");
 482 	gint i, j;
 483 
 484 	for (i = 0; i < config->state->col_count; i++) {
 485 		for (j = 0, column = config->source_spec->columns; *column; column++, j++) {
 486 
 487 			if ((*column)->disabled)
 488 				continue;
 489 
 490 			if (config->state->columns[i] != j)
 491 				continue;
 492 
 493 			g_string_append (res, dgettext (config->domain, (*column)->title));
 494 			if (i + 1 < config->state->col_count)
 495 				g_string_append (res, ", ");
 496 
 497 			break;
 498 		}
 499 	}
 500 
 501 	gtk_label_set_text (GTK_LABEL (config->fields_label), res->str);
 502 	g_string_free (res, TRUE);
 503 }
 504 
 505 static void
 506 do_sort_and_group_config_dialog (ETableConfig *config,
 507                                  gboolean is_sort)
 508 {
 509 	GtkDialog *dialog;
 510 	gint response, running = 1;
 511 
 512 	config->temp_state = e_table_state_duplicate (config->state);
 513 
 514 	update_sort_and_group_config_dialog (config, is_sort);
 515 
 516 	gtk_widget_grab_focus (GTK_WIDGET (
 517 		is_sort
 518 		? config->sort[0].combo
 519 		: config->group[0].combo));
 520 
 521 	if (is_sort)
 522 		dialog = GTK_DIALOG (config->dialog_sort);
 523 	else
 524 		dialog = GTK_DIALOG (config->dialog_group_by);
 525 
 526 	gtk_window_set_transient_for (
 527 		GTK_WINDOW (dialog), GTK_WINDOW (config->dialog_toplevel));
 528 
 529 	do {
 530 		response = gtk_dialog_run (dialog);
 531 		switch (response) {
 532 		case 0: /* clear fields */
 533 			if (is_sort) {
 534 				e_table_sort_info_sorting_truncate (
 535 					config->temp_state->sort_info, 0);
 536 			} else {
 537 				e_table_sort_info_grouping_truncate (
 538 					config->temp_state->sort_info, 0);
 539 			}
 540 			update_sort_and_group_config_dialog (config, is_sort);
 541 			break;
 542 
 543 		case GTK_RESPONSE_OK:
 544 			g_object_unref (config->state);
 545 			config->state = config->temp_state;
 546 			config->temp_state = NULL;
 547 			running = 0;
 548 			config_dialog_changed (config);
 549 			break;
 550 
 551 		case GTK_RESPONSE_DELETE_EVENT:
 552 		case GTK_RESPONSE_CANCEL:
 553 			g_object_unref (config->temp_state);
 554 			config->temp_state = NULL;
 555 			running = 0;
 556 			break;
 557 		}
 558 
 559 	} while (running);
 560 	gtk_widget_hide (GTK_WIDGET (dialog));
 561 
 562 	if (is_sort)
 563 		config_sort_info_update (config);
 564 	else
 565 		config_group_info_update (config);
 566 }
 567 
 568 static void
 569 do_fields_config_dialog (ETableConfig *config)
 570 {
 571 	GtkDialog *dialog;
 572 	GtkWidget *widget;
 573 	gint response, running = 1;
 574 
 575 	dialog = GTK_DIALOG (config->dialog_show_fields);
 576 
 577 	gtk_widget_ensure_style (config->dialog_show_fields);
 578 
 579 	widget = gtk_dialog_get_content_area (dialog);
 580 	gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
 581 
 582 	widget = gtk_dialog_get_action_area (dialog);
 583 	gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
 584 
 585 	config->temp_state = e_table_state_duplicate (config->state);
 586 
 587 	setup_fields (config);
 588 
 589 	gtk_window_set_transient_for (
 590 		GTK_WINDOW (config->dialog_show_fields),
 591 		GTK_WINDOW (config->dialog_toplevel));
 592 
 593 	do {
 594 		response = gtk_dialog_run (GTK_DIALOG (config->dialog_show_fields));
 595 		switch (response) {
 596 		case GTK_RESPONSE_OK:
 597 			g_object_unref (config->state);
 598 			config->state = config->temp_state;
 599 			config->temp_state = NULL;
 600 			running = 0;
 601 			config_dialog_changed (config);
 602 			break;
 603 
 604 		case GTK_RESPONSE_DELETE_EVENT:
 605 		case GTK_RESPONSE_CANCEL:
 606 			g_object_unref (config->temp_state);
 607 			config->temp_state = NULL;
 608 			running = 0;
 609 			break;
 610 		}
 611 
 612 	} while (running);
 613 	gtk_widget_hide (GTK_WIDGET (config->dialog_show_fields));
 614 
 615 	config_fields_info_update (config);
 616 }
 617 
 618 static ETableMemoryStoreColumnInfo store_columns[] = {
 619 	E_TABLE_MEMORY_STORE_STRING,
 620 	E_TABLE_MEMORY_STORE_INTEGER,
 621 	E_TABLE_MEMORY_STORE_TERMINATOR
 622 };
 623 
 624 static ETableModel *
 625 create_store (ETableConfig *config)
 626 {
 627 	gint i;
 628 	ETableModel *store;
 629 
 630 	store = e_table_memory_store_new (store_columns);
 631 	for (i = 0; config->source_spec->columns[i]; i++) {
 632 
 633 		gchar *text;
 634 
 635 		if (config->source_spec->columns[i]->disabled)
 636 			continue;
 637 
 638 		text = g_strdup (dgettext (
 639 			config->domain,
 640 			config->source_spec->columns[i]->title));
 641 		e_table_memory_store_insert_adopt (
 642 			E_TABLE_MEMORY_STORE (store), -1, NULL, text, i);
 643 	}
 644 
 645 	return store;
 646 }
 647 
 648 static const gchar *spec =
 649 "<ETableSpecification gettext-domain=\"" GETTEXT_PACKAGE "\""
 650 " no-headers=\"true\" cursor-mode=\"line\" draw-grid=\"false\" "
 651 " draw-focus=\"true\" selection-mode=\"browse\">"
 652 "<ETableColumn model_col= \"0\" _title=\"Name\" minimum_width=\"30\""
 653 " resizable=\"true\" cell=\"string\" compare=\"string\"/>"
 654 "<ETableState> <column source=\"0\"/>"
 655 "<grouping/>"
 656 "</ETableState>"
 657 "</ETableSpecification>";
 658 
 659 static GtkWidget *
 660 e_table_proxy_etable_shown_new (ETableModel *store)
 661 {
 662 	ETableModel *model = NULL;
 663 	GtkWidget *widget;
 664 
 665 	model = e_table_subset_variable_new (store);
 666 
 667 	widget = e_table_new (model, NULL, spec, NULL);
 668 
 669 	atk_object_set_name (
 670 		gtk_widget_get_accessible (widget),
 671 		_("Show Fields"));
 672 
 673 	return widget;
 674 }
 675 
 676 static GtkWidget *
 677 e_table_proxy_etable_available_new (ETableModel *store)
 678 {
 679 	ETableModel *model;
 680 	GtkWidget *widget;
 681 
 682 	model = e_table_without_new (
 683 		store, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
 684 
 685 	e_table_without_show_all (E_TABLE_WITHOUT (model));
 686 
 687 	widget = e_table_new (model, NULL, spec, NULL);
 688 
 689 	atk_object_set_name (
 690 		gtk_widget_get_accessible (widget),
 691 		_("Available Fields"));
 692 
 693 	return widget;
 694 }
 695 
 696 static void
 697 config_button_fields (GtkWidget *widget,
 698                       ETableConfig *config)
 699 {
 700 	do_fields_config_dialog (config);
 701 }
 702 
 703 static void
 704 config_button_sort (GtkWidget *widget,
 705                     ETableConfig *config)
 706 {
 707 	do_sort_and_group_config_dialog (config, TRUE);
 708 }
 709 
 710 static void
 711 config_button_group (GtkWidget *widget,
 712                      ETableConfig *config)
 713 {
 714 	do_sort_and_group_config_dialog (config, FALSE);
 715 }
 716 
 717 static void
 718 dialog_destroyed (gpointer data,
 719                   GObject *where_object_was)
 720 {
 721 	ETableConfig *config = data;
 722 	g_object_unref (config);
 723 }
 724 
 725 static void
 726 dialog_response (GtkWidget *dialog,
 727                  gint response_id,
 728                  ETableConfig *config)
 729 {
 730 	if (response_id == GTK_RESPONSE_APPLY
 731 	    || response_id == GTK_RESPONSE_OK) {
 732 		e_table_config_changed (config, config->state);
 733 	}
 734 
 735 	if (response_id == GTK_RESPONSE_CANCEL
 736 	    || response_id == GTK_RESPONSE_OK) {
 737 		gtk_widget_destroy (dialog);
 738 	}
 739 }
 740 
 741 /*
 742  * Invoked by the GtkBuilder auto-connect code
 743  */
 744 static GtkWidget *
 745 e_table_proxy_gtk_combo_text_new (void)
 746 {
 747 	GtkCellRenderer *renderer;
 748 	GtkListStore *store;
 749 	GtkWidget *combo_box;
 750 	GHashTable *index;
 751 
 752 	store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
 753 	combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
 754 
 755 	renderer = gtk_cell_renderer_text_new ();
 756 	gtk_cell_layout_pack_start (
 757 		GTK_CELL_LAYOUT (combo_box), renderer, FALSE);
 758 	gtk_cell_layout_add_attribute (
 759 		GTK_CELL_LAYOUT (combo_box), renderer, "text", COLUMN_ITEM);
 760 
 761 	/* Embed a reverse-lookup index into the widget. */
 762 	index = g_hash_table_new_full (
 763 		g_str_hash, g_str_equal,
 764 		(GDestroyNotify) g_free,
 765 		(GDestroyNotify) gtk_tree_row_reference_free);
 766 	g_object_set_data_full (
 767 		G_OBJECT (combo_box), "index", index,
 768 		(GDestroyNotify) g_hash_table_destroy);
 769 
 770 	return combo_box;
 771 }
 772 
 773 static void
 774 connect_button (ETableConfig *config,
 775                 GtkBuilder *builder,
 776                 const gchar *widget_name,
 777                 GCallback cback)
 778 {
 779 	GtkWidget *button = e_builder_get_widget (builder, widget_name);
 780 
 781 	if (button)
 782 		g_signal_connect (button, "clicked", cback, config);
 783 }
 784 
 785 static gint
 786 get_source_model_col_index (ETableConfig *config,
 787                             gint idx)
 788 {
 789 	gint visible_index;
 790 	ETableModel *src_model;
 791 
 792 	src_model = E_TABLE_SUBSET (config->available_model)->source;
 793 
 794 	visible_index = e_table_subset_view_to_model_row (
 795 		E_TABLE_SUBSET (config->available_model), idx);
 796 
 797 	return GPOINTER_TO_INT (e_table_model_value_at (src_model, 1, visible_index));
 798 }
 799 
 800 static void
 801 sort_combo_changed (GtkComboBox *combo_box,
 802                     ETableConfigSortWidgets *sort)
 803 {
 804 	ETableConfig *config = sort->e_table_config;
 805 	ETableSortInfo *sort_info = config->temp_state->sort_info;
 806 	ETableConfigSortWidgets *base = &config->sort[0];
 807 	GtkToggleButton *toggle_button;
 808 	gint idx = sort - base;
 809 	gchar *s;
 810 
 811 	s = configure_combo_box_get_active (combo_box);
 812 
 813 	if (s != NULL) {
 814 		ETableSortColumn c;
 815 		gint col;
 816 
 817 		col = find_model_column_by_name (config->source_spec, s);
 818 		if (col == -1) {
 819 			g_warning ("sort: This should not happen (%s)", s);
 820 			g_free (s);
 821 			return;
 822 		}
 823 
 824 		toggle_button = GTK_TOGGLE_BUTTON (
 825 			config->sort[idx].radio_ascending);
 826 		c.ascending = gtk_toggle_button_get_active (toggle_button);
 827 		c.column = col;
 828 		e_table_sort_info_sorting_set_nth (sort_info, idx, c);
 829 
 830 		update_sort_and_group_config_dialog (config, TRUE);
 831 	}  else {
 832 		e_table_sort_info_sorting_truncate (sort_info, idx);
 833 		update_sort_and_group_config_dialog (config, TRUE);
 834 	}
 835 
 836 	g_free (s);
 837 }
 838 
 839 static void
 840 sort_ascending_toggled (GtkToggleButton *t,
 841                         ETableConfigSortWidgets *sort)
 842 {
 843 	ETableConfig *config = sort->e_table_config;
 844 	ETableSortInfo *si = config->temp_state->sort_info;
 845 	ETableConfigSortWidgets *base = &config->sort[0];
 846 	gint idx = sort - base;
 847 	ETableSortColumn c;
 848 
 849 	c = e_table_sort_info_sorting_get_nth (si, idx);
 850 	c.ascending = gtk_toggle_button_get_active (t);
 851 	e_table_sort_info_sorting_set_nth (si, idx, c);
 852 }
 853 
 854 static void
 855 configure_sort_dialog (ETableConfig *config,
 856                        GtkBuilder *builder)
 857 {
 858 	GSList *l;
 859 	gint i;
 860 
 861 	const gchar *algs[] = {
 862 		"alignment4",
 863 		"alignment3",
 864 		"alignment2",
 865 		"alignment1",
 866 		NULL
 867 	};
 868 
 869 	for (i = 0; i < 4; i++) {
 870 		gchar buffer[80];
 871 
 872 		snprintf (buffer, sizeof (buffer), "sort-combo-%d", i + 1);
 873 		config->sort[i].combo = e_table_proxy_gtk_combo_text_new ();
 874 		gtk_widget_show (GTK_WIDGET (config->sort[i].combo));
 875 		gtk_container_add (
 876 			GTK_CONTAINER (e_builder_get_widget (
 877 			builder, algs[i])), config->sort[i].combo);
 878 		configure_combo_box_add (
 879 			GTK_COMBO_BOX (config->sort[i].combo), "", "");
 880 
 881 		snprintf (buffer, sizeof (buffer), "frame-sort-%d", i + 1);
 882 		config->sort[i].frames =
 883 			e_builder_get_widget (builder, buffer);
 884 
 885 		snprintf (
 886 			buffer, sizeof (buffer),
 887 			"radiobutton-ascending-sort-%d", i + 1);
 888 		config->sort[i].radio_ascending = e_builder_get_widget (
 889 			builder, buffer);
 890 
 891 		snprintf (
 892 			buffer, sizeof (buffer),
 893 			"radiobutton-descending-sort-%d", i + 1);
 894 		config->sort[i].radio_descending = e_builder_get_widget (
 895 			builder, buffer);
 896 
 897 		config->sort[i].e_table_config = config;
 898 	}
 899 
 900 	for (l = config->column_names; l; l = l->next) {
 901 		gchar *label = l->data;
 902 
 903 		for (i = 0; i < 4; i++) {
 904 			configure_combo_box_add (
 905 				GTK_COMBO_BOX (config->sort[i].combo),
 906 				dgettext (config->domain, label), label);
 907 		}
 908 	}
 909 
 910 	/*
 911 	 * After we have runtime modified things, signal connect
 912 	 */
 913 	for (i = 0; i < 4; i++) {
 914 		config->sort[i].changed_id = g_signal_connect (
 915 			config->sort[i].combo,
 916 			"changed", G_CALLBACK (sort_combo_changed),
 917 			&config->sort[i]);
 918 
 919 		config->sort[i].toggled_id = g_signal_connect (
 920 			config->sort[i].radio_ascending,
 921 			"toggled", G_CALLBACK (sort_ascending_toggled),
 922 			&config->sort[i]);
 923 	}
 924 }
 925 
 926 static void
 927 group_combo_changed (GtkComboBox *combo_box,
 928                      ETableConfigSortWidgets *group)
 929 {
 930 	ETableConfig *config = group->e_table_config;
 931 	ETableSortInfo *sort_info = config->temp_state->sort_info;
 932 	ETableConfigSortWidgets *base = &config->group[0];
 933 	gint idx = group - base;
 934 	gchar *s;
 935 
 936 	s = configure_combo_box_get_active (combo_box);
 937 
 938 	if (s != NULL) {
 939 		GtkToggleButton *toggle_button;
 940 		ETableSortColumn c;
 941 		gint col;
 942 
 943 		col = find_model_column_by_name (config->source_spec, s);
 944 		if (col == -1) {
 945 			g_warning ("grouping: this should not happen, %s", s);
 946 			g_free (s);
 947 			return;
 948 		}
 949 
 950 		toggle_button = GTK_TOGGLE_BUTTON (
 951 			config->group[idx].radio_ascending);
 952 		c.ascending = gtk_toggle_button_get_active (toggle_button);
 953 		c.column = col;
 954 		e_table_sort_info_grouping_set_nth (sort_info, idx, c);
 955 
 956 		update_sort_and_group_config_dialog (config, FALSE);
 957 	}  else {
 958 		e_table_sort_info_grouping_truncate (sort_info, idx);
 959 		update_sort_and_group_config_dialog (config, FALSE);
 960 	}
 961 
 962 	g_free (s);
 963 }
 964 
 965 static void
 966 group_ascending_toggled (GtkToggleButton *t,
 967                          ETableConfigSortWidgets *group)
 968 {
 969 	ETableConfig *config = group->e_table_config;
 970 	ETableSortInfo *si = config->temp_state->sort_info;
 971 	ETableConfigSortWidgets *base = &config->group[0];
 972 	gint idx = group - base;
 973 	ETableSortColumn c;
 974 
 975 	c = e_table_sort_info_grouping_get_nth (si, idx);
 976 	c.ascending = gtk_toggle_button_get_active (t);
 977 	e_table_sort_info_grouping_set_nth (si, idx, c);
 978 }
 979 
 980 static void
 981 configure_group_dialog (ETableConfig *config,
 982                         GtkBuilder *builder)
 983 {
 984 	GSList *l;
 985 	gint i;
 986 	const gchar *vboxes[] = {"vbox7", "vbox9", "vbox11", "vbox13", NULL};
 987 
 988 	for (i = 0; i < 4; i++) {
 989 		gchar buffer[80];
 990 
 991 		snprintf (buffer, sizeof (buffer), "group-combo-%d", i + 1);
 992 		config->group[i].combo = e_table_proxy_gtk_combo_text_new ();
 993 		gtk_widget_show (GTK_WIDGET (config->group[i].combo));
 994 		gtk_box_pack_start (
 995 			GTK_BOX (e_builder_get_widget (builder, vboxes[i])),
 996 			config->group[i].combo, FALSE, FALSE, 0);
 997 
 998 		configure_combo_box_add (
 999 			GTK_COMBO_BOX (config->group[i].combo), "", "");
1000 
1001 		snprintf (buffer, sizeof (buffer), "frame-group-%d", i + 1);
1002 		config->group[i].frames =
1003 			e_builder_get_widget (builder, buffer);
1004 
1005 		snprintf (
1006 			buffer, sizeof (buffer),
1007 			"radiobutton-ascending-group-%d", i + 1);
1008 		config->group[i].radio_ascending = e_builder_get_widget (
1009 			builder, buffer);
1010 
1011 		snprintf (
1012 			buffer, sizeof (buffer),
1013 			"radiobutton-descending-group-%d", i + 1);
1014 		config->group[i].radio_descending = e_builder_get_widget (
1015 			builder, buffer);
1016 
1017 		snprintf (
1018 			buffer, sizeof (buffer),
1019 			"checkbutton-group-%d", i + 1);
1020 		config->group[i].view_check = e_builder_get_widget (
1021 			builder, buffer);
1022 
1023 		config->group[i].e_table_config = config;
1024 	}
1025 
1026 	for (l = config->column_names; l; l = l->next) {
1027 		gchar *label = l->data;
1028 
1029 		for (i = 0; i < 4; i++) {
1030 			configure_combo_box_add (
1031 				GTK_COMBO_BOX (config->group[i].combo),
1032 				dgettext (config->domain, label), label);
1033 		}
1034 	}
1035 
1036 	/*
1037 	 * After we have runtime modified things, signal connect
1038 	 */
1039 	for (i = 0; i < 4; i++) {
1040 		config->group[i].changed_id = g_signal_connect (
1041 			config->group[i].combo,
1042 			"changed", G_CALLBACK (group_combo_changed),
1043 			&config->group[i]);
1044 
1045 		config->group[i].toggled_id = g_signal_connect (
1046 			config->group[i].radio_ascending,
1047 			"toggled", G_CALLBACK (group_ascending_toggled),
1048 			&config->group[i]);
1049 	}
1050 }
1051 
1052 static void
1053 add_column (gint model_row,
1054             gpointer closure)
1055 {
1056 	GList **columns = closure;
1057 	*columns = g_list_prepend (*columns, GINT_TO_POINTER (model_row));
1058 }
1059 
1060 static void
1061 config_button_add (GtkWidget *widget,
1062                    ETableConfig *config)
1063 {
1064 	GList *columns = NULL;
1065 	GList *column;
1066 	gint count;
1067 	gint i;
1068 
1069 	e_table_selected_row_foreach (config->available, add_column, &columns);
1070 	columns = g_list_reverse (columns);
1071 
1072 	count = g_list_length (columns);
1073 
1074 	config->temp_state->columns = g_renew (
1075 		int, config->temp_state->columns,
1076 		config->temp_state->col_count + count);
1077 	config->temp_state->expansions = g_renew (
1078 		gdouble, config->temp_state->expansions,
1079 		config->temp_state->col_count + count);
1080 	i = config->temp_state->col_count;
1081 	for (column = columns; column; column = column->next) {
1082 		config->temp_state->columns[i] =
1083 			get_source_model_col_index (
1084 			config, GPOINTER_TO_INT (column->data));
1085 		config->temp_state->expansions[i] =
1086 			config->source_spec->columns
1087 			[config->temp_state->columns[i]]->expansion;
1088 		i++;
1089 	}
1090 	config->temp_state->col_count += count;
1091 
1092 	g_list_free (columns);
1093 
1094 	setup_fields (config);
1095 }
1096 
1097 static void
1098 config_button_remove (GtkWidget *widget,
1099                       ETableConfig *config)
1100 {
1101 	GList *columns = NULL;
1102 	GList *column;
1103 
1104 	e_table_selected_row_foreach (config->shown, add_column, &columns);
1105 
1106 	for (column = columns; column; column = column->next) {
1107 		gint row = GPOINTER_TO_INT (column->data);
1108 
1109 		memmove (
1110 			config->temp_state->columns + row,
1111 			config->temp_state->columns + row + 1,
1112 			sizeof (gint) * (config->temp_state->col_count - row - 1));
1113 		memmove (
1114 			config->temp_state->expansions + row,
1115 			config->temp_state->expansions + row + 1,
1116 			sizeof (gdouble) * (config->temp_state->col_count - row - 1));
1117 		config->temp_state->col_count--;
1118 	}
1119 	config->temp_state->columns = g_renew (
1120 		int, config->temp_state->columns,
1121 		config->temp_state->col_count);
1122 	config->temp_state->expansions = g_renew (
1123 		gdouble, config->temp_state->expansions,
1124 		config->temp_state->col_count);
1125 
1126 	g_list_free (columns);
1127 
1128 	setup_fields (config);
1129 }
1130 
1131 static void
1132 config_button_up (GtkWidget *widget,
1133                   ETableConfig *config)
1134 {
1135 	GList *columns = NULL;
1136 	GList *column;
1137 	gint *new_shown;
1138 	gdouble *new_expansions;
1139 	gint next_col;
1140 	gdouble next_expansion;
1141 	gint i;
1142 
1143 	e_table_selected_row_foreach (config->shown, add_column, &columns);
1144 
1145 	/* if no columns left, just return */
1146 	if (columns == NULL)
1147 		return;
1148 
1149 	columns = g_list_reverse (columns);
1150 
1151 	new_shown = g_new (int, config->temp_state->col_count);
1152 	new_expansions = g_new (double, config->temp_state->col_count);
1153 
1154 	column = columns;
1155 
1156 	next_col = config->temp_state->columns[0];
1157 	next_expansion = config->temp_state->expansions[0];
1158 
1159 	for (i = 1; i < config->temp_state->col_count; i++) {
1160 		if (column && (GPOINTER_TO_INT (column->data) == i)) {
1161 			new_expansions[i - 1] = config->temp_state->expansions[i];
1162 			new_shown[i - 1] = config->temp_state->columns[i];
1163 			column = column->next;
1164 		} else {
1165 			new_shown[i - 1] = next_col;
1166 			next_col = config->temp_state->columns[i];
1167 
1168 			new_expansions[i - 1] = next_expansion;
1169 			next_expansion = config->temp_state->expansions[i];
1170 		}
1171 	}
1172 
1173 	new_shown[i - 1] = next_col;
1174 	new_expansions[i - 1] = next_expansion;
1175 
1176 	g_free (config->temp_state->columns);
1177 	g_free (config->temp_state->expansions);
1178 
1179 	config->temp_state->columns = new_shown;
1180 	config->temp_state->expansions = new_expansions;
1181 
1182 	g_list_free (columns);
1183 
1184 	setup_fields (config);
1185 }
1186 
1187 static void
1188 config_button_down (GtkWidget *widget,
1189                     ETableConfig *config)
1190 {
1191 	GList *columns = NULL;
1192 	GList *column;
1193 	gint *new_shown;
1194 	gdouble *new_expansions;
1195 	gint next_col;
1196 	gdouble next_expansion;
1197 	gint i;
1198 
1199 	e_table_selected_row_foreach (config->shown, add_column, &columns);
1200 
1201 	/* if no columns left, just return */
1202 	if (columns == NULL)
1203 		return;
1204 
1205 	new_shown = g_new (gint, config->temp_state->col_count);
1206 	new_expansions = g_new (gdouble, config->temp_state->col_count);
1207 
1208 	column = columns;
1209 
1210 	next_col =
1211 		config->temp_state->columns[config->temp_state->col_count - 1];
1212 	next_expansion =
1213 		config->temp_state->expansions[config->temp_state->col_count - 1];
1214 
1215 	for (i = config->temp_state->col_count - 1; i > 0; i--) {
1216 		if (column && (GPOINTER_TO_INT (column->data) == i - 1)) {
1217 			new_expansions[i] = config->temp_state->expansions[i - 1];
1218 			new_shown[i] = config->temp_state->columns[i - 1];
1219 			column = column->next;
1220 		} else {
1221 			new_shown[i] = next_col;
1222 			next_col = config->temp_state->columns[i - 1];
1223 
1224 			new_expansions[i] = next_expansion;
1225 			next_expansion = config->temp_state->expansions[i - 1];
1226 		}
1227 	}
1228 
1229 	new_shown[0] = next_col;
1230 	new_expansions[0] = next_expansion;
1231 
1232 	g_free (config->temp_state->columns);
1233 	g_free (config->temp_state->expansions);
1234 
1235 	config->temp_state->columns = new_shown;
1236 	config->temp_state->expansions = new_expansions;
1237 
1238 	g_list_free (columns);
1239 
1240 	setup_fields (config);
1241 }
1242 
1243 static void
1244 configure_fields_dialog (ETableConfig *config,
1245                          GtkBuilder *builder)
1246 {
1247 	GtkWidget *scrolled;
1248 	GtkWidget *etable;
1249 	ETableModel *store = create_store (config);
1250 
1251 	/* "custom-available" widget */
1252 	etable = e_table_proxy_etable_available_new (store);
1253 	gtk_widget_show (etable);
1254 	scrolled = e_builder_get_widget (builder, "available-scrolled");
1255 	gtk_container_add (GTK_CONTAINER (scrolled), etable);
1256 	config->available = E_TABLE (etable);
1257 	g_object_get (
1258 		config->available,
1259 		"model", &config->available_model,
1260 		NULL);
1261 	gtk_widget_show_all (etable);
1262 	gtk_label_set_mnemonic_widget (
1263 		GTK_LABEL (e_builder_get_widget (
1264 		builder, "label-available")), etable);
1265 
1266 	/* "custom-shown" widget */
1267 	etable = e_table_proxy_etable_shown_new (store);
1268 	gtk_widget_show (etable);
1269 	scrolled = e_builder_get_widget (builder, "shown-scrolled");
1270 	gtk_container_add (GTK_CONTAINER (scrolled), etable);
1271 	config->shown = E_TABLE (etable);
1272 	g_object_get (
1273 		config->shown,
1274 		"model", &config->shown_model,
1275 		NULL);
1276 	gtk_widget_show_all (etable);
1277 	gtk_label_set_mnemonic_widget (
1278 		GTK_LABEL (e_builder_get_widget (
1279 		builder, "label-displayed")), etable);
1280 
1281 	connect_button (
1282 		config, builder, "button-add",
1283 		G_CALLBACK (config_button_add));
1284 	connect_button (
1285 		config, builder, "button-remove",
1286 		G_CALLBACK (config_button_remove));
1287 	connect_button (
1288 		config, builder, "button-up",
1289 		G_CALLBACK (config_button_up));
1290 	connect_button (
1291 		config, builder, "button-down",
1292 		G_CALLBACK (config_button_down));
1293 
1294 	setup_fields (config);
1295 
1296 	g_object_unref (store);
1297 }
1298 
1299 static void
1300 setup_gui (ETableConfig *config)
1301 {
1302 	GtkBuilder *builder;
1303 	gboolean can_group;
1304 
1305 	can_group = e_table_sort_info_get_can_group (config->state->sort_info);
1306 	builder = gtk_builder_new ();
1307 	e_load_ui_builder_definition (builder, "e-table-config.ui");
1308 
1309 	config->dialog_toplevel = e_builder_get_widget (
1310 		builder, "e-table-config");
1311 
1312 	if (config->header)
1313 		gtk_window_set_title (
1314 			GTK_WINDOW (config->dialog_toplevel),
1315 			config->header);
1316 
1317 	config->dialog_show_fields = e_builder_get_widget (
1318 		builder, "dialog-show-fields");
1319 	config->dialog_group_by =  e_builder_get_widget (
1320 		builder, "dialog-group-by");
1321 	config->dialog_sort = e_builder_get_widget (
1322 		builder, "dialog-sort");
1323 
1324 	config->sort_label = e_builder_get_widget (
1325 		builder, "label-sort");
1326 	config->group_label = e_builder_get_widget (
1327 		builder, "label-group");
1328 	config->fields_label = e_builder_get_widget (
1329 		builder, "label-fields");
1330 
1331 	connect_button (
1332 		config, builder, "button-sort",
1333 		G_CALLBACK (config_button_sort));
1334 	connect_button (
1335 		config, builder, "button-group",
1336 		G_CALLBACK (config_button_group));
1337 	connect_button (
1338 		config, builder, "button-fields",
1339 		G_CALLBACK (config_button_fields));
1340 
1341 	if (!can_group) {
1342 		GtkWidget *w;
1343 
1344 		w = e_builder_get_widget (builder, "button-group");
1345 		if (w)
1346 			gtk_widget_hide (w);
1347 
1348 		w = e_builder_get_widget (builder, "label3");
1349 		if (w)
1350 			gtk_widget_hide (w);
1351 
1352 		w = config->group_label;
1353 		if (w)
1354 			gtk_widget_hide (w);
1355 	}
1356 
1357 	configure_sort_dialog (config, builder);
1358 	configure_group_dialog (config, builder);
1359 	configure_fields_dialog (config, builder);
1360 
1361 	g_object_weak_ref (
1362 		G_OBJECT (config->dialog_toplevel),
1363 		dialog_destroyed, config);
1364 
1365 	g_signal_connect (
1366 		config->dialog_toplevel, "response",
1367 		G_CALLBACK (dialog_response), config);
1368 
1369 	g_object_unref (builder);
1370 }
1371 
1372 static void
1373 e_table_config_init (ETableConfig *config)
1374 {
1375 	config->domain = NULL;
1376 }
1377 
1378 ETableConfig *
1379 e_table_config_construct (ETableConfig *config,
1380                           const gchar *header,
1381                           ETableSpecification *spec,
1382                           ETableState *state,
1383                           GtkWindow *parent_window)
1384 {
1385 	ETableColumnSpecification **column;
1386 
1387 	g_return_val_if_fail (config != NULL, NULL);
1388 	g_return_val_if_fail (header != NULL, NULL);
1389 	g_return_val_if_fail (spec != NULL, NULL);
1390 	g_return_val_if_fail (state != NULL, NULL);
1391 
1392 	config->source_spec = spec;
1393 	config->source_state = state;
1394 	config->header = g_strdup (header);
1395 
1396 	g_object_ref (config->source_spec);
1397 	g_object_ref (config->source_state);
1398 
1399 	config->state = e_table_state_duplicate (state);
1400 
1401 	config->domain = g_strdup (spec->domain);
1402 
1403 	for (column = config->source_spec->columns; *column; column++) {
1404 		gchar *label = (*column)->title;
1405 
1406 		if ((*column)->disabled)
1407 			continue;
1408 
1409 		config->column_names = g_slist_append (
1410 			config->column_names, label);
1411 	}
1412 
1413 	setup_gui (config);
1414 
1415 	gtk_window_set_transient_for (GTK_WINDOW (config->dialog_toplevel),
1416 				      parent_window);
1417 
1418 	config_sort_info_update   (config);
1419 	config_group_info_update  (config);
1420 	config_fields_info_update (config);
1421 
1422 	return E_TABLE_CONFIG (config);
1423 }
1424 
1425 /**
1426  * e_table_config_new:
1427  * @header: The title of the dialog for the ETableConfig.
1428  * @spec: The specification for the columns to allow.
1429  * @state: The current state of the configuration.
1430  *
1431  * Creates a new ETable config object.
1432  *
1433  * Returns: The config object.
1434  */
1435 ETableConfig *
1436 e_table_config_new (const gchar *header,
1437                     ETableSpecification *spec,
1438                     ETableState *state,
1439                     GtkWindow *parent_window)
1440 {
1441 	ETableConfig *config;
1442 	GtkDialog *dialog;
1443 	GtkWidget *widget;
1444 
1445 	config = g_object_new (E_TYPE_TABLE_CONFIG, NULL);
1446 
1447 	e_table_config_construct (
1448 		config, header, spec, state, parent_window);
1449 
1450 	dialog = GTK_DIALOG (config->dialog_toplevel);
1451 
1452 	gtk_widget_ensure_style (config->dialog_toplevel);
1453 
1454 	widget = gtk_dialog_get_content_area (dialog);
1455 	gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
1456 
1457 	widget = gtk_dialog_get_action_area (dialog);
1458 	gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
1459 
1460 	gtk_dialog_set_response_sensitive (
1461 		GTK_DIALOG (config->dialog_toplevel),
1462 		GTK_RESPONSE_APPLY, FALSE);
1463 	gtk_widget_show (config->dialog_toplevel);
1464 
1465 	return E_TABLE_CONFIG (config);
1466 }
1467 
1468 /**
1469  * e_table_config_raise:
1470  * @config: The ETableConfig object.
1471  *
1472  * Raises the dialog associated with this ETableConfig object.
1473  */
1474 void
1475 e_table_config_raise (ETableConfig *config)
1476 {
1477 	GdkWindow *window;
1478 
1479 	window = gtk_widget_get_window (GTK_WIDGET (config->dialog_toplevel));
1480 	gdk_window_raise (window);
1481 }