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)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(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 }