1 /********************************************************************\
2 * gnc-component-manager.h - GUI component manager interface *
3 * Copyright (C) 2000 Dave Peticolas <dave@krondo.com> *
4 * *
5 * This program is free software; you can redistribute it and/or *
6 * modify it under the terms of the GNU General Public License as *
7 * published by the Free Software Foundation; either version 2 of *
8 * the License, or (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License*
16 * along with this program; if not, write to the Free Software *
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
18 \********************************************************************/
19
20 #include <config.h>
21
22 #include <stdio.h>
23
24 #include "gnc-component-manager.h"
25 #include "qof.h"
26 #include "gnc-ui-util.h"
27
28
29 /** Declarations ****************************************************/
30
31 #define CM_DEBUG 0
32
33 typedef struct
34 {
35 QofIdType entity_type;
36 QofEventId event_mask;
37 } EntityTypeEventInfo;
38
39 typedef struct
40 {
41 GHashTable * event_masks;
42 GHashTable * entity_events;
43
44 gboolean match;
45 } ComponentEventInfo;
46
47 typedef struct
48 {
49 GNCComponentRefreshHandler refresh_handler;
50 GNCComponentCloseHandler close_handler;
51 gpointer user_data;
52
53 ComponentEventInfo watch_info;
54
55 char *component_class;
56 gint component_id;
57 gpointer session;
58 } ComponentInfo;
59
60
61 /** Static Variables ************************************************/
62 static guint suspend_counter = 0;
63 /* Some code foolishly uses 0 instead of NO_COMPONENT, so we start with 1. */
64 static gint next_component_id = 1;
65 static GList *components = NULL;
66
67 static ComponentEventInfo changes = { NULL, NULL, FALSE };
68 static ComponentEventInfo changes_backup = { NULL, NULL, FALSE };
69
70
71 /* This static indicates the debugging module that this .o belongs to. */
72 static QofLogModule log_module = GNC_MOD_GUI;
73
74
75 /** Prototypes ******************************************************/
76 static void gnc_gui_refresh_internal (gboolean force);
77 static GList * find_component_ids_by_class (const char *component_class);
78 static gboolean got_events = FALSE;
79
80
81 /** Implementations *************************************************/
82
83 #if CM_DEBUG
84 static void
dump_components(void)85 dump_components (void)
86 {
87 GList *node;
88
89 fprintf (stderr, "Components:\n");
90
91 for (node = components; node; node = node->next)
92 {
93 ComponentInfo *ci = node->data;
94
95 fprintf (stderr, " %s:\t%d\n",
96 ci->component_class ? ci->component_class : "(null)",
97 ci->component_id);
98 }
99
100 fprintf (stderr, "\n");
101 }
102 #endif
103
104 static void
clear_mask_hash_helper(gpointer key,gpointer value,gpointer user_data)105 clear_mask_hash_helper (gpointer key, gpointer value, gpointer user_data)
106 {
107 QofEventId * et = value;
108
109 *et = 0;
110 }
111
112 /* clear a hash table of the form string --> QofEventId,
113 * where the values are g_malloced and the keys are in the engine
114 * string cache. */
115 static void
clear_mask_hash(GHashTable * hash)116 clear_mask_hash (GHashTable *hash)
117 {
118 if (hash == NULL)
119 return;
120
121 g_hash_table_foreach (hash, clear_mask_hash_helper, NULL);
122 }
123
124 static gboolean
destroy_mask_hash_helper(gpointer key,gpointer value,gpointer user_data)125 destroy_mask_hash_helper (gpointer key, gpointer value, gpointer user_data)
126 {
127 qof_string_cache_remove (key);
128 g_free (value);
129
130 return TRUE;
131 }
132
133 static void
destroy_mask_hash(GHashTable * hash)134 destroy_mask_hash (GHashTable *hash)
135 {
136 g_hash_table_foreach_remove (hash, destroy_mask_hash_helper, NULL);
137 g_hash_table_destroy (hash);
138 }
139
140 static gboolean
destroy_event_hash_helper(gpointer key,gpointer value,gpointer user_data)141 destroy_event_hash_helper (gpointer key, gpointer value, gpointer user_data)
142 {
143 GncGUID *guid = key;
144 EventInfo *ei = value;
145
146 guid_free (guid);
147 g_free (ei);
148
149 return TRUE;
150 }
151
152 /* clear a hash table of the form GncGUID --> EventInfo, where
153 * both keys and values are g_malloced */
154 static void
clear_event_hash(GHashTable * hash)155 clear_event_hash (GHashTable *hash)
156 {
157 if (hash == NULL)
158 return;
159
160 g_hash_table_foreach_remove (hash, destroy_event_hash_helper, NULL);
161 }
162
163 static void
destroy_event_hash(GHashTable * hash)164 destroy_event_hash (GHashTable *hash)
165 {
166 clear_event_hash (hash);
167 g_hash_table_destroy (hash);
168 }
169
170 static void
clear_event_info(ComponentEventInfo * cei)171 clear_event_info (ComponentEventInfo *cei)
172 {
173 if (!cei)
174 return;
175
176 clear_mask_hash (cei->event_masks);
177 clear_event_hash (cei->entity_events);
178 }
179
180 static void
add_event(ComponentEventInfo * cei,const GncGUID * entity,QofEventId event_mask,gboolean or_in)181 add_event (ComponentEventInfo *cei, const GncGUID *entity,
182 QofEventId event_mask, gboolean or_in)
183 {
184 GHashTable *hash;
185
186 if (!cei || !cei->entity_events || !entity)
187 return;
188
189 hash = cei->entity_events;
190
191 if (event_mask == 0)
192 {
193 gpointer key;
194 gpointer value;
195
196 if (or_in)
197 return;
198
199 if (g_hash_table_lookup_extended (hash, entity, &key, &value))
200 {
201 g_hash_table_remove (hash, entity);
202 guid_free (key);
203 g_free (value);
204 }
205 }
206 else
207 {
208 EventInfo *ei;
209
210 ei = g_hash_table_lookup (hash, entity);
211 if (ei == NULL)
212 {
213 GncGUID *key;
214
215 key = guid_malloc ();
216 *key = *entity;
217
218 ei = g_new (EventInfo, 1);
219 ei->event_mask = 0;
220
221 g_hash_table_insert (hash, key, ei);
222 }
223
224 if (or_in)
225 ei->event_mask |= event_mask;
226 else
227 ei->event_mask = event_mask;
228 }
229 }
230
231 static void
add_event_type(ComponentEventInfo * cei,QofIdTypeConst entity_type,QofEventId event_mask,gboolean or_in)232 add_event_type (ComponentEventInfo *cei, QofIdTypeConst entity_type,
233 QofEventId event_mask, gboolean or_in)
234 {
235 QofEventId *mask;
236
237 g_return_if_fail (cei);
238 g_return_if_fail (cei->event_masks);
239 g_return_if_fail (entity_type);
240
241 mask = g_hash_table_lookup (cei->event_masks, entity_type);
242 if (!mask)
243 {
244 const char * key = qof_string_cache_insert ((gpointer) entity_type);
245 mask = g_new0 (QofEventId, 1);
246 g_hash_table_insert (cei->event_masks, (gpointer)key, mask);
247 }
248
249 if (or_in)
250 *mask |= event_mask;
251 else
252 *mask = event_mask;
253 }
254
255 static void
gnc_cm_event_handler(QofInstance * entity,QofEventId event_type,gpointer user_data,gpointer event_data)256 gnc_cm_event_handler (QofInstance *entity,
257 QofEventId event_type,
258 gpointer user_data,
259 gpointer event_data)
260 {
261 const GncGUID *guid = qof_entity_get_guid(entity);
262 #if CM_DEBUG
263 gchar guidstr[GUID_ENCODING_LENGTH+1];
264 guid_to_string_buff (guid, guidstr);
265 fprintf (stderr, "event_handler: event %d, entity %p, guid %s\n", event_type,
266 entity, guidstr);
267 #endif
268 add_event (&changes, guid, event_type, TRUE);
269
270 if (QOF_CHECK_TYPE(entity, GNC_ID_SPLIT))
271 {
272 /* split events are never generated by the engine, but might
273 * be generated by a backend (viz. the postgres backend.)
274 * Handle them like a transaction modify event. */
275 add_event_type (&changes, GNC_ID_TRANS, QOF_EVENT_MODIFY, TRUE);
276 }
277 else
278 add_event_type (&changes, entity->e_type, event_type, TRUE);
279
280 got_events = TRUE;
281
282 if (suspend_counter == 0)
283 gnc_gui_refresh_internal (FALSE);
284 }
285
286 static gint handler_id;
287
288 void
gnc_component_manager_init(void)289 gnc_component_manager_init (void)
290 {
291 if (changes.entity_events)
292 {
293 PERR ("component manager already initialized");
294 return;
295 }
296
297 changes.event_masks = g_hash_table_new (g_str_hash, g_str_equal);
298 changes.entity_events = guid_hash_table_new ();
299
300 changes_backup.event_masks = g_hash_table_new (g_str_hash, g_str_equal);
301 changes_backup.entity_events = guid_hash_table_new ();
302
303 handler_id = qof_event_register_handler (gnc_cm_event_handler, NULL);
304 }
305
306 void
gnc_component_manager_shutdown(void)307 gnc_component_manager_shutdown (void)
308 {
309 if (!changes.entity_events)
310 {
311 PERR ("component manager not initialized");
312 return;
313 }
314
315 destroy_mask_hash (changes.event_masks);
316 changes.event_masks = NULL;
317
318 destroy_event_hash (changes.entity_events);
319 changes.entity_events = NULL;
320
321 destroy_mask_hash (changes_backup.event_masks);
322 changes_backup.event_masks = NULL;
323
324 destroy_event_hash (changes_backup.entity_events);
325 changes_backup.entity_events = NULL;
326
327 qof_event_unregister_handler (handler_id);
328 }
329
330 static ComponentInfo *
find_component(gint component_id)331 find_component (gint component_id)
332 {
333 GList *node;
334
335 for (node = components; node; node = node->next)
336 {
337 ComponentInfo *ci = node->data;
338
339 if (ci->component_id == component_id)
340 return ci;
341 }
342
343 return NULL;
344 }
345
346 static GList *
find_components_by_data(gpointer user_data)347 find_components_by_data (gpointer user_data)
348 {
349 GList *list = NULL;
350 GList *node;
351
352 for (node = components; node; node = node->next)
353 {
354 ComponentInfo *ci = node->data;
355
356 if (ci->user_data == user_data)
357 list = g_list_prepend (list, ci);
358 }
359
360 return list;
361 }
362
363 static GList *
find_components_by_session(gpointer session)364 find_components_by_session (gpointer session)
365 {
366 GList *list = NULL;
367 GList *node;
368
369 for (node = components; node; node = node->next)
370 {
371 ComponentInfo *ci = node->data;
372
373 if (ci->session == session)
374 list = g_list_prepend (list, ci);
375 }
376
377 return list;
378 }
379
380 static ComponentInfo *
gnc_register_gui_component_internal(const char * component_class)381 gnc_register_gui_component_internal (const char * component_class)
382 {
383 ComponentInfo *ci;
384 gint component_id;
385
386 g_return_val_if_fail (component_class, NULL);
387
388 /* look for a free handler id */
389 component_id = next_component_id;
390
391 /* design warning: if we ever get 2^32-1 components,
392 this loop is infinite. Instead of fixing it, we'll just
393 complain when (if) we get half way there (probably never).
394 */
395 while (find_component (component_id))
396 if (++component_id == NO_COMPONENT)
397 component_id++;
398
399 if (component_id < 0)
400 PERR("Amazing! Half way to running out of component_ids.");
401
402 /* found one, add the handler */
403 ci = g_new0 (ComponentInfo, 1);
404
405 ci->watch_info.event_masks = g_hash_table_new (g_str_hash, g_str_equal);
406 ci->watch_info.entity_events = guid_hash_table_new ();
407
408 ci->component_class = g_strdup (component_class);
409 ci->component_id = component_id;
410 ci->session = NULL;
411
412 components = g_list_prepend (components, ci);
413
414 /* update id for next registration */
415 next_component_id = component_id + 1;
416
417 #if CM_DEBUG
418 fprintf (stderr, "Register component %d in class %s\n",
419 component_id, component_class ? component_class : "(null)");
420 dump_components ();
421 #endif
422
423 return ci;
424 }
425
426 gint
gnc_register_gui_component(const char * component_class,GNCComponentRefreshHandler refresh_handler,GNCComponentCloseHandler close_handler,gpointer user_data)427 gnc_register_gui_component (const char *component_class,
428 GNCComponentRefreshHandler refresh_handler,
429 GNCComponentCloseHandler close_handler,
430 gpointer user_data)
431 {
432 ComponentInfo *ci;
433
434 /* sanity check */
435 if (!component_class)
436 {
437 PERR ("no class specified");
438 return NO_COMPONENT;
439 }
440
441 ci = gnc_register_gui_component_internal (component_class);
442 g_return_val_if_fail (ci, NO_COMPONENT);
443
444 ci->refresh_handler = refresh_handler;
445 ci->close_handler = close_handler;
446 ci->user_data = user_data;
447
448 return ci->component_id;
449 }
450
451 void
gnc_gui_component_watch_entity(gint component_id,const GncGUID * entity,QofEventId event_mask)452 gnc_gui_component_watch_entity (gint component_id,
453 const GncGUID *entity,
454 QofEventId event_mask)
455 {
456 ComponentInfo *ci;
457
458 if (entity == NULL)
459 return;
460
461 ci = find_component (component_id);
462 if (!ci)
463 {
464 PERR ("component not found");
465 return;
466 }
467
468 add_event (&ci->watch_info, entity, event_mask, FALSE);
469 }
470
471 void
gnc_gui_component_watch_entity_type(gint component_id,QofIdTypeConst entity_type,QofEventId event_mask)472 gnc_gui_component_watch_entity_type (gint component_id,
473 QofIdTypeConst entity_type,
474 QofEventId event_mask)
475 {
476 ComponentInfo *ci;
477
478 ci = find_component (component_id);
479 if (!ci)
480 {
481 PERR ("component not found");
482 return;
483 }
484
485 add_event_type (&ci->watch_info, entity_type, event_mask, FALSE);
486 }
487
488 const EventInfo *
gnc_gui_get_entity_events(GHashTable * changes,const GncGUID * entity)489 gnc_gui_get_entity_events (GHashTable *changes, const GncGUID *entity)
490 {
491 if (!changes || !entity)
492 return QOF_EVENT_NONE;
493
494 return g_hash_table_lookup (changes, entity);
495 }
496
497 void
gnc_gui_component_clear_watches(gint component_id)498 gnc_gui_component_clear_watches (gint component_id)
499 {
500 ComponentInfo *ci;
501
502 ci = find_component (component_id);
503 if (!ci)
504 {
505 PERR ("component not found");
506 return;
507 }
508
509 clear_event_info (&ci->watch_info);
510 }
511
512 void
gnc_unregister_gui_component(gint component_id)513 gnc_unregister_gui_component (gint component_id)
514 {
515 ComponentInfo *ci;
516
517 ci = find_component (component_id);
518 if (!ci)
519 {
520 PERR ("component %d not found", component_id);
521 return;
522 }
523
524 #if CM_DEBUG
525 fprintf (stderr, "Unregister component %d in class %s\n",
526 ci->component_id,
527 ci->component_class ? ci->component_class : "(null)");
528 #endif
529
530 gnc_gui_component_clear_watches (component_id);
531
532 components = g_list_remove (components, ci);
533
534 destroy_mask_hash (ci->watch_info.event_masks);
535 ci->watch_info.event_masks = NULL;
536
537 destroy_event_hash (ci->watch_info.entity_events);
538 ci->watch_info.entity_events = NULL;
539
540 g_free (ci->component_class);
541 ci->component_class = NULL;
542
543 g_free (ci);
544
545 #if CM_DEBUG
546 dump_components ();
547 #endif
548 }
549
550 void
gnc_unregister_gui_component_by_data(const char * component_class,gpointer user_data)551 gnc_unregister_gui_component_by_data (const char *component_class,
552 gpointer user_data)
553 {
554 GList *list;
555 GList *node;
556
557 list = find_components_by_data (user_data);
558
559 for (node = list; node; node = node->next)
560 {
561 ComponentInfo *ci = node->data;
562
563 if (component_class &&
564 g_strcmp0 (component_class, ci->component_class) != 0)
565 continue;
566
567 gnc_unregister_gui_component (ci->component_id);
568 }
569
570 g_list_free (list);
571 }
572
573 void
gnc_suspend_gui_refresh(void)574 gnc_suspend_gui_refresh (void)
575 {
576 suspend_counter++;
577
578 if (suspend_counter == 0)
579 {
580 PERR ("suspend counter overflow");
581 }
582 }
583
584 void
gnc_resume_gui_refresh(void)585 gnc_resume_gui_refresh (void)
586 {
587 if (suspend_counter == 0)
588 {
589 PERR ("suspend counter underflow");
590 return;
591 }
592
593 suspend_counter--;
594
595 if (suspend_counter == 0)
596 gnc_gui_refresh_internal (FALSE);
597 }
598
599 static void
match_type_helper(gpointer key,gpointer value,gpointer user_data)600 match_type_helper (gpointer key, gpointer value, gpointer user_data)
601 {
602 ComponentEventInfo *cei = user_data;
603 QofIdType id_type = key;
604 QofEventId * et = value;
605 QofEventId * et_2;
606
607 et_2 = g_hash_table_lookup (cei->event_masks, id_type);
608 if (!et_2)
609 return;
610
611 if (*et & *et_2)
612 cei->match = TRUE;
613 }
614
615 static void
match_helper(gpointer key,gpointer value,gpointer user_data)616 match_helper (gpointer key, gpointer value, gpointer user_data)
617 {
618 GncGUID *guid = key;
619 EventInfo *ei_1 = value;
620 EventInfo *ei_2;
621 ComponentEventInfo *cei = user_data;
622
623 ei_2 = g_hash_table_lookup (cei->entity_events, guid);
624 if (!ei_2)
625 return;
626
627 if (ei_1->event_mask & ei_2->event_mask)
628 cei->match = TRUE;
629 }
630
631 static gboolean
changes_match(ComponentEventInfo * cei,ComponentEventInfo * changes)632 changes_match (ComponentEventInfo *cei, ComponentEventInfo *changes)
633 {
634 ComponentEventInfo *big_cei;
635 GHashTable *smalltable;
636
637 if (cei == NULL)
638 return FALSE;
639
640 /* check types first, for efficiency */
641 cei->match = FALSE;
642 g_hash_table_foreach (changes->event_masks, match_type_helper, cei);
643 if (cei->match)
644 return TRUE;
645
646 if (g_hash_table_size (cei->entity_events) <=
647 g_hash_table_size (changes->entity_events))
648 {
649 smalltable = cei->entity_events;
650 big_cei = changes;
651 }
652 else
653 {
654 smalltable = changes->entity_events;
655 big_cei = cei;
656 }
657
658 big_cei->match = FALSE;
659
660 g_hash_table_foreach (smalltable, match_helper, big_cei);
661
662 return big_cei->match;
663 }
664
665 static void
gnc_gui_refresh_internal(gboolean force)666 gnc_gui_refresh_internal (gboolean force)
667 {
668 GList *list;
669 GList *node;
670
671 if (!got_events && !force)
672 return;
673
674 gnc_suspend_gui_refresh ();
675
676 {
677 GHashTable *table;
678
679 table = changes_backup.event_masks;
680 changes_backup.event_masks = changes.event_masks;
681 changes.event_masks = table;
682
683 table = changes_backup.entity_events;
684 changes_backup.entity_events = changes.entity_events;
685 changes.entity_events = table;
686 }
687
688 #if CM_DEBUG
689 fprintf (stderr, "%srefresh!\n", force ? "forced " : "");
690 #endif
691
692 list = find_component_ids_by_class (NULL);
693 // reverse the list so class GncPluginPageRegister is before register-single
694 list = g_list_reverse (list);
695
696 for (node = list; node; node = node->next)
697 {
698 ComponentInfo *ci = find_component (GPOINTER_TO_INT (node->data));
699
700 if (!ci)
701 continue;
702
703 if (!ci->refresh_handler)
704 {
705 #if CM_DEBUG
706 fprintf (stderr, "no handlers for %s:%d\n", ci->component_class, ci->component_id);
707 #endif
708 continue;
709 }
710
711 if (force)
712 {
713 if (ci->refresh_handler)
714 {
715 #if CM_DEBUG
716 fprintf (stderr, "calling %s:%d C handler\n", ci->component_class, ci->component_id);
717 #endif
718 ci->refresh_handler (NULL, ci->user_data);
719 }
720 }
721 else if (changes_match (&ci->watch_info, &changes_backup))
722 {
723 if (ci->refresh_handler)
724 {
725 #if CM_DEBUG
726 fprintf (stderr, "calling %s:%d C handler\n", ci->component_class, ci->component_id);
727 #endif
728 ci->refresh_handler (changes_backup.entity_events, ci->user_data);
729 }
730 }
731 else
732 {
733 #if CM_DEBUG
734 fprintf (stderr, "no match for %s:%d\n", ci->component_class, ci->component_id);
735 #endif
736 }
737 }
738
739 clear_event_info (&changes_backup);
740 got_events = FALSE;
741
742 g_list_free (list);
743
744 gnc_resume_gui_refresh ();
745 }
746
747 void
gnc_gui_refresh_all(void)748 gnc_gui_refresh_all (void)
749 {
750 if (suspend_counter != 0)
751 {
752 PERR ("suspend counter not zero");
753 return;
754 }
755
756 gnc_gui_refresh_internal (TRUE);
757 }
758
759 gboolean
gnc_gui_refresh_suspended(void)760 gnc_gui_refresh_suspended (void)
761 {
762 return suspend_counter != 0;
763 }
764
765 void
gnc_close_gui_component(gint component_id)766 gnc_close_gui_component (gint component_id)
767 {
768 ComponentInfo *ci;
769
770 ci = find_component (component_id);
771 if (!ci)
772 {
773 PERR ("component not found");
774 return;
775 }
776
777 if (!ci->close_handler)
778 return;
779
780 if (ci->close_handler)
781 ci->close_handler (ci->user_data);
782 }
783
784 void
gnc_close_gui_component_by_data(const char * component_class,gpointer user_data)785 gnc_close_gui_component_by_data (const char *component_class,
786 gpointer user_data)
787 {
788 GList *list;
789 GList *node;
790
791 list = find_components_by_data (user_data);
792
793 for (node = list; node; node = node->next)
794 {
795 ComponentInfo *ci = node->data;
796
797 if (component_class &&
798 g_strcmp0 (component_class, ci->component_class) != 0)
799 continue;
800
801 gnc_close_gui_component (ci->component_id);
802 }
803
804 g_list_free (list);
805 }
806
807 void
gnc_gui_component_set_session(gint component_id,gpointer session)808 gnc_gui_component_set_session (gint component_id, gpointer session)
809 {
810 ComponentInfo *ci;
811
812 ci = find_component (component_id);
813 if (!ci)
814 {
815 PERR ("component not found");
816 return;
817 }
818
819 ci->session = session;
820 }
821
822 void
gnc_close_gui_component_by_session(gpointer session)823 gnc_close_gui_component_by_session (gpointer session)
824 {
825 GList *list;
826 GList *node;
827
828 list = find_components_by_session (session);
829
830 // reverse the list so class like dialog-options close before window-report
831 list = g_list_reverse (list);
832
833 for (node = list; node; node = node->next)
834 {
835 ComponentInfo *ci = node->data;
836
837 gnc_close_gui_component (ci->component_id);
838 }
839
840 g_list_free (list);
841 }
842
843 GList *
gnc_find_gui_components(const char * component_class,GNCComponentFindHandler find_handler,gpointer find_data)844 gnc_find_gui_components (const char *component_class,
845 GNCComponentFindHandler find_handler,
846 gpointer find_data)
847 {
848 GList *list = NULL;
849 GList *node;
850
851 if (!component_class)
852 return NULL;
853
854 for (node = components; node; node = node->next)
855 {
856 ComponentInfo *ci = node->data;
857
858 if (g_strcmp0 (component_class, ci->component_class) != 0)
859 continue;
860
861 if (find_handler && !find_handler (find_data, ci->user_data))
862 continue;
863
864 list = g_list_prepend (list, ci->user_data);
865 }
866
867 return list;
868 }
869
870 gpointer
gnc_find_first_gui_component(const char * component_class,GNCComponentFindHandler find_handler,gpointer find_data)871 gnc_find_first_gui_component (const char *component_class,
872 GNCComponentFindHandler find_handler,
873 gpointer find_data)
874 {
875 GList *list;
876 gpointer user_data;
877
878 #if CM_DEBUG
879 fprintf (stderr, "find: class %s, fn %p, data %p\n", component_class,
880 find_handler, find_data);
881 #endif
882 if (!component_class)
883 return NULL;
884
885 list = gnc_find_gui_components (component_class, find_handler, find_data);
886 if (!list)
887 return NULL;
888
889 user_data = list->data;
890
891 g_list_free (list);
892
893 #if CM_DEBUG
894 fprintf (stderr, "found: data %p\n", user_data);
895 #endif
896 return user_data;
897 }
898
899 static GList *
find_component_ids_by_class(const char * component_class)900 find_component_ids_by_class (const char *component_class)
901 {
902 GList *list = NULL;
903 GList *node;
904
905 for (node = components; node; node = node->next)
906 {
907 ComponentInfo *ci = node->data;
908
909 if (component_class &&
910 g_strcmp0 (component_class, ci->component_class) != 0)
911 continue;
912
913 list = g_list_prepend (list, GINT_TO_POINTER (ci->component_id));
914 }
915
916 return list;
917 }
918
919 gint
gnc_forall_gui_components(const char * component_class,GNCComponentHandler handler,gpointer iter_data)920 gnc_forall_gui_components (const char *component_class,
921 GNCComponentHandler handler,
922 gpointer iter_data)
923 {
924 GList *list;
925 GList *node;
926 gint count = 0;
927
928 if (!handler)
929 return(0);
930
931 /* so components can be destroyed during the forall */
932 list = find_component_ids_by_class (component_class);
933
934 for (node = list; node; node = node->next)
935 {
936 ComponentInfo *ci = find_component (GPOINTER_TO_INT (node->data));
937
938 if (!ci)
939 continue;
940
941 if (handler (ci->component_class, ci->component_id, ci->user_data, iter_data))
942 count++;
943 }
944
945 g_list_free (list);
946 return(count);
947 }
948