evolution-3.6.4/e-util/e-config.c

Location Tool Test ID Function Issue
e-config.c:1057:8 clang-analyzer Access to field 'item' results in a dereference of a null pointer (loaded from variable 'sectionnode')
e-config.c:1057:8 clang-analyzer Access to field 'item' results in a dereference of a null pointer (loaded from variable 'sectionnode')
   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  *		Michael Zucchi <notzed@ximian.com>
  18  *
  19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  20  *
  21  */
  22 
  23 #ifdef HAVE_CONFIG_H
  24 #include <config.h>
  25 #endif
  26 
  27 #include <string.h>
  28 #include <stdlib.h>
  29 
  30 #include <gtk/gtk.h>
  31 #include <glib/gi18n.h>
  32 
  33 #include "e-config.h"
  34 
  35 #include <glib/gi18n.h>
  36 
  37 #define E_CONFIG_GET_PRIVATE(obj) \
  38 	(G_TYPE_INSTANCE_GET_PRIVATE \
  39 	((obj), E_TYPE_CONFIG, EConfigPrivate))
  40 
  41 #define d(x)
  42 
  43 typedef GtkWidget *
  44 		(*EConfigItemSectionFactoryFunc)
  45 						(EConfig *ec,
  46 						 EConfigItem *item,
  47 						 GtkWidget *parent,
  48 						 GtkWidget *old,
  49 						 gint position,
  50 						 gpointer data,
  51 						 GtkWidget **real_frame);
  52 
  53 struct _EConfigFactory {
  54 	gchar *id;
  55 	EConfigFactoryFunc func;
  56 	gpointer user_data;
  57 };
  58 
  59 struct _menu_node {
  60 	GSList *menu;
  61 	EConfigItemsFunc free;
  62 	gpointer data;
  63 };
  64 
  65 struct _widget_node {
  66 	EConfig *config;
  67 
  68 	struct _menu_node *context;
  69 	EConfigItem *item;
  70 	GtkWidget *widget; /* widget created by the factory, if any */
  71 	GtkWidget *frame; /* if created by us */
  72 	GtkWidget *real_frame; /* used for sections and section tables, this is the real GtkFrame (whereas "frame" above is the internal vbox/table) */
  73 
  74 	guint empty:1;		/* set if empty (i.e. hidden) */
  75 };
  76 
  77 struct _check_node {
  78 	gchar *pageid;
  79 	EConfigCheckFunc func;
  80 	gpointer data;
  81 };
  82 
  83 struct _finish_page_node {
  84 	gchar *pageid;
  85 	gboolean is_finish;
  86 	gint orig_type;
  87 };
  88 
  89 struct _EConfigPrivate {
  90 	GList *menus;
  91 	GList *widgets;
  92 	GList *checks;
  93 	GList *finish_pages;
  94 	GHashTable *skip_checks;
  95 };
  96 
  97 static GtkWidget *
  98 		ech_config_section_factory	(EConfig *config,
  99 						 EConfigItem *item,
 100 						 GtkWidget *parent,
 101 						 GtkWidget *old,
 102 						 gint position,
 103 						 gpointer data,
 104 						 GtkWidget **real_frame);
 105 
 106 enum {
 107 	ABORT,
 108 	COMMIT,
 109 	LAST_SIGNAL
 110 };
 111 
 112 static guint signals[LAST_SIGNAL];
 113 
 114 G_DEFINE_TYPE (
 115 	EConfig,
 116 	e_config,
 117 	G_TYPE_OBJECT)
 118 
 119 static void
 120 check_node_free (struct _check_node *node)
 121 {
 122 	g_free (node->pageid);
 123 
 124 	g_slice_free (struct _check_node, node);
 125 }
 126 
 127 static void
 128 config_finalize (GObject *object)
 129 {
 130 	EConfigPrivate *priv;
 131 	GList *link;
 132 
 133 	priv = E_CONFIG_GET_PRIVATE (object);
 134 
 135 	d (printf ("finalising EConfig %p\n", object));
 136 
 137 	g_free (E_CONFIG (object)->id);
 138 
 139 	link = priv->menus;
 140 	while (link != NULL) {
 141 		struct _menu_node *node = link->data;
 142 
 143 		if (node->free)
 144 			node->free (E_CONFIG (object), node->menu, node->data);
 145 
 146 		g_free (node);
 147 
 148 		link = g_list_delete_link (link, link);
 149 	}
 150 
 151 	link = priv->widgets;
 152 	while (link != NULL) {
 153 		struct _widget_node *node = link->data;
 154 
 155 		/* disconnect the ec_widget_destroyed function from the widget */
 156 		if (node->widget)
 157 			g_signal_handlers_disconnect_matched (
 158 				node->widget, G_SIGNAL_MATCH_DATA,
 159 				0, 0, NULL, NULL, node);
 160 
 161 		g_free (node);
 162 
 163 		link = g_list_delete_link (link, link);
 164 	}
 165 
 166 	g_list_free_full (priv->checks, (GDestroyNotify) check_node_free);
 167 
 168 	link = priv->finish_pages;
 169 	while (link != NULL) {
 170 		struct _finish_page_node *node = link->data;
 171 
 172 		g_free (node->pageid);
 173 		g_free (node);
 174 
 175 		link = g_list_delete_link (link, link);
 176 	}
 177 
 178 	g_hash_table_destroy (priv->skip_checks);
 179 
 180 	/* Chain up to parent's finalize() method. */
 181 	G_OBJECT_CLASS (e_config_parent_class)->finalize (object);
 182 }
 183 
 184 static void
 185 config_target_free (EConfig *config,
 186                     EConfigTarget *target)
 187 {
 188 	g_free (target);
 189 	g_object_unref (config);
 190 }
 191 
 192 static void
 193 config_set_target (EConfig *config,
 194                    EConfigTarget *target)
 195 {
 196 	if (config->target != NULL)
 197 		e_config_target_free (config, target);
 198 
 199 	config->target = target;
 200 }
 201 
 202 static void
 203 e_config_class_init (EConfigClass *class)
 204 {
 205 	GObjectClass *object_class;
 206 
 207 	g_type_class_add_private (class, sizeof (EConfigPrivate));
 208 
 209 	object_class = G_OBJECT_CLASS (class);
 210 	object_class->finalize = config_finalize;
 211 
 212 	class->set_target = config_set_target;
 213 	class->target_free = config_target_free;
 214 
 215 	signals[ABORT] = g_signal_new (
 216 		"abort",
 217 		G_OBJECT_CLASS_TYPE (object_class),
 218 		G_SIGNAL_RUN_LAST,
 219 		G_STRUCT_OFFSET (EConfigClass, abort),
 220 		NULL, NULL,
 221 		g_cclosure_marshal_VOID__VOID,
 222 		G_TYPE_NONE, 0);
 223 
 224 	signals[COMMIT] = g_signal_new (
 225 		"commit",
 226 		G_OBJECT_CLASS_TYPE (object_class),
 227 		G_SIGNAL_RUN_LAST,
 228 		G_STRUCT_OFFSET (EConfigClass, commit),
 229 		NULL, NULL,
 230 		g_cclosure_marshal_VOID__VOID,
 231 		G_TYPE_NONE, 0);
 232 }
 233 
 234 static void
 235 e_config_init (EConfig *config)
 236 {
 237 	config->priv = E_CONFIG_GET_PRIVATE (config);
 238 
 239 	config->priv->skip_checks = g_hash_table_new_full (
 240 		(GHashFunc) g_str_hash,
 241 		(GEqualFunc) g_str_equal,
 242 		(GDestroyNotify) NULL,
 243 		(GDestroyNotify) check_node_free);
 244 }
 245 
 246 /**
 247  * e_config_construct:
 248  * @ep: The instance to initialise.
 249  * @type: The type of configuration manager, @E_CONFIG_BOOK or
 250  * @E_CONFIG_ASSISTANT.
 251  * @id: The name of the configuration window this manager drives.
 252  *
 253  * Used by implementing classes to initialise base parameters.
 254  *
 255  * Return value: @ep is returned.
 256  **/
 257 EConfig *
 258 e_config_construct (EConfig *ep,
 259                     gint type,
 260                     const gchar *id)
 261 {
 262 	g_return_val_if_fail (type == E_CONFIG_BOOK || type == E_CONFIG_ASSISTANT, NULL);
 263 
 264 	ep->type = type;
 265 	ep->id = g_strdup (id);
 266 
 267 	return ep;
 268 }
 269 
 270 /**
 271  * e_config_add_items:
 272  * @ec: An initialised implementing instance of EConfig.
 273  * @items: A list of EConfigItem's to add to the configuration manager
 274  * @ec.
 275  * @freefunc: If supplied, called to free the item list (and/or items)
 276  * once they are no longer needed.
 277  * @data: Data for the callback methods.
 278  *
 279  * Add new EConfigItems to the configuration window.  Nothing will be
 280  * done with them until the widget is built.
 281  *
 282  * TODO: perhaps commit and abort should just be signals.
 283  **/
 284 void
 285 e_config_add_items (EConfig *ec,
 286                     GSList *items,
 287                     EConfigItemsFunc freefunc,
 288                     gpointer data)
 289 {
 290 	struct _menu_node *node;
 291 
 292 	node = g_malloc (sizeof (*node));
 293 	node->menu = items;
 294 	node->free = freefunc;
 295 	node->data = data;
 296 
 297 	ec->priv->menus = g_list_append (ec->priv->menus, node);
 298 }
 299 
 300 /**
 301  * e_config_add_page_check:
 302  * @ec: Initialised implemeting instance of EConfig.
 303  * @pageid: pageid to check.
 304  * @func: checking callback.
 305  * @data: user-data for the callback.
 306  *
 307  * Add a page-checking function callback.  It will be called to validate the
 308  * data in the given page or pages.  If @pageid is NULL then it will be called
 309  * to validate every page, or the whole configuration window.
 310  *
 311  * In the latter case, the pageid in the callback will be either the
 312  * specific page being checked, or NULL when the whole config window
 313  * is being checked.
 314  *
 315  * The page check function is used to validate input before allowing
 316  * the assistant to continue or the notebook to close.
 317  **/
 318 void
 319 e_config_add_page_check (EConfig *ec,
 320                          const gchar *pageid,
 321                          EConfigCheckFunc func,
 322                          gpointer data)
 323 {
 324 	struct _check_node *cn;
 325 
 326 	cn = g_slice_new0 (struct _check_node);
 327 	cn->pageid = g_strdup (pageid);
 328 	cn->func = func;
 329 	cn->data = data;
 330 
 331 	ec->priv->checks = g_list_append (ec->priv->checks, cn);
 332 }
 333 
 334 /**
 335  * e_config_add_skip_check:
 336  * @config: an #EConfig
 337  * @pageid: the page ID for the skip page callback
 338  * @func: the skip page callback function
 339  * @data: data to pass to the callback function
 340  *
 341  * Adds a callback function to decide whether to skip the page in a
 342  * GtkAssistant, useful if the page is blank in certain conditions.
 343  *
 344  * The callback function should return %TRUE if the page should be
 345  * skipped, or %FALSE if the page should be visited.
 346  **/
 347 void
 348 e_config_add_skip_check (EConfig *config,
 349                          const gchar *pageid,
 350                          EConfigCheckFunc func,
 351                          gpointer data)
 352 {
 353 	struct _check_node *cn;
 354 
 355 	g_return_if_fail (E_IS_CONFIG (config));
 356 	g_return_if_fail (pageid != NULL);
 357 	g_return_if_fail (func != NULL);
 358 
 359 	cn = g_slice_new0 (struct _check_node);
 360 	cn->pageid = g_strdup (pageid);
 361 	cn->func = func;
 362 	cn->data = data;
 363 
 364 	g_hash_table_insert (config->priv->skip_checks, cn->pageid, cn);
 365 }
 366 
 367 static struct _finish_page_node *
 368 find_page_finish (EConfig *config,
 369                   const gchar *pageid)
 370 {
 371 	GList *link;
 372 
 373 	link = config->priv->finish_pages;
 374 
 375 	while (link != NULL) {
 376 		struct _finish_page_node *node = link->data;
 377 
 378 		if (g_str_equal (node->pageid, pageid))
 379 			return node;
 380 
 381 		link = g_list_next (link);
 382 	}
 383 
 384 	return NULL;
 385 }
 386 
 387 /**
 388  * e_config_set_page_is_finish:
 389  * @ec: Initialised implementing instance of EConfig.
 390  * @pageid: pageid to change the value on.
 391  * @can_finish: whether the pageid can finish immediately or not.
 392  *
 393  * With is_finish set on the pageid the page is treated as the last page in an assistant.
 394  **/
 395 void
 396 e_config_set_page_is_finish (EConfig *ec,
 397                              const gchar *pageid,
 398                              gboolean is_finish)
 399 {
 400 	struct _finish_page_node *fp;
 401 
 402 	fp = find_page_finish (ec, pageid);
 403 
 404 	if (is_finish) {
 405 		if (!fp) {
 406 			fp = g_malloc0 (sizeof (*fp));
 407 			fp->pageid = g_strdup (pageid);
 408 			ec->priv->finish_pages = g_list_append (
 409 				ec->priv->finish_pages, fp);
 410 		}
 411 
 412 		fp->is_finish = TRUE;
 413 	} else {
 414 		if (fp)
 415 			fp->is_finish = FALSE;
 416 	}
 417 }
 418 
 419 static void
 420 ec_add_static_items (EConfig *config)
 421 {
 422 	EConfigClass *class;
 423 	GList *link;
 424 
 425 	class = E_CONFIG_GET_CLASS (config);
 426 	for (link = class->factories; link != NULL; link = link->next) {
 427 		EConfigFactory *factory = link->data;
 428 
 429 		if (factory->id == NULL || strcmp (factory->id, config->id) == 0)
 430 			factory->func (config, factory->user_data);
 431 	}
 432 }
 433 
 434 static gint
 435 ep_cmp (gconstpointer ap,
 436         gconstpointer bp)
 437 {
 438 	struct _widget_node *a = *((gpointer *) ap);
 439 	struct _widget_node *b = *((gpointer *) bp);
 440 
 441 	return strcmp (a->item->path, b->item->path);
 442 }
 443 
 444 static GList *
 445 ec_assistant_find_page (EConfig *ec,
 446                         GtkWidget *page,
 447                         gint *page_index)
 448 {
 449 	struct _widget_node *node = NULL;
 450 	GList *link;
 451 
 452 	g_return_val_if_fail (ec != NULL, NULL);
 453 	g_return_val_if_fail (GTK_IS_ASSISTANT (ec->widget), NULL);
 454 	g_return_val_if_fail (page != NULL, NULL);
 455 
 456 	/* Assume failure, then if we do fail we can just return. */
 457 	if (page_index != NULL)
 458 		*page_index = -1;
 459 
 460 	/* Find the page widget in our sorted widget node list. */
 461 	for (link = ec->priv->widgets; link != NULL; link = link->next) {
 462 		node = link->data;
 463 
 464 		if (node->frame != page)
 465 			continue;
 466 
 467 		if (node->item->type == E_CONFIG_PAGE)
 468 			break;
 469 
 470 		if (node->item->type == E_CONFIG_PAGE_START)
 471 			break;
 472 
 473 		if (node->item->type == E_CONFIG_PAGE_FINISH)
 474 			break;
 475 
 476 		if (node->item->type == E_CONFIG_PAGE_PROGRESS)
 477 			break;
 478 	}
 479 
 480 	/* FAIL: The widget is not in our list. */
 481 	if (link == NULL)
 482 		return NULL;
 483 
 484 	/* Find the corresponding GtkAssistant page index. */
 485 	if (page_index) {
 486 		GtkAssistant *assistant;
 487 		GtkWidget *nth_page;
 488 		gint ii, n_pages;
 489 
 490 		assistant = GTK_ASSISTANT (ec->widget);
 491 		n_pages = gtk_assistant_get_n_pages (assistant);
 492 
 493 		for (ii = 0; ii < n_pages; ii++) {
 494 			nth_page = gtk_assistant_get_nth_page (assistant, ii);
 495 			if (page == nth_page) {
 496 				*page_index = ii;
 497 				break;
 498 			}
 499 		}
 500 
 501 		g_warn_if_fail (ii < n_pages);
 502 	}
 503 
 504 	return link;
 505 }
 506 
 507 static void
 508 ec_assistant_check_current (EConfig *ec)
 509 {
 510 	struct _widget_node *wn;
 511 	struct _finish_page_node *fp;
 512 	GtkAssistant *assistant;
 513 	GtkWidget *page;
 514 	GList *link;
 515 	gint page_no;
 516 
 517 	g_return_if_fail (GTK_IS_ASSISTANT (ec->widget));
 518 
 519 	assistant = GTK_ASSISTANT (ec->widget);
 520 	page_no = gtk_assistant_get_current_page (assistant);
 521 
 522 	/* no page selected yet */
 523 	if (page_no == -1)
 524 		return;
 525 
 526 	page = gtk_assistant_get_nth_page (assistant, page_no);
 527 	g_return_if_fail (page != NULL);
 528 
 529 	link = ec_assistant_find_page (ec, page, NULL);
 530 	g_return_if_fail (link != NULL);
 531 	wn = link->data;
 532 
 533 	/* this should come first, as the check function can change the finish state of the page */
 534 	gtk_assistant_set_page_complete (assistant, page, e_config_page_check (ec, wn->item->path));
 535 
 536 	fp = find_page_finish (ec, wn->item->path);
 537 	if (fp) {
 538 		GtkAssistantPageType pt = gtk_assistant_get_page_type (assistant, page);
 539 
 540 		if (fp->is_finish && pt != GTK_ASSISTANT_PAGE_CONFIRM) {
 541 			if (fp->orig_type == GTK_ASSISTANT_PAGE_CONTENT)
 542 				fp->orig_type = pt;
 543 			gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_CONFIRM);
 544 		} else if (!fp->is_finish && pt != fp->orig_type) {
 545 			gtk_assistant_set_page_type (assistant, page, fp->orig_type);
 546 		}
 547 	}
 548 
 549 	gtk_assistant_update_buttons_state (assistant);
 550 }
 551 
 552 static gboolean
 553 ec_assistant_skip_page (EConfig *config,
 554                         struct _widget_node *wn)
 555 {
 556 	struct _check_node *cn;
 557 	gboolean skip_page = FALSE;
 558 
 559 	g_return_val_if_fail (wn->item->path != NULL, FALSE);
 560 	cn = g_hash_table_lookup (config->priv->skip_checks, wn->item->path);
 561 
 562 	if (cn != NULL) {
 563 		g_return_val_if_fail (cn->func != NULL, FALSE);
 564 		skip_page = cn->func (config, wn->item->path, cn->data);
 565 	}
 566 
 567 	return skip_page;
 568 }
 569 
 570 static gint
 571 ec_assistant_forward (gint current_page,
 572                       gpointer user_data)
 573 {
 574 	GtkAssistant *assistant;
 575 	EConfig *ec = user_data;
 576 	struct _widget_node *node;
 577 	GtkWidget *page_widget;
 578 	GList *link = NULL;
 579 	gint next_page;
 580 
 581 	/* As far as we're concerned, the GtkAssistant is just an unordered
 582 	 * collection of pages.  Our sorted list of widget nodes determines
 583 	 * the next page. */
 584 
 585 	assistant = GTK_ASSISTANT (ec->widget);
 586 	page_widget = gtk_assistant_get_nth_page (assistant, current_page);
 587 	link = ec_assistant_find_page (ec, page_widget, NULL);
 588 
 589 	g_return_val_if_fail (link != NULL, -1);
 590 	node = (struct _widget_node *) link->data;
 591 
 592 	/* If we're already on a FINISH page then we're done. */
 593 	if (node->item->type == E_CONFIG_PAGE_FINISH)
 594 		return -1;
 595 
 596 	/* Find the next E_CONFIG_PAGE* type node. */
 597 	for (link = link->next; link != NULL; link = link->next) {
 598 		gboolean node_is_page;
 599 
 600 		node = (struct _widget_node *) link->data;
 601 
 602 		if (node->empty || node->frame == NULL)
 603 			continue;
 604 
 605 		switch (node->item->type) {
 606 			case E_CONFIG_PAGE:
 607 			case E_CONFIG_PAGE_START:
 608 			case E_CONFIG_PAGE_FINISH:
 609 			case E_CONFIG_PAGE_PROGRESS:
 610 				node_is_page = TRUE;
 611 				break;
 612 			default:
 613 				node_is_page = FALSE;
 614 				break;
 615 		}
 616 
 617 		if (node_is_page && !ec_assistant_skip_page (ec, node))
 618 			break;
 619 	}
 620 
 621 	/* Find the corresponding GtkAssistant page number. */
 622 	if (link != NULL) {
 623 		node = (struct _widget_node *) link->data;
 624 		ec_assistant_find_page (ec, node->frame, &next_page);
 625 	} else
 626 		next_page = -1;
 627 
 628 	return next_page;
 629 }
 630 
 631 static void
 632 ec_widget_destroyed (GtkWidget *widget,
 633                      struct _widget_node *node)
 634 {
 635 	/* Use our own function instead of gtk_widget_destroyed()
 636 	 * so it's easier to trap EConfig widgets in a debugger. */
 637 
 638 	node->widget = NULL;
 639 }
 640 
 641 static void
 642 ec_rebuild (EConfig *emp)
 643 {
 644 	EConfigPrivate *p = emp->priv;
 645 	struct _widget_node *sectionnode = NULL, *pagenode = NULL;
 646 	GtkWidget *book = NULL, *page = NULL, *section = NULL, *root = NULL, *assistant = NULL;
 647 	gint pageno = 0, sectionno = 0, itemno = 0;
 648 	gint n_visible_widgets = 0;
 649 	GList *last_active_link = NULL;
 650 	gboolean is_assistant;
 651 	GList *link;
 652 
 653 	d (printf ("target changed, rebuilding:\n"));
 654 
 655 	/* TODO: This code is pretty complex, and will probably just
 656 	 * become more complex with time.  It could possibly be split
 657 	 * into the two base types, but there would be a lot of code
 658 	 * duplication */
 659 
 660 	/* because rebuild destroys pages, and destroying active page causes crashes */
 661 	is_assistant = GTK_IS_ASSISTANT (emp->widget);
 662 	if (is_assistant) {
 663 		GtkAssistant *assistant;
 664 		gint page_index;
 665 
 666 		assistant = GTK_ASSISTANT (emp->widget);
 667 		page_index = gtk_assistant_get_current_page (assistant);
 668 
 669 		if (page_index != -1) {
 670 			GtkWidget *nth_page;
 671 
 672 			nth_page = gtk_assistant_get_nth_page (
 673 				GTK_ASSISTANT (emp->widget), page_index);
 674 			last_active_link = ec_assistant_find_page (
 675 				emp, nth_page, NULL);
 676 		}
 677 		gtk_assistant_set_current_page (GTK_ASSISTANT (emp->widget), 0);
 678 	}
 679 
 680 	for (link = p->widgets; link != NULL; link = g_list_next (link)) {
 681 		struct _widget_node *wn = link->data;
 682 		struct _EConfigItem *item = wn->item;
 683 		const gchar *translated_label = NULL;
 684 		GtkWidget *w;
 685 
 686 		d (printf (" '%s'\n", item->path));
 687 
 688 		if (item->label != NULL)
 689 			translated_label = gettext (item->label);
 690 
 691 		/* If the last section doesn't contain any visible widgets, hide it */
 692 		if (sectionnode != NULL
 693 		    && sectionnode->frame != NULL
 694 		    && (item->type == E_CONFIG_PAGE
 695 			|| item->type == E_CONFIG_PAGE_START
 696 			|| item->type == E_CONFIG_PAGE_FINISH
 697 			|| item->type == E_CONFIG_PAGE_PROGRESS
 698 			|| item->type == E_CONFIG_SECTION
 699 			|| item->type == E_CONFIG_SECTION_TABLE)) {
 700 			if ((sectionnode->empty = (itemno == 0 || n_visible_widgets == 0))) {
 701 				if (sectionnode->real_frame)
 702 					gtk_widget_hide (sectionnode->real_frame);
 703 
 704 				if (sectionnode->frame)
 705 					gtk_widget_hide (sectionnode->frame);
 706 
 707 				sectionno--;
 708 			} else {
 709 				if (sectionnode->real_frame)
 710 					gtk_widget_show (sectionnode->real_frame);
 711 
 712 				if (sectionnode->frame)
 713 					gtk_widget_show (sectionnode->frame);
 714 			}
 715 
 716 			d (printf ("%s section '%s' [sections=%d]\n", sectionnode->empty?"hiding":"showing", sectionnode->item->path, sectionno));
 717 		}
 718 
 719 		/* If the last page doesn't contain anything, hide it */
 720 		if (pagenode != NULL
 721 		    && pagenode->frame != NULL
 722 		    && (item->type == E_CONFIG_PAGE
 723 			|| item->type == E_CONFIG_PAGE_START
 724 			|| item->type == E_CONFIG_PAGE_FINISH
 725 			|| item->type == E_CONFIG_PAGE_PROGRESS)) {
 726 			if ((pagenode->empty = sectionno == 0)) {
 727 				gtk_widget_hide (pagenode->frame);
 728 				pageno--;
 729 			} else
 730 				gtk_widget_show (pagenode->frame);
 731 			d (printf ("%s page '%s' [section=%d]\n", pagenode->empty?"hiding":"showing", pagenode->item->path, pageno));
 732 		}
 733 
 734 		/* Now process the item */
 735 		switch (item->type) {
 736 		case E_CONFIG_BOOK:
 737 		case E_CONFIG_ASSISTANT:
 738 			/* Only one of BOOK or ASSISTANT may be define, it
 739 			 * is used by the defining code to mark the
 740 			 * type of the config window.  It is
 741 			 * cross-checked with the code's defined
 742 			 * type. */
 743 			if (root != NULL) {
 744 				g_warning ("EConfig book/assistant redefined at: %s", item->path);
 745 				break;
 746 			}
 747 
 748 			if (wn->widget == NULL) {
 749 				if (item->type != emp->type) {
 750 					g_warning ("EConfig book/assistant type mismatch");
 751 					break;
 752 				}
 753 				if (item->factory) {
 754 					root = item->factory (
 755 						emp, item, NULL, wn->widget,
 756 						0, wn->context->data);
 757 				} else if (item->type == E_CONFIG_BOOK) {
 758 					root = gtk_notebook_new ();
 759 					gtk_widget_show (root);
 760 				} else if (item->type == E_CONFIG_ASSISTANT) {
 761 					root = gtk_assistant_new ();
 762 				} else
 763 					abort ();
 764 
 765 				if (item->type == E_CONFIG_ASSISTANT) {
 766 					g_signal_connect_swapped (
 767 						root, "apply",
 768 						G_CALLBACK (e_config_commit), emp);
 769 					g_signal_connect_swapped (
 770 						root, "cancel",
 771 						G_CALLBACK (e_config_abort), emp);
 772 					g_signal_connect (
 773 						root, "cancel",
 774 						G_CALLBACK (gtk_widget_destroy), emp);
 775 					g_signal_connect (
 776 						root, "close",
 777 						G_CALLBACK (gtk_widget_destroy), NULL);
 778 					g_signal_connect_swapped (
 779 						root, "prepare",
 780 						G_CALLBACK (ec_assistant_check_current), emp);
 781 					gtk_assistant_set_forward_page_func (
 782 						GTK_ASSISTANT (root),
 783 						ec_assistant_forward, emp, NULL);
 784 				}
 785 
 786 				emp->widget = root;
 787 				wn->widget = root;
 788 			} else {
 789 				root = wn->widget;
 790 			}
 791 
 792 			if (item->type == E_CONFIG_BOOK)
 793 				book = root;
 794 			else
 795 				assistant = root;
 796 
 797 			page = NULL;
 798 			pagenode = NULL;
 799 			section = NULL;
 800 			sectionnode = NULL;
 801 			pageno = 0;
 802 			sectionno = 0;
 803 			break;
 804 		case E_CONFIG_PAGE_START:
 805 		case E_CONFIG_PAGE_FINISH:
 806 			if (root == NULL) {
 807 				g_warning ("EConfig page defined before container widget: %s", item->path);
 808 				break;
 809 			}
 810 			if (emp->type != E_CONFIG_ASSISTANT) {
 811 				g_warning ("EConfig assistant start/finish pages can't be used on E_CONFIG_BOOKs");
 812 				break;
 813 			}
 814 
 815 			if (wn->widget == NULL) {
 816 				if (item->factory) {
 817 					page = item->factory (
 818 						emp, item, root, wn->frame,
 819 						pageno, wn->context->data);
 820 				} else {
 821 					page = gtk_vbox_new (FALSE, 0);
 822 					gtk_container_set_border_width (GTK_CONTAINER (page), 12);
 823 					if (pagenode) {
 824 						/* put after */
 825 						gint index = -1;
 826 						ec_assistant_find_page (emp, pagenode->frame, &index);
 827 						gtk_assistant_insert_page (GTK_ASSISTANT (assistant), page, index + 1);
 828 					} else {
 829 						gtk_assistant_prepend_page (GTK_ASSISTANT (assistant), page);
 830 					}
 831 
 832 					gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, item->type == E_CONFIG_PAGE_START ? GTK_ASSISTANT_PAGE_INTRO : GTK_ASSISTANT_PAGE_CONFIRM);
 833 					gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, translated_label);
 834 					gtk_widget_show_all (page);
 835 				}
 836 
 837 				if (wn->widget != NULL && wn->widget != page) {
 838 					gtk_widget_destroy (wn->widget);
 839 				}
 840 
 841 				wn->frame = page;
 842 				wn->widget = page;
 843 
 844 				if (page) {
 845 					const gchar *empty_xpm_img[] = {
 846 						"75 1 2 1",
 847 						"	c None",
 848 						".	c #FFFFFF",
 849 						"                                                                           "};
 850 
 851 					/* left side place with a blue background on a start and finish page */
 852 					GdkPixbuf *spacer = gdk_pixbuf_new_from_xpm_data (empty_xpm_img);
 853 
 854 					gtk_assistant_set_page_side_image (GTK_ASSISTANT (assistant), page, spacer);
 855 
 856 					g_object_unref (spacer);
 857 				}
 858 			}
 859 
 860 			pageno++;
 861 			page = NULL;
 862 			pagenode = wn; /* need this for previous page linking */
 863 			section = NULL;
 864 			sectionnode = NULL;
 865 			sectionno = 1; /* never want to hide these */
 866 			break;
 867 		case E_CONFIG_PAGE:
 868 		case E_CONFIG_PAGE_PROGRESS:
 869 			/* CONFIG_PAGEs depend on the config type.
 870 			 * E_CONFIG_BOOK:
 871 				The page is a VBox, stored in the notebook.
 872 			 * E_CONFIG_ASSISTANT
 873 				The page is a VBox, stored in the GtkAssistant,
 874 				any sections automatically added inside it. */
 875 			sectionno = 0;
 876 			if (root == NULL) {
 877 				g_warning ("EConfig page defined before container widget: %s", item->path);
 878 				break;
 879 			}
 880 			if (item->type == E_CONFIG_PAGE_PROGRESS &&
 881 			    emp->type != E_CONFIG_ASSISTANT) {
 882 				g_warning ("EConfig assistant progress pages can't be used on E_CONFIG_BOOKs");
 883 				break;
 884 			}
 885 
 886 			if (item->factory) {
 887 				page = item->factory (
 888 					emp, item, root, wn->frame,
 889 					pageno, wn->context->data);
 890 				if (emp->type == E_CONFIG_ASSISTANT) {
 891 					wn->frame = page;
 892 				} else {
 893 					wn->frame = page;
 894 					if (page)
 895 						gtk_notebook_reorder_child ((GtkNotebook *) book, page, pageno);
 896 				}
 897 				if (page)
 898 					sectionno = 1;
 899 			} else if (wn->widget == NULL) {
 900 				if (emp->type == E_CONFIG_ASSISTANT) {
 901 					page = gtk_vbox_new (FALSE, 0);
 902 					gtk_container_set_border_width (GTK_CONTAINER (page), 12);
 903 					if (pagenode) {
 904 						/* put after */
 905 						gint index = -1;
 906 						ec_assistant_find_page (emp, pagenode->frame, &index);
 907 						gtk_assistant_insert_page (GTK_ASSISTANT (assistant), page, index + 1);
 908 					} else {
 909 						gtk_assistant_prepend_page (GTK_ASSISTANT (assistant), page);
 910 					}
 911 
 912 					gtk_assistant_set_page_type (GTK_ASSISTANT (assistant), page, item->type == E_CONFIG_PAGE ? GTK_ASSISTANT_PAGE_CONTENT : GTK_ASSISTANT_PAGE_PROGRESS);
 913 					gtk_assistant_set_page_title (GTK_ASSISTANT (assistant), page, translated_label);
 914 					gtk_widget_show_all (page);
 915 
 916 					wn->frame = page;
 917 				} else {
 918 					w = gtk_label_new_with_mnemonic (translated_label);
 919 					gtk_widget_show (w);
 920 					page = gtk_vbox_new (FALSE, 12);
 921 					gtk_container_set_border_width ((GtkContainer *) page, 12);
 922 					gtk_widget_show (page);
 923 					gtk_notebook_insert_page ((GtkNotebook *) book, page, w, pageno);
 924 					gtk_container_child_set (GTK_CONTAINER (book), page, "tab-fill", FALSE, "tab-expand", FALSE, NULL);
 925 					wn->frame = page;
 926 				}
 927 			} else
 928 				page = wn->widget;
 929 
 930 			d (printf ("page %d:%s widget %p\n", pageno, item->path, page));
 931 
 932 			if (wn->widget && wn->widget != page) {
 933 				d (printf ("destroy old widget for page '%s' (%p)\n", item->path, wn->widget));
 934 				gtk_widget_destroy (wn->widget);
 935 			}
 936 
 937 			pageno++;
 938 			pagenode = wn;
 939 			section = NULL;
 940 			sectionnode = NULL;
 941 			wn->widget = page;
 942 			if (page)
 943 				g_signal_connect (
 944 					page, "destroy",
 945 					G_CALLBACK (ec_widget_destroyed), wn);
 946 			break;
 947 		case E_CONFIG_SECTION:
 948 		case E_CONFIG_SECTION_TABLE:
 949 			/* The section factory is always called with
 950 			 * the parent vbox object.  Even for assistant pages. */
 951 			if (page == NULL) {
 952 				/*g_warning("EConfig section '%s' has no parent page", item->path);*/
 953 				section = NULL;
 954 				wn->widget = NULL;
 955 				wn->frame = NULL;
 956 				goto nopage;
 957 			}
 958 
 959 			itemno = 0;
 960 			n_visible_widgets = 0;
 961 
 962 			d (printf ("Building section %s - '%s' - %s factory\n", item->path, item->label, item->factory ? "with" : "without"));
 963 
 964 			if (item->factory) {
 965 				/* For sections, we pass an extra argument to the usual EConfigItemFactoryFunc.
 966 				 * If this is an automatically-generated section, that extra argument (real_frame from
 967 				 * EConfigItemSectionFactoryFunc) will contain the actual GtkFrame upon returning.
 968 				 */
 969 				EConfigItemSectionFactoryFunc factory = (EConfigItemSectionFactoryFunc) item->factory;
 970 
 971 				section = factory (
 972 					emp, item, page, wn->widget, 0,
 973 					wn->context->data, &wn->real_frame);
 974 				wn->frame = section;
 975 				if (section)
 976 					itemno = 1;
 977 
 978 				if (factory != ech_config_section_factory) {
 979 					/* This means there is a section that came from a user-specified factory,
 980 					 * so we don't know what is inside the section.  In that case, we increment
 981 					 * n_visible_widgets so that the section will not get hidden later (we don't know
 982 					 * if the section is empty or not, so we cannot decide to hide it).
 983 					 *
 984 					 * For automatically-generated sections, we use a special ech_config_section_factory() -
 985 					 * see emph_construct_item().
 986 					 */
 987 					n_visible_widgets++;
 988 					d (printf ("  n_visible_widgets++ because there is a section factory -> frame=%p\n", section));
 989 				}
 990 
 991 				if (section
 992 				    && ((item->type == E_CONFIG_SECTION && !GTK_IS_BOX (section))
 993 					|| (item->type == E_CONFIG_SECTION_TABLE && !GTK_IS_TABLE (section))))
 994 					g_warning ("EConfig section type is wrong");
 995 			} else {
 996 				GtkWidget *frame;
 997 				GtkWidget *label = NULL;
 998 
 999 				if (wn->frame) {
1000 					d (printf ("Item %s, clearing generated section widget\n", wn->item->path));
1001 					gtk_widget_destroy (wn->frame);
1002 					wn->widget = NULL;
1003 					wn->frame = NULL;
1004 				}
1005 
1006 				if (translated_label != NULL) {
1007 					gchar *txt = g_markup_printf_escaped ("<span weight=\"bold\">%s</span>", translated_label);
1008 
1009 					label = g_object_new (
1010 						gtk_label_get_type (),
1011 						"label", txt,
1012 						"use_markup", TRUE,
1013 						"xalign", 0.0, NULL);
1014 					g_free (txt);
1015 				}
1016 
1017 				if (item->type == E_CONFIG_SECTION)
1018 					section = gtk_vbox_new (FALSE, 6);
1019 				else {
1020 					section = gtk_table_new (1, 1, FALSE);
1021 					gtk_table_set_col_spacings ((GtkTable *) section, 6);
1022 					gtk_table_set_row_spacings ((GtkTable *) section, 6);
1023 				}
1024 
1025 				frame = g_object_new (
1026 					gtk_frame_get_type (),
1027 					"shadow_type", GTK_SHADOW_NONE,
1028 					"label_widget", label,
1029 					"child", g_object_new (gtk_alignment_get_type (),
1030 					"left_padding", 12,
1031 					"top_padding", 6,
1032 					"child", section, NULL),
1033 					NULL);
1034 				gtk_widget_show_all (frame);
1035 				gtk_box_pack_start ((GtkBox *) page, frame, FALSE, FALSE, 0);
1036 				wn->frame = frame;
1037 			}
1038 		nopage:
1039 			if (wn->widget && wn->widget != section) {
1040 				d (printf ("destroy old widget for section '%s'\n", item->path));
1041 				gtk_widget_destroy (wn->widget);
1042 			}
1043 
1044 			d (printf ("Item %s, setting section widget\n", wn->item->path));
1045 
1046 			sectionno++;
1047 			wn->widget = section;
1048 			if (section)
1049 				g_signal_connect (
1050 					section, "destroy",
1051 					G_CALLBACK (ec_widget_destroyed), wn);
1052 			sectionnode = wn;
1053 			break;
1054 		case E_CONFIG_ITEM:
1055 		case E_CONFIG_ITEM_TABLE:
1056 			/* generated sections never retain their widgets on a rebuild */
1057 			if (sectionnode->item->factory == NULL)
Access to field 'item' results in a dereference of a null pointer (loaded from variable 'sectionnode')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Access to field 'item' results in a dereference of a null pointer (loaded from variable 'sectionnode')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1058 wn->widget = NULL; 1059 1060 /* ITEMs are called with the section parent. 1061 * The type depends on the section type, 1062 * either a GtkTable, or a GtkVBox */ 1063 w = NULL; 1064 if (section == NULL) { 1065 wn->widget = NULL; 1066 wn->frame = NULL; 1067 g_warning ("EConfig item has no parent section: %s", item->path); 1068 } else if ((item->type == E_CONFIG_ITEM && !GTK_IS_BOX (section)) 1069 || (item->type == E_CONFIG_ITEM_TABLE && !GTK_IS_TABLE (section))) 1070 g_warning ("EConfig item parent type is incorrect: %s", item->path); 1071 else if (item->factory) 1072 w = item->factory ( 1073 emp, item, section, wn->widget, 1074 0, wn->context->data); 1075 1076 if (wn->widget && wn->widget != w) { 1077 d (printf ("destroy old widget for item '%s'\n", item->path)); 1078 gtk_widget_destroy (wn->widget); 1079 } 1080 1081 wn->widget = w; 1082 if (w) { 1083 g_signal_connect ( 1084 w, "destroy", 1085 G_CALLBACK (ec_widget_destroyed), wn); 1086 itemno++; 1087 1088 if (gtk_widget_get_visible (w)) 1089 n_visible_widgets++; 1090 } 1091 break; 1092 } 1093 } 1094 1095 /* If the last section doesn't contain any visible widgets, hide it */ 1096 if (sectionnode != NULL && sectionnode->frame != NULL) { 1097 d (printf ("Section %s - %d visible widgets (frame=%p)\n", sectionnode->item->path, n_visible_widgets, sectionnode->frame)); 1098 if ((sectionnode->empty = (itemno == 0 || n_visible_widgets == 0))) { 1099 if (sectionnode->real_frame) 1100 gtk_widget_hide (sectionnode->real_frame); 1101 1102 if (sectionnode->frame) 1103 gtk_widget_hide (sectionnode->frame); 1104 1105 sectionno--; 1106 } else { 1107 if (sectionnode->real_frame) 1108 gtk_widget_show (sectionnode->real_frame); 1109 1110 if (sectionnode->frame) 1111 gtk_widget_show (sectionnode->frame); 1112 } 1113 d (printf ("%s section '%s' [sections=%d]\n", sectionnode->empty?"hiding":"showing", sectionnode->item->path, sectionno)); 1114 } 1115 1116 /* If the last page doesn't contain anything, hide it */ 1117 if (pagenode != NULL && pagenode->frame != NULL) { 1118 if ((pagenode->empty = sectionno == 0)) { 1119 gtk_widget_hide (pagenode->frame); 1120 pageno--; 1121 } else 1122 gtk_widget_show (pagenode->frame); 1123 d (printf ("%s page '%s' [section=%d]\n", pagenode->empty?"hiding":"showing", pagenode->item->path, pageno)); 1124 } 1125 1126 if (book) { 1127 /* make this depend on flags?? */ 1128 if (gtk_notebook_get_n_pages ((GtkNotebook *) book) == 1) { 1129 gtk_notebook_set_show_tabs ((GtkNotebook *) book, FALSE); 1130 gtk_notebook_set_show_border ((GtkNotebook *) book, FALSE); 1131 } 1132 } 1133 1134 if (is_assistant && last_active_link != NULL) { 1135 GtkAssistant *assistant; 1136 struct _widget_node *wn; 1137 gint page_index = -1; 1138 1139 wn = last_active_link->data; 1140 assistant = GTK_ASSISTANT (emp->widget); 1141 ec_assistant_find_page (emp, wn->frame, &page_index); 1142 gtk_assistant_set_current_page (assistant, page_index); 1143 } 1144 } 1145 1146 /** 1147 * e_config_set_target: 1148 * @emp: An initialised EConfig. 1149 * @target: A target allocated from @emp. 1150 * 1151 * Sets the target object for the config window. Generally the target 1152 * is set only once, and will supply its own "changed" signal which 1153 * can be used to drive the modal. This is a virtual method so that 1154 * the implementing class can connect to the changed signal and 1155 * initiate a e_config_target_changed() call where appropriate. 1156 **/ 1157 void 1158 e_config_set_target (EConfig *emp, 1159 EConfigTarget *target) 1160 { 1161 if (emp->target != target) 1162 ((EConfigClass *) G_OBJECT_GET_CLASS (emp))->set_target (emp, target); 1163 } 1164 1165 static void 1166 ec_widget_destroy (GtkWidget *w, 1167 EConfig *ec) 1168 { 1169 if (ec->target) { 1170 e_config_target_free (ec, ec->target); 1171 ec->target = NULL; 1172 } 1173 1174 g_object_unref (ec); 1175 } 1176 1177 /** 1178 * e_config_create_widget: 1179 * @emp: An initialised EConfig object. 1180 * 1181 * Create the widget described by @emp. Only the core widget 1182 * appropriate for the given type is created, i.e. a GtkNotebook for 1183 * the E_CONFIG_BOOK type and a GtkAssistant for the E_CONFIG_ASSISTANT 1184 * type. 1185 * 1186 * This object will be self-driving, but will not close itself once 1187 * complete. 1188 * 1189 * Unless reffed otherwise, the management object @emp will be 1190 * finalized when the widget is. 1191 * 1192 * Return value: The widget, also available in @emp.widget 1193 **/ 1194 GtkWidget * 1195 e_config_create_widget (EConfig *emp) 1196 { 1197 EConfigPrivate *p = emp->priv; 1198 GPtrArray *items = g_ptr_array_new (); 1199 GList *link; 1200 GSList *l; 1201 gint i; 1202 1203 g_return_val_if_fail (emp->target != NULL, NULL); 1204 1205 ec_add_static_items (emp); 1206 1207 /* FIXME: need to override old ones with new names */ 1208 link = p->menus; 1209 while (link != NULL) { 1210 struct _menu_node *mnode = link->data; 1211 1212 for (l = mnode->menu; l; l = l->next) { 1213 struct _EConfigItem *item = l->data; 1214 struct _widget_node *wn = g_malloc0 (sizeof (*wn)); 1215 1216 wn->item = item; 1217 wn->context = mnode; 1218 wn->config = emp; 1219 g_ptr_array_add (items, wn); 1220 } 1221 1222 link = g_list_next (link); 1223 } 1224 1225 qsort (items->pdata, items->len, sizeof (items->pdata[0]), ep_cmp); 1226 1227 for (i = 0; i < items->len; i++) 1228 p->widgets = g_list_append (p->widgets, items->pdata[i]); 1229 1230 g_ptr_array_free (items, TRUE); 1231 ec_rebuild (emp); 1232 1233 /* auto-unref it */ 1234 g_signal_connect ( 1235 emp->widget, "destroy", 1236 G_CALLBACK (ec_widget_destroy), emp); 1237 1238 /* FIXME: for some reason ec_rebuild puts the widget on page 1, this is just to override that */ 1239 if (emp->type == E_CONFIG_BOOK) 1240 gtk_notebook_set_current_page ((GtkNotebook *) emp->widget, 0); 1241 else { 1242 gtk_window_set_position (GTK_WINDOW (emp->widget), GTK_WIN_POS_CENTER); 1243 } 1244 1245 return emp->widget; 1246 } 1247 1248 static void 1249 ec_dialog_response (GtkWidget *d, 1250 gint id, 1251 EConfig *ec) 1252 { 1253 if (id == GTK_RESPONSE_OK) 1254 e_config_commit (ec); 1255 else 1256 e_config_abort (ec); 1257 1258 gtk_widget_destroy (d); 1259 } 1260 1261 /** 1262 * e_config_create_window: 1263 * @emp: Initialised and configured EMConfig derived instance. 1264 * @parent: Parent window or NULL. 1265 * @title: Title of window or dialog. 1266 * 1267 * Create a managed GtkWindow object from @emp. This window will be 1268 * fully driven by the EConfig @emp. If @emp.type is 1269 * @E_CONFIG_ASSISTANT, then this will be a toplevel GtkWindow containing 1270 * a GtkAssistant. If it is @E_CONFIG_BOOK then it will be a GtkDialog 1271 * containing a Notebook. 1272 * 1273 * Unless reffed otherwise, the management object @emp will be 1274 * finalized when the widget is. 1275 * 1276 * Return value: The window widget. This is also stored in @emp.window. 1277 **/ 1278 GtkWidget * 1279 e_config_create_window (EConfig *emp, 1280 GtkWindow *parent, 1281 const gchar *title) 1282 { 1283 GtkWidget *window; 1284 1285 e_config_create_widget (emp); 1286 1287 if (emp->type == E_CONFIG_BOOK) { 1288 GtkWidget *content_area; 1289 1290 window = gtk_dialog_new_with_buttons ( 1291 title, parent, 1292 GTK_DIALOG_DESTROY_WITH_PARENT, 1293 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, 1294 GTK_STOCK_APPLY, GTK_RESPONSE_OK, 1295 NULL); 1296 g_signal_connect ( 1297 window, "response", 1298 G_CALLBACK (ec_dialog_response), emp); 1299 1300 gtk_container_set_border_width (GTK_CONTAINER (window), 5); 1301 gtk_container_set_border_width (GTK_CONTAINER (emp->widget), 5); 1302 1303 content_area = 1304 gtk_dialog_get_content_area (GTK_DIALOG (window)); 1305 gtk_box_pack_start ( 1306 GTK_BOX (content_area), emp->widget, TRUE, TRUE, 0); 1307 } else { 1308 /* response is handled directly by the assistant stuff */ 1309 window = emp->widget; 1310 gtk_window_set_title (GTK_WINDOW (window), title); 1311 } 1312 1313 emp->window = window; 1314 gtk_widget_show (window); 1315 1316 return window; 1317 } 1318 1319 static void 1320 ec_call_page_check (EConfig *emp) 1321 { 1322 if (emp->type == E_CONFIG_ASSISTANT) { 1323 ec_assistant_check_current (emp); 1324 } else { 1325 if (emp->window) { 1326 if (e_config_page_check (emp, NULL)) { 1327 gtk_dialog_set_response_sensitive ((GtkDialog *) emp->window, GTK_RESPONSE_OK, TRUE); 1328 } else { 1329 gtk_dialog_set_response_sensitive ((GtkDialog *) emp->window, GTK_RESPONSE_OK, FALSE); 1330 } 1331 } 1332 } 1333 } 1334 1335 static gboolean 1336 ec_idle_handler_for_rebuild (gpointer data) 1337 { 1338 EConfig *emp = (EConfig *) data; 1339 1340 ec_rebuild (emp); 1341 ec_call_page_check (emp); 1342 1343 return FALSE; 1344 } 1345 1346 /** 1347 * e_config_target_changed: 1348 * @emp: an #EConfig 1349 * @how: an enum value indicating how the target has changed 1350 * 1351 * Indicate that the target has changed. This may be called by the 1352 * self-aware target itself, or by the driving code. If @how is 1353 * %E_CONFIG_TARGET_CHANGED_REBUILD, then the entire configuration 1354 * widget may be recreated based on the changed target. 1355 * 1356 * This is used to sensitise Assistant next/back buttons and the Apply 1357 * button for the Notebook mode. 1358 **/ 1359 void 1360 e_config_target_changed (EConfig *emp, 1361 e_config_target_change_t how) 1362 { 1363 if (how == E_CONFIG_TARGET_CHANGED_REBUILD) { 1364 g_idle_add (ec_idle_handler_for_rebuild, emp); 1365 } else { 1366 ec_call_page_check (emp); 1367 } 1368 1369 /* virtual method/signal? */ 1370 } 1371 1372 /** 1373 * e_config_abort: 1374 * @config: an #EConfig 1375 * 1376 * Signify that the stateful configuration changes must be discarded 1377 * to all listeners. This is used by self-driven assistant or notebook, or 1378 * may be used by code using the widget directly. 1379 **/ 1380 void 1381 e_config_abort (EConfig *config) 1382 { 1383 g_return_if_fail (E_IS_CONFIG (config)); 1384 1385 g_signal_emit (config, signals[ABORT], 0); 1386 } 1387 1388 /** 1389 * e_config_commit: 1390 * @ec: an #EConfig 1391 * 1392 * Signify that the stateful configuration changes should be saved. 1393 * This is used by the self-driven assistant or notebook, or may be used 1394 * by code driving the widget directly. 1395 **/ 1396 void 1397 e_config_commit (EConfig *config) 1398 { 1399 g_return_if_fail (E_IS_CONFIG (config)); 1400 1401 g_signal_emit (config, signals[COMMIT], 0); 1402 } 1403 1404 /** 1405 * e_config_page_check: 1406 * @config: an #EConfig 1407 * @pageid: the path of the page item 1408 * 1409 * Check that a given page is complete. If @pageid is NULL, then check 1410 * the whole config. No check is made that the page actually exists. 1411 * 1412 * Return value: FALSE if the data is inconsistent/incomplete. 1413 **/ 1414 gboolean 1415 e_config_page_check (EConfig *config, 1416 const gchar *pageid) 1417 { 1418 GList *link; 1419 1420 link = config->priv->checks; 1421 1422 while (link != NULL) { 1423 struct _check_node *node = link->data; 1424 1425 if ((pageid == NULL 1426 || node->pageid == NULL 1427 || strcmp (node->pageid, pageid) == 0) 1428 && !node->func (config, pageid, node->data)) { 1429 return FALSE; 1430 } 1431 1432 link = g_list_next (link); 1433 } 1434 1435 return TRUE; 1436 } 1437 1438 /** 1439 * e_config_page_get: 1440 * @ec: 1441 * @pageid: The path of the page item. 1442 * 1443 * Retrieve the page widget corresponding to @pageid. 1444 * 1445 * Return value: The page widget. It will be the root GtkNotebook 1446 * container or the GtkVBox object inside the assistant. 1447 **/ 1448 GtkWidget * 1449 e_config_page_get (EConfig *ec, 1450 const gchar *pageid) 1451 { 1452 GList *link; 1453 1454 link = ec->priv->widgets; 1455 1456 while (link != NULL) { 1457 struct _widget_node *wn = link->data; 1458 1459 if (!wn->empty 1460 && (wn->item->type == E_CONFIG_PAGE 1461 || wn->item->type == E_CONFIG_PAGE_START 1462 || wn->item->type == E_CONFIG_PAGE_FINISH 1463 || wn->item->type == E_CONFIG_PAGE_PROGRESS) 1464 && !strcmp (wn->item->path, pageid)) 1465 return wn->frame; 1466 1467 link = g_list_next (link); 1468 } 1469 1470 return NULL; 1471 } 1472 1473 /** 1474 * e_config_page_next: 1475 * @ec: 1476 * @pageid: The path of the page item. 1477 * 1478 * Find the path of the next visible page after @pageid. If @pageid 1479 * is NULL then find the first visible page. 1480 * 1481 * Return value: The path of the next page, or @NULL if @pageid was the 1482 * last configured and visible page. 1483 **/ 1484 const gchar * 1485 e_config_page_next (EConfig *ec, 1486 const gchar *pageid) 1487 { 1488 GList *link; 1489 gint found; 1490 1491 link = g_list_first (ec->priv->widgets); 1492 found = pageid == NULL ? 1 : 0; 1493 1494 while (link != NULL) { 1495 struct _widget_node *wn = link->data; 1496 1497 if (!wn->empty 1498 && (wn->item->type == E_CONFIG_PAGE 1499 || wn->item->type == E_CONFIG_PAGE_START 1500 || wn->item->type == E_CONFIG_PAGE_FINISH 1501 || wn->item->type == E_CONFIG_PAGE_PROGRESS)) { 1502 if (found) 1503 return wn->item->path; 1504 else if (strcmp (wn->item->path, pageid) == 0) 1505 found = 1; 1506 } 1507 1508 link = g_list_next (link); 1509 } 1510 1511 return NULL; 1512 } 1513 1514 /** 1515 * e_config_page_prev: 1516 * @ec: an #EConfig 1517 * @pageid: The path of the page item. 1518 * 1519 * Find the path of the previous visible page before @pageid. If @pageid 1520 * is NULL then find the last visible page. 1521 * 1522 * Return value: The path of the previous page, or @NULL if @pageid was the 1523 * first configured and visible page. 1524 **/ 1525 const gchar * 1526 e_config_page_prev (EConfig *ec, 1527 const gchar *pageid) 1528 { 1529 GList *link; 1530 gint found; 1531 1532 link = g_list_last (ec->priv->widgets); 1533 found = pageid == NULL ? 1 : 0; 1534 1535 while (link != NULL) { 1536 struct _widget_node *wn = link->data; 1537 1538 if (!wn->empty 1539 && (wn->item->type == E_CONFIG_PAGE 1540 || wn->item->type == E_CONFIG_PAGE_START 1541 || wn->item->type == E_CONFIG_PAGE_FINISH 1542 || wn->item->type == E_CONFIG_PAGE_PROGRESS)) { 1543 if (found) 1544 return wn->item->path; 1545 else if (strcmp (wn->item->path, pageid) == 0) 1546 found = 1; 1547 } 1548 1549 link = g_list_previous (link); 1550 } 1551 1552 return NULL; 1553 } 1554 1555 /* ********************************************************************** */ 1556 1557 /** 1558 * e_config_class_add_factory: 1559 * @class: Implementing class pointer. 1560 * @id: The name of the configuration window you're interested in. 1561 * This may be NULL to be called for all windows. 1562 * @func: An EConfigFactoryFunc to call when the window @id is being 1563 * created. 1564 * @data: Callback data. 1565 * 1566 * Add a config factory which will be called to add_items() any 1567 * extra items's if wants to, to the current Config window. 1568 * 1569 * TODO: Make the id a pattern? 1570 * 1571 * Return value: A handle to the factory. 1572 **/ 1573 EConfigFactory * 1574 e_config_class_add_factory (EConfigClass *class, 1575 const gchar *id, 1576 EConfigFactoryFunc func, 1577 gpointer user_data) 1578 { 1579 EConfigFactory *factory; 1580 1581 g_return_val_if_fail (E_IS_CONFIG_CLASS (class), NULL); 1582 g_return_val_if_fail (func != NULL, NULL); 1583 1584 factory = g_slice_new0 (EConfigFactory); 1585 factory->id = g_strdup (id); 1586 factory->func = func; 1587 factory->user_data = user_data; 1588 1589 class->factories = g_list_append (class->factories, factory); 1590 1591 return factory; 1592 } 1593 1594 /** 1595 * e_config_class_remove_factory: 1596 * @factory: an #EConfigFactory 1597 * 1598 * Removes a config factory. 1599 **/ 1600 void 1601 e_config_class_remove_factory (EConfigClass *class, 1602 EConfigFactory *factory) 1603 { 1604 g_return_if_fail (E_IS_CONFIG_CLASS (class)); 1605 g_return_if_fail (factory != NULL); 1606 1607 class->factories = g_list_remove (class->factories, factory); 1608 1609 g_free (factory->id); 1610 1611 g_slice_free (EConfigFactory, factory); 1612 } 1613 1614 /** 1615 * e_config_target_new: 1616 * @ep: Parent EConfig object. 1617 * @type: type, up to implementor 1618 * @size: Size of object to allocate. 1619 * 1620 * Allocate a new config target suitable for this class. Implementing 1621 * classes will define the actual content of the target. 1622 **/ 1623 gpointer e_config_target_new (EConfig *ep, gint type, gsize size) 1624 { 1625 EConfigTarget *t; 1626 1627 if (size < sizeof (EConfigTarget)) { 1628 g_warning ("Size is less than size of EConfigTarget\n"); 1629 size = sizeof (EConfigTarget); 1630 } 1631 1632 t = g_malloc0 (size); 1633 t->config = ep; 1634 g_object_ref (ep); 1635 t->type = type; 1636 1637 return t; 1638 } 1639 1640 /** 1641 * e_config_target_free: 1642 * @ep: Parent EConfig object. 1643 * @o: The target to fre. 1644 * 1645 * Free a target. The implementing class can override this method to 1646 * free custom targets. 1647 **/ 1648 void 1649 e_config_target_free (EConfig *ep, 1650 gpointer o) 1651 { 1652 EConfigTarget *t = o; 1653 1654 ((EConfigClass *) G_OBJECT_GET_CLASS (ep))->target_free (ep, t); 1655 } 1656 1657 /* ********************************************************************** */ 1658 1659 /* Config menu plugin handler */ 1660 1661 /* 1662 * <e-plugin 1663 * class="org.gnome.mail.plugin.config:1.0" 1664 * id="org.gnome.mail.plugin.config.item:1.0" 1665 * type="shlib" 1666 * location="/opt/gnome2/lib/camel/1.0/libcamelimap.so" 1667 * name="imap" 1668 * description="IMAP4 and IMAP4v1 mail store"> 1669 * <hook class="org.gnome.mail.configMenu:1.0" 1670 * handler="HandleConfig"> 1671 * <menu id="any" target="select"> 1672 * <item 1673 * type="item|toggle|radio|image|submenu|bar" 1674 * active 1675 * path="foo/bar" 1676 * label="label" 1677 * icon="foo" 1678 * activate="ep_view_emacs"/> 1679 * </menu> 1680 * </e-plugin> 1681 */ 1682 1683 #define emph ((EConfigHook *)eph) 1684 1685 static const EPluginHookTargetKey ech_item_types[] = { 1686 { "book", E_CONFIG_BOOK }, 1687 { "assistant", E_CONFIG_ASSISTANT }, 1688 1689 { "page", E_CONFIG_PAGE }, 1690 { "page_start", E_CONFIG_PAGE_START }, 1691 { "page_finish", E_CONFIG_PAGE_FINISH }, 1692 { "section", E_CONFIG_SECTION }, 1693 { "section_table", E_CONFIG_SECTION_TABLE }, 1694 { "item", E_CONFIG_ITEM }, 1695 { "item_table", E_CONFIG_ITEM_TABLE }, 1696 { NULL }, 1697 }; 1698 1699 G_DEFINE_TYPE ( 1700 EConfigHook, 1701 e_config_hook, 1702 E_TYPE_PLUGIN_HOOK) 1703 1704 static void 1705 ech_commit (EConfig *ec, 1706 EConfigHookGroup *group) 1707 { 1708 if (group->commit && group->hook->hook.plugin->enabled) 1709 e_plugin_invoke (group->hook->hook.plugin, group->commit, ec->target); 1710 } 1711 1712 static void 1713 ech_abort (EConfig *ec, 1714 EConfigHookGroup *group) 1715 { 1716 if (group->abort && group->hook->hook.plugin->enabled) 1717 e_plugin_invoke (group->hook->hook.plugin, group->abort, ec->target); 1718 } 1719 1720 static gboolean 1721 ech_check (EConfig *ec, 1722 const gchar *pageid, 1723 gpointer data) 1724 { 1725 EConfigHookGroup *group = data; 1726 EConfigHookPageCheckData hdata; 1727 1728 if (!group->hook->hook.plugin->enabled) 1729 return TRUE; 1730 1731 hdata.config = ec; 1732 hdata.target = ec->target; 1733 hdata.pageid = pageid ? pageid:""; 1734 1735 return GPOINTER_TO_INT (e_plugin_invoke (group->hook->hook.plugin, group->check, &hdata)); 1736 } 1737 1738 static void 1739 ech_config_factory (EConfig *emp, 1740 gpointer data) 1741 { 1742 EConfigHookGroup *group = data; 1743 1744 d (printf ("config factory called %s\n", group->id ? group->id:"all menus")); 1745 1746 if (emp->target->type != group->target_type 1747 || !group->hook->hook.plugin->enabled) 1748 return; 1749 1750 if (group->items) { 1751 e_config_add_items (emp, group->items, NULL, group); 1752 g_signal_connect ( 1753 emp, "abort", 1754 G_CALLBACK (ech_abort), group); 1755 g_signal_connect ( 1756 emp, "commit", 1757 G_CALLBACK (ech_commit), group); 1758 } 1759 1760 if (group->check) 1761 e_config_add_page_check (emp, NULL, ech_check, group); 1762 } 1763 1764 static void 1765 emph_free_item (struct _EConfigItem *item) 1766 { 1767 g_free (item->path); 1768 g_free (item->label); 1769 g_free (item->user_data); 1770 g_free (item); 1771 } 1772 1773 static void 1774 emph_free_group (EConfigHookGroup *group) 1775 { 1776 g_slist_foreach (group->items, (GFunc) emph_free_item, NULL); 1777 g_slist_free (group->items); 1778 1779 g_free (group->id); 1780 g_free (group); 1781 } 1782 1783 static GtkWidget * 1784 ech_config_widget_factory (EConfig *config, 1785 EConfigItem *item, 1786 GtkWidget *parent, 1787 GtkWidget *old, 1788 gint position, 1789 gpointer data) 1790 { 1791 EConfigHookGroup *group = data; 1792 EConfigHookItemFactoryData factory_data; 1793 EPlugin *plugin; 1794 1795 factory_data.config = config; 1796 factory_data.item = item; 1797 factory_data.target = config->target; 1798 factory_data.parent = parent; 1799 factory_data.old = old; 1800 factory_data.position = position; 1801 1802 plugin = group->hook->hook.plugin; 1803 return e_plugin_invoke (plugin, item->user_data, &factory_data); 1804 } 1805 1806 static GtkWidget * 1807 ech_config_section_factory (EConfig *config, 1808 EConfigItem *item, 1809 GtkWidget *parent, 1810 GtkWidget *old, 1811 gint position, 1812 gpointer data, 1813 GtkWidget **real_frame) 1814 { 1815 EConfigHookGroup *group = data; 1816 GtkWidget *label = NULL; 1817 GtkWidget *widget; 1818 EPlugin *plugin; 1819 1820 if (item->label != NULL) { 1821 const gchar *translated; 1822 gchar *markup; 1823 1824 translated = gettext (item->label); 1825 markup = g_markup_printf_escaped ("<b>%s</b>", translated); 1826 1827 label = gtk_label_new (markup); 1828 gtk_label_set_use_markup (GTK_LABEL (label), TRUE); 1829 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); 1830 gtk_widget_show (label); 1831 1832 g_free (markup); 1833 } 1834 1835 widget = gtk_frame_new (NULL); 1836 gtk_frame_set_label_widget (GTK_FRAME (widget), label); 1837 gtk_frame_set_shadow_type (GTK_FRAME (widget), GTK_SHADOW_NONE); 1838 gtk_box_pack_start (GTK_BOX (parent), widget, FALSE, FALSE, 0); 1839 1840 *real_frame = widget; 1841 1842 /* This is why we have a custom factory for sections. 1843 * When the plugin is disabled the frame is invisible. */ 1844 plugin = group->hook->hook.plugin; 1845 g_object_bind_property ( 1846 plugin, "enabled", 1847 widget, "visible", 1848 G_BINDING_SYNC_CREATE); 1849 1850 parent = widget; 1851 1852 widget = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); 1853 gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 6, 0, 12, 0); 1854 gtk_container_add (GTK_CONTAINER (parent), widget); 1855 gtk_widget_show (widget); 1856 1857 parent = widget; 1858 1859 switch (item->type) { 1860 case E_CONFIG_SECTION: 1861 widget = gtk_vbox_new (FALSE, 6); 1862 break; 1863 1864 case E_CONFIG_SECTION_TABLE: 1865 widget = gtk_table_new (1, 1, FALSE); 1866 gtk_table_set_col_spacings (GTK_TABLE (widget), 6); 1867 gtk_table_set_row_spacings (GTK_TABLE (widget), 6); 1868 break; 1869 1870 default: 1871 g_return_val_if_reached (NULL); 1872 } 1873 1874 gtk_container_add (GTK_CONTAINER (parent), widget); 1875 gtk_widget_show (widget); 1876 1877 return widget; 1878 } 1879 1880 static struct _EConfigItem * 1881 emph_construct_item (EPluginHook *eph, 1882 EConfigHookGroup *menu, 1883 xmlNodePtr root, 1884 EConfigHookTargetMap *map) 1885 { 1886 struct _EConfigItem *item; 1887 1888 d (printf (" loading config item\n")); 1889 item = g_malloc0 (sizeof (*item)); 1890 if ((item->type = e_plugin_hook_id (root, ech_item_types, "type")) == -1) 1891 goto error; 1892 item->path = e_plugin_xml_prop (root, "path"); 1893 item->label = e_plugin_xml_prop_domain (root, "label", eph->plugin->domain); 1894 item->user_data = e_plugin_xml_prop (root, "factory"); 1895 1896 if (item->path == NULL 1897 || (item->label == NULL && item->user_data == NULL)) 1898 goto error; 1899 1900 if (item->user_data) 1901 item->factory = ech_config_widget_factory; 1902 else if (item->type == E_CONFIG_SECTION) 1903 item->factory = (EConfigItemFactoryFunc) ech_config_section_factory; 1904 else if (item->type == E_CONFIG_SECTION_TABLE) 1905 item->factory = (EConfigItemFactoryFunc) ech_config_section_factory; 1906 1907 d (printf (" path=%s label=%s factory=%s\n", item->path, item->label, (gchar *) item->user_data)); 1908 1909 return item; 1910 error: 1911 d (printf ("error!\n")); 1912 emph_free_item (item); 1913 return NULL; 1914 } 1915 1916 static EConfigHookGroup * 1917 emph_construct_menu (EPluginHook *eph, 1918 xmlNodePtr root) 1919 { 1920 EConfigHookGroup *menu; 1921 xmlNodePtr node; 1922 EConfigHookTargetMap *map; 1923 EConfigHookClass *class = (EConfigHookClass *) G_OBJECT_GET_CLASS (eph); 1924 gchar *tmp; 1925 1926 d (printf (" loading menu\n")); 1927 menu = g_malloc0 (sizeof (*menu)); 1928 1929 tmp = (gchar *) xmlGetProp (root, (const guchar *)"target"); 1930 if (tmp == NULL) 1931 goto error; 1932 map = g_hash_table_lookup (class->target_map, tmp); 1933 xmlFree (tmp); 1934 if (map == NULL) 1935 goto error; 1936 1937 menu->target_type = map->id; 1938 menu->id = e_plugin_xml_prop (root, "id"); 1939 if (menu->id == NULL) { 1940 g_warning ( 1941 "Plugin '%s' missing 'id' field in group for '%s'\n", 1942 eph->plugin->name, 1943 ((EPluginHookClass *) G_OBJECT_GET_CLASS (eph))->id); 1944 goto error; 1945 } 1946 menu->check = e_plugin_xml_prop (root, "check"); 1947 menu->commit = e_plugin_xml_prop (root, "commit"); 1948 menu->abort = e_plugin_xml_prop (root, "abort"); 1949 menu->hook = (EConfigHook *) eph; 1950 node = root->children; 1951 while (node) { 1952 if (0 == strcmp ((gchar *) node->name, "item")) { 1953 struct _EConfigItem *item; 1954 1955 item = emph_construct_item (eph, menu, node, map); 1956 if (item) 1957 menu->items = g_slist_append (menu->items, item); 1958 } 1959 node = node->next; 1960 } 1961 1962 return menu; 1963 error: 1964 emph_free_group (menu); 1965 return NULL; 1966 } 1967 1968 static gint 1969 emph_construct (EPluginHook *eph, 1970 EPlugin *ep, 1971 xmlNodePtr root) 1972 { 1973 xmlNodePtr node; 1974 EConfigClass *class; 1975 1976 d (printf ("loading config hook\n")); 1977 1978 if (((EPluginHookClass *) e_config_hook_parent_class)->construct (eph, ep, root) == -1) 1979 return -1; 1980 1981 class = ((EConfigHookClass *) G_OBJECT_GET_CLASS (eph))->config_class; 1982 1983 node = root->children; 1984 while (node) { 1985 if (strcmp ((gchar *) node->name, "group") == 0) { 1986 EConfigHookGroup *group; 1987 1988 group = emph_construct_menu (eph, node); 1989 if (group) { 1990 e_config_class_add_factory (class, group->id, ech_config_factory, group); 1991 emph->groups = g_slist_append (emph->groups, group); 1992 } 1993 } 1994 node = node->next; 1995 } 1996 1997 eph->plugin = ep; 1998 1999 return 0; 2000 } 2001 2002 static void 2003 emph_finalize (GObject *o) 2004 { 2005 EPluginHook *eph = (EPluginHook *) o; 2006 2007 g_slist_foreach (emph->groups, (GFunc) emph_free_group, NULL); 2008 g_slist_free (emph->groups); 2009 2010 ((GObjectClass *) e_config_hook_parent_class)->finalize (o); 2011 } 2012 2013 static void 2014 e_config_hook_class_init (EConfigHookClass *class) 2015 { 2016 GObjectClass *object_class; 2017 EPluginHookClass *plugin_hook_class; 2018 2019 object_class = G_OBJECT_CLASS (class); 2020 object_class->finalize = emph_finalize; 2021 2022 plugin_hook_class = E_PLUGIN_HOOK_CLASS (class); 2023 plugin_hook_class->construct = emph_construct; 2024 2025 /* this is actually an abstract implementation but list it anyway */ 2026 plugin_hook_class->id = "org.gnome.evolution.config:1.0"; 2027 2028 class->target_map = g_hash_table_new (g_str_hash, g_str_equal); 2029 class->config_class = g_type_class_ref (e_config_get_type ()); 2030 } 2031 2032 static void 2033 e_config_hook_init (EConfigHook *hook) 2034 { 2035 } 2036 2037 /** 2038 * e_config_hook_class_add_target_map: 2039 * 2040 * @class: The dervied EconfigHook class. 2041 * @map: A map used to describe a single EConfigTarget type for this 2042 * class. 2043 * 2044 * Add a targe tmap to a concrete derived class of EConfig. The 2045 * target map enumates the target types available for the implenting 2046 * class. 2047 **/ 2048 void 2049 e_config_hook_class_add_target_map (EConfigHookClass *class, 2050 const EConfigHookTargetMap *map) 2051 { 2052 g_hash_table_insert (class->target_map, (gpointer) map->type, (gpointer) map); 2053 }