GnuCash  4.8a-176-g88ecf8dd1
qofevent.cpp
1 /********************************************************************
2  * qofevent.c -- QOF event handling implementation *
3  * Copyright 2000 Dave Peticolas <dave@krondo.com> *
4  * Copyright 2006 Neil Williams <linux@codehelp.co.uk> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  * *
23  ********************************************************************/
24 
25 #include <config.h>
26 #include <glib.h>
27 
28 #include "qof.h"
29 #include "qofevent-p.h"
30 
31 /* Static Variables ************************************************/
32 static guint suspend_counter = 0;
33 static gint next_handler_id = 1;
34 static guint handler_run_level = 0;
35 static guint pending_deletes = 0;
36 static GList *handlers = NULL;
37 
38 /* This static indicates the debugging module that this .o belongs to. */
39 static QofLogModule log_module = QOF_MOD_ENGINE;
40 
41 /* Implementations *************************************************/
42 
43 static gint
44 find_next_handler_id(void)
45 {
46  HandlerInfo *hi;
47  gint handler_id;
48  GList *node;
49 
50  /* look for a free handler id */
51  handler_id = next_handler_id;
52  node = handlers;
53 
54  while (node)
55  {
56  hi = static_cast<HandlerInfo*>(node->data);
57 
58  if (hi->handler_id == handler_id)
59  {
60  handler_id++;
61  node = handlers;
62  continue;
63  }
64 
65  node = node->next;
66  }
67  /* Update id for next registration */
68  next_handler_id = handler_id + 1;
69  return handler_id;
70 }
71 
72 gint
73 qof_event_register_handler (QofEventHandler handler, gpointer user_data)
74 {
75  HandlerInfo *hi;
76  gint handler_id;
77 
78  ENTER ("(handler=%p, data=%p)", handler, user_data);
79 
80  /* sanity check */
81  if (!handler)
82  {
83  PERR ("no handler specified");
84  return 0;
85  }
86 
87  /* look for a free handler id */
88  handler_id = find_next_handler_id();
89 
90  /* Found one, add the handler */
91  hi = g_new0 (HandlerInfo, 1);
92 
93  hi->handler = handler;
94  hi->user_data = user_data;
95  hi->handler_id = handler_id;
96 
97  handlers = g_list_prepend (handlers, hi);
98  LEAVE ("(handler=%p, data=%p) handler_id=%d", handler, user_data, handler_id);
99  return handler_id;
100 }
101 
102 void
104 {
105  GList *node;
106 
107  ENTER ("(handler_id=%d)", handler_id);
108  for (node = handlers; node; node = node->next)
109  {
110  HandlerInfo *hi = static_cast<HandlerInfo*>(node->data);
111 
112  if (hi->handler_id != handler_id)
113  continue;
114 
115  /* Normally, we could actually remove the handler's node from the
116  list, but we may be unregistering the event handler as a result
117  of a generated event, such as QOF_EVENT_DESTROY. In that case,
118  we're in the middle of walking the GList and it is wrong to
119  modify the list. So, instead, we just NULL the handler. */
120  if (hi->handler)
121  LEAVE ("(handler_id=%d) handler=%p data=%p", handler_id,
122  hi->handler, hi->user_data);
123 
124  /* safety -- clear the handler in case we're running events now */
125  hi->handler = NULL;
126 
127  if (handler_run_level == 0)
128  {
129  handlers = g_list_remove_link (handlers, node);
130  g_list_free_1 (node);
131  g_free (hi);
132  }
133  else
134  {
135  pending_deletes++;
136  }
137 
138  return;
139  }
140 
141  PERR ("no such handler: %d", handler_id);
142 }
143 
144 void
146 {
147  suspend_counter++;
148 
149  if (suspend_counter == 0)
150  {
151  PERR ("suspend counter overflow");
152  }
153 }
154 
155 void
157 {
158  if (suspend_counter == 0)
159  {
160  PERR ("suspend counter underflow");
161  return;
162  }
163 
164  suspend_counter--;
165 }
166 
167 static void
168 qof_event_generate_internal (QofInstance *entity, QofEventId event_id,
169  gpointer event_data)
170 {
171  GList *node;
172  GList *next_node = NULL;
173 
174  g_return_if_fail(entity);
175 
176  switch (event_id)
177  {
178  case QOF_EVENT_NONE:
179  {
180  /* if none, don't log, just return. */
181  return;
182  }
183  }
184 
185  handler_run_level++;
186  for (node = handlers; node; node = next_node)
187  {
188  HandlerInfo *hi = static_cast<HandlerInfo*>(node->data);
189 
190  next_node = node->next;
191  if (hi->handler)
192  {
193  PINFO("id=%d hi=%p han=%p data=%p", hi->handler_id, hi,
194  hi->handler, event_data);
195  hi->handler (entity, event_id, hi->user_data, event_data);
196  }
197  }
198  handler_run_level--;
199 
200  /* If we're the outermost event runner and we have pending deletes
201  * then go delete the handlers now.
202  */
203  if (handler_run_level == 0 && pending_deletes)
204  {
205  for (node = handlers; node; node = next_node)
206  {
207  HandlerInfo *hi = static_cast<HandlerInfo*>(node->data);
208  next_node = node->next;
209  if (hi->handler == NULL)
210  {
211  /* remove this node from the list, then free this node */
212  handlers = g_list_remove_link (handlers, node);
213  g_list_free_1 (node);
214  g_free (hi);
215  }
216  }
217  pending_deletes = 0;
218  }
219 }
220 
221 void
222 qof_event_force (QofInstance *entity, QofEventId event_id, gpointer event_data)
223 {
224  if (!entity)
225  return;
226 
227  qof_event_generate_internal (entity, event_id, event_data);
228 }
229 
230 void
231 qof_event_gen (QofInstance *entity, QofEventId event_id, gpointer event_data)
232 {
233  if (!entity)
234  return;
235 
236  if (suspend_counter)
237  return;
238 
239  qof_event_generate_internal (entity, event_id, event_data);
240 }
241 
242 /* =========================== END OF FILE ======================= */
void(* QofEventHandler)(QofInstance *ent, QofEventId event_type, gpointer handler_data, gpointer event_data)
Handler invoked when an event is generated.
Definition: qofevent.h:89
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
gint qof_event_register_handler(QofEventHandler handler, gpointer user_data)
Register a handler for events.
Definition: qofevent.cpp:73
gint QofEventId
Define the type of events allowed.
Definition: qofevent.h:45
void qof_event_unregister_handler(gint handler_id)
Unregister an event handler.
Definition: qofevent.cpp:103
#define QOF_EVENT_NONE
Default events for backwards compatibility.
Definition: qofevent.h:72
void qof_event_suspend(void)
Suspend all engine events.
Definition: qofevent.cpp:145
void qof_event_resume(void)
Resume engine event generation.
Definition: qofevent.cpp:156
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition: qofevent.cpp:231