~siretart/gnucash/ubuntu-fullsource

« back to all changes in this revision

Viewing changes to src/doc/design/component-manager.texinfo

  • Committer: Reinhard Tartler
  • Date: 2008-08-03 07:25:46 UTC
  • Revision ID: siretart@tauware.de-20080803072546-y6p8xda8zpfi62ys
import gnucash_2.2.4.orig.tar.gz

The original tarball had the md5sum: 27e660297dc5b8ce574515779d05a5a5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
@node Component Manager
 
2
@chapter Component Manager
 
3
@cindex Component Manager
 
4
 
 
5
@strong{This whole document is completely outdated. Don't read this. All
 
6
function names and every described structure has changed
 
7
completely. Only read this if you want to know how gnucash looked like
 
8
in 1999. This is completely outdated!}
 
9
 
 
10
The Component Manager (hereafter referred to as the CM) is a framework
 
11
for managing GUI objects in GnuCash. The CM provides several services.
 
12
 
 
13
First, components may request automatic invocation of a 'refresh'
 
14
handler that is called when a component may need to be redrawn. This
 
15
mechanism is tied into the Engine's Event mechanism (@pxref{Events}),
 
16
so that GUI components are notified when relevant Engine entities are
 
17
created, modified, or destroyed.
 
18
 
 
19
Components may also provide a 'close' handler so that the component
 
20
may be closed through the CM API.
 
21
 
 
22
The CM also provides the ability to search for existing components.
 
23
 
 
24
 
 
25
@menu
 
26
* Component Manager Introduction::  
 
27
* Refresh Mechanism::           
 
28
* CM Initialization and Shutdown::  
 
29
* Refresh Handlers::            
 
30
* Close Handlers::              
 
31
* Registering and Unregistering Components::  
 
32
* Watching Engine Objects::     
 
33
* Controlling Refreshes::       
 
34
* Finding Components::          
 
35
* Iterating over Components::   
 
36
@end menu
 
37
 
 
38
 
 
39
@node Component Manager Introduction, Refresh Mechanism, Component Manager, Component Manager
 
40
@section Introduction
 
41
 
 
42
The GnuCash GUI must manage many different components (registers,
 
43
reconcile windows, reports, graphs, main window, etc.).  Functions which
 
44
need to be performed on or with GUI components include:
 
45
 
 
46
 
 
47
@itemize
 
48
 
 
49
@item Refreshing components. When Engine objects (Accounts,
 
50
Transactions, Splits, etc.) are created, modified, or destroyed,
 
51
the GUI components which reference those objects must be refreshed.
 
52
 
 
53
@item Searching for existing components. For example, when the
 
54
user chooses to 'Reconcile' an account with an existing reconcile
 
55
dialog, that dialog should be raised to the top in lieu of creating
 
56
a new reconcile dialog.
 
57
 
 
58
@item Closing components.
 
59
 
 
60
@end itemize
 
61
 
 
62
 
 
63
In particular, keeping components updated in the face of changes in the
 
64
Engine is a difficult problem. Requirements for handling refreshes
 
65
include:
 
66
 
 
67
@itemize
 
68
 
 
69
@item The Engine should be able to inform user code when any account
 
70
or transaction is changed (including changing their respective splits).
 
71
This requirement is satisfied by the Engine Events.
 
72
 
 
73
@item The refresh mechanism should not have specific knowledge of
 
74
individual windows and other GUI elements. It should be easy to
 
75
add new refreshable elements to the mechanism.
 
76
 
 
77
@item Changes should be batchable with respect to refreshes, so that
 
78
a sequence of changes only cause a refresh after the last change
 
79
in a batch. Batching should be nestable, for coding simplicity.
 
80
 
 
81
@item For very large batches of changes (loading files) we need to be
 
82
able to turn off the mechanism entirely, perform the changes, and then
 
83
perform a global, forced refresh. This should also be nestable.
 
84
 
 
85
@item The Engine should not be managing GUI components.
 
86
 
 
87
@item The refresh mechanism should be extendable to a multi-user
 
88
situation in which changes can come from external components.
 
89
 
 
90
@item Components should be able to specify which Engine entities
 
91
can cause refreshes. This requirement avoids allows the implementation
 
92
to avoid unnecessary refreshing.
 
93
 
 
94
@end itemize
 
95
 
 
96
 
 
97
@node Refresh Mechanism, CM Initialization and Shutdown, Component Manager Introduction, Component Manager
 
98
@section Refresh Mechanism
 
99
@cindex Refresh Mechanism
 
100
 
 
101
The major design decisions of the CM relate to the refresh
 
102
mechanism. The refresh mechanism consists of two parts, the Engine
 
103
component and the GUI component. The Engine component is the
 
104
Event mechanism (@pxref{Events}), while the GUI component is the
 
105
Component Manager, which provide refresh functionality as well
 
106
as other services.
 
107
 
 
108
The diagram below illustrated the design of the GnuCash refresh
 
109
mechanism.
 
110
 
 
111
@example
 
112
                            ----------
 
113
                            |        |
 
114
                            | Engine |
 
115
                            |        |
 
116
                            ----------
 
117
                                /|\
 
118
                                 |
 
119
                                 |--- Events (from Engine)
 
120
                                 |
 
121
                                \|/
 
122
                    -------------------------
 
123
                    |                       |
 
124
                    |   Component Manager   |
 
125
                    |                       |
 
126
                    -------------------------
 
127
                   /            /|\          \     GUI Commands
 
128
                  /              |            \--- including refresh
 
129
                 /              \|/            \   invocations (from CM)
 
130
-----------------         -----------------
 
131
|               |         |               |
 
132
| GUI Component |         | GUI Component |           ...
 
133
|               |         |               |
 
134
-----------------         -----------------
 
135
@end example
 
136
 
 
137
The top-level component is the Engine, which emits Events to the
 
138
Component Manager. In fact, the Engine will send events to any
 
139
registered handler, but in GnuCash, only the CM registers with
 
140
the engine. All other GUI components register with the CM.
 
141
 
 
142
The CM invokes the refresh handlers of GUI components based on the
 
143
Engine events received the CM has received as well as information
 
144
provided by the GUI components (such as which specific Engine
 
145
entities the components are 'watching').
 
146
 
 
147
 
 
148
@node CM Initialization and Shutdown, Refresh Handlers, Refresh Mechanism, Component Manager
 
149
@section Initialization and Shutdown
 
150
 
 
151
@deftypefun void gnc_component_manager_init (void)
 
152
Initialize the Component Manager. This should be called
 
153
before using an other CM functions.
 
154
@end deftypefun
 
155
 
 
156
@deftypefun void gnc_component_manager_shutdown (void)
 
157
Shutdown the Component Manager. This should be called
 
158
to release Component Manager resources.
 
159
@end deftypefun
 
160
 
 
161
 
 
162
@node Refresh Handlers, Close Handlers, CM Initialization and Shutdown, Component Manager
 
163
@section Refresh Handlers
 
164
@tindex EventInfo
 
165
 
 
166
When a component registers itself with the CM, it may specify two
 
167
different handlers: a refresh handler and a close handler. A refresh
 
168
handler is a function with the following type signature:
 
169
 
 
170
@deftp {Data type} GNCComponentRefreshHandler void (*) (GHashTable *@var{changes}, gpointer @var{user_data})
 
171
This is the type signature of a refresh handler. The @var{changes} hash
 
172
describes the Engine events which have occurred since the last refresh.
 
173
It is used to determine whether a refresh is actually needed. It may,
 
174
however, be @code{NULL}, meaning the component must perform a refresh.
 
175
The @code{user_data} pointer is the data pointer supplied when the
 
176
component was registered.
 
177
@end deftp
 
178
 
 
179
When a refresh handler is invoked, it should perform the following actions:
 
180
 
 
181
@enumerate
 
182
 
 
183
@item Check if the component should be closed. When a refresh handler
 
184
is invoked, any and all of the Engine objects which the component was
 
185
referencing may have been destroyed, possibly making the component
 
186
obsolete. For example, a dialog to edit the parameters of a specific
 
187
Account should be automatically closed when the account is deleted. On
 
188
the other hand, a list of all Accounts in a hierarchy should be updated
 
189
when an Account is deleted, but not necessarily closed.
 
190
 
 
191
Components must test for the destruction of critical Engine objects
 
192
in two ways.
 
193
 
 
194
@enumerate
 
195
 
 
196
@item Use the @code{GUID} lookup functions (such as
 
197
@code{xaccAccountLookup}), to determine if the engine object is still
 
198
bound to its @code{GUID}. Of course, this means that components should
 
199
store the @code{GUID}s of critical Engine objects instead of simply
 
200
storing their C pointers.
 
201
 
 
202
@item If the first test succeeds and the @var{changes} hash table
 
203
of the refresh handler is non-NULL, the component should use the hash to
 
204
determine of the GNC_EVENT_DESTROY event has ocurred for the Engine
 
205
object in question. The @var{changes} hash is a mapping from
 
206
@code{GUID}s to @code{EventInfo} structures. An @code{EventInfo}
 
207
structure has a single member, @code{event_mask}, of type
 
208
@code{GNCEngineEventType}. The @code{event_mask} is a logical or of the
 
209
@code{GNC_EVENT_CREATE}, @code{GNC_EVENT_MODIFY}, and
 
210
@code{GNC_EVENT_DESTROY} event types. Since refreshes may not occur with
 
211
every Engine event, @code{event_mask} may have all three values.
 
212
 
 
213
There is a utility function for accessing the @var{changes} hash:
 
214
 
 
215
@deftypefun {const EventInfo *} gnc_gui_get_entity_events (GHashTable * @var{changes}, const GUID * @var{entity})
 
216
Return the event info for the entity specified by @var{entity}. If there
 
217
are no events for that entity, @code{NULL} is returned.
 
218
@end deftypefun
 
219
 
 
220
@end enumerate
 
221
 
 
222
If the @var{changes} hash is NULL, then the first test is sufficient
 
223
to determine whether an object has been destroyed.
 
224
 
 
225
If the refresh handler determines the component should be destroyed,
 
226
it should destroy the component and return.
 
227
 
 
228
@item Check if the component should be refreshed. If the @var{changes}
 
229
hash is @code{NULL}, then the component must refresh itself. Otherwise,
 
230
it may use the @var{changes} hash to determine whether or not a refresh
 
231
is actually necessary. However, since the component may specify which
 
232
particular Engine objects are relevant (see "Watching Components"
 
233
below), generally a component will simply refresh unconditionally.
 
234
 
 
235
@item Refresh the component if necessary. This includes updating the
 
236
GUI as well as internal structures to reflect the new state of Engine
 
237
objects.
 
238
 
 
239
@end enumerate
 
240
 
 
241
 
 
242
@node Close Handlers, Registering and Unregistering Components, Refresh Handlers, Component Manager
 
243
@section Close Handlers
 
244
 
 
245
A close handler is a function with the following type signature:
 
246
 
 
247
@deftp {Data type} GNCComponentCloseHandler void (*) (gpointer @var{user_data})
 
248
This is the type signature of a close handler. The @code{user_data}
 
249
pointer is the data pointer supplied when the component was registered.
 
250
@end deftp
 
251
 
 
252
The invocation of a close handler is a command to the component to close
 
253
itself. The component must close itself -- the handler should not be
 
254
ignored. The component is still responsible for unregistering itself
 
255
with the Component Manager.
 
256
 
 
257
 
 
258
@node Registering and Unregistering Components, Watching Engine Objects, Close Handlers, Component Manager
 
259
@section Registering and Unregistering Components
 
260
 
 
261
@deftypefun gint gnc_register_gui_component (const char * @var{component_class}, GNCComponentRefreshHandler @var{refresh_handler}, GNCComponentCloseHandler @var{close_handler}, gpointer @var{user_data})
 
262
Register a gui component with the Component Manager.
 
263
 
 
264
The variable @var{component_class} is a string specifying a class of
 
265
components. Certain CM functions can be performed on all components in a
 
266
class. For that reason, components in the same class should all use the
 
267
same type for @var{user_data}.
 
268
 
 
269
@var{refresh_handler} and @var{close_handler} specify the refresh and close
 
270
handlers, respectively. Either or both may be @code{NULL}.
 
271
 
 
272
The @var{user_data} is supplied as an argument when the handlers are invoked.
 
273
 
 
274
The function returns the id of the newly-registered component, or
 
275
@code{NO_COMPONENT} if there was an error.
 
276
@end deftypefun
 
277
 
 
278
After a refresh handler is registered, the component must use the API
 
279
calls under "Watching Engine Objects" below to inform the Component
 
280
Manager which engine entities are being watched, i.e., which engine
 
281
entities may cause the component to need refreshing. When a component is
 
282
first registered, it is not watching anything, and thus will not receive
 
283
refresh events.
 
284
 
 
285
@deftypefun void gnc_unregister_gui_component (gint @var{component_id})
 
286
Unregister the component with id @var{component} from the CM.
 
287
@end deftypefun
 
288
 
 
289
@deftypefun void gnc_unregister_gui_component_by_data (const char * @var{component_class}, gpointer @var{user_data})
 
290
Unregister all gui components in the class @var{component_class} which have
 
291
@var{user_data} as a user data pointer.
 
292
@end deftypefun
 
293
 
 
294
 
 
295
@node Watching Engine Objects, Controlling Refreshes, Registering and Unregistering Components, Component Manager
 
296
@section Watching Engine Objects
 
297
 
 
298
@deftypefun void gnc_gui_component_watch_entity (gint @var{component_id}, const GUID * @var{entity}, GNCEngineEventType @var{event_mask})
 
299
If @var{event_mask} is non-zero, add the Engine entity specified by
 
300
@var{entity} to the list of entities being watched by the component with
 
301
id @var{component_id}. Only the events specified by @var{events} are
 
302
watched. If @var{event_mask} is 0, the call turns off watching for the
 
303
entity.
 
304
@end deftypefun
 
305
 
 
306
@deftypefun void gnc_gui_component_watch_entity_type (gint @var{component_id}, GNCIdType @var{entity_type}, GNCEngineEventType @var{event_mask})
 
307
if @var{event_mask}, turn on watching for all entities of @var{entity_type}.
 
308
Only events specified by @var{event_mask} are watched. If @var{event_mask}
 
309
is 0, turns off watching for the entity type.
 
310
@end deftypefun
 
311
 
 
312
@deftypefun void gnc_gui_component_clear_watches (gint @var{component_id})
 
313
Clear all watches for the component with id @var{component_id}.
 
314
@end deftypefun
 
315
 
 
316
 
 
317
@node Controlling Refreshes, Finding Components, Watching Engine Objects, Component Manager
 
318
@section Controlling Refreshes
 
319
 
 
320
@deftypefun void gnc_suspend_gui_refresh (void)
 
321
Suspend the invocation of refresh handlers by the Component Manager.
 
322
This routine may be called multiple times. Each call increases the
 
323
suspend counter which starts at zero. When refreshes are not suspended,
 
324
any engine event causes a refresh cycle in which the refresh handler for
 
325
every component watching for that event is invoked.
 
326
@end deftypefun
 
327
 
 
328
@deftypefun void gnc_resume_gui_refresh (void)
 
329
Resume the invocation of refresh handlers by the Component Manager.
 
330
Each call reduces the suspend counter by one. When the counter reaches
 
331
zero, all events which have occured since the last refresh are collected
 
332
and passed to refresh handlers via the @var{changes} argument. Refresh
 
333
handlers will still be excluded based on their watches.
 
334
@end deftypefun
 
335
 
 
336
@deftypefun void gnc_gui_refresh_all (void)
 
337
Force all components to refresh, i.e., invoke all refresh handlers
 
338
with a @code{NULL} value for @var{changes}.
 
339
 
 
340
This routine may only be invoked when the suspend counter is zero. It
 
341
should never be mixed with the suspend/resume refresh routines.
 
342
@end deftypefun
 
343
 
 
344
@deftypefun gboolean gnc_gui_refresh_suspended (void)
 
345
Returns TRUE if GUI refreshing is currently suspended.
 
346
@end deftypefun
 
347
 
 
348
 
 
349
@node Finding Components, Iterating over Components, Controlling Refreshes, Component Manager
 
350
@section Finding Components
 
351
 
 
352
The Component Manager API provides two functions that allow components
 
353
to be searched for. Each function uses a find handler to perform the
 
354
actual search work. A find handler is a function with the following
 
355
signature:
 
356
 
 
357
@deftp {Data type} GNCComponentFindHandler gboolean (*) (gpointer @var{find_data}, gpointer @var{user_data})
 
358
A find handler is invoked with the @var{find_data} specified in the search
 
359
API call, and the @var{user_data} of a particular component. The handler
 
360
should return TRUE if the component matches the search criteria and FALSE
 
361
otherwise.
 
362
@end deftp
 
363
 
 
364
@deftypefun {GList *} gnc_find_gui_components (const char * @var{component_class}, GNCComponentFindHandler @var{find_handler}, gpointer @var{find_data})
 
365
Search for all components in class @var{component_class} using @var{find_handler}. Return a @code{GList} of the user data pointers of matching components.
 
366
@end deftypefun
 
367
 
 
368
@deftypefun gpointer gnc_find_first_gui_component (const char * @var{component_class}, GNCComponentFindHandler @var{find_handler}, gpointer @var{find_data})
 
369
Like @code{gnc_find_gui_components} above, but return the user data pointer
 
370
of the first matching component, or @code{NULL} if there are no matching
 
371
components.
 
372
@end deftypefun
 
373
 
 
374
 
 
375
@node Iterating over Components,  , Finding Components, Component Manager
 
376
@section Iterating over Components
 
377
 
 
378
The Component Manager API provides a function for iterating over all
 
379
components in a class as well as all registered components regardless
 
380
of class.
 
381
 
 
382
In either case, a generic component handler is invoked for each
 
383
component. The handler has the following signature:
 
384
 
 
385
@deftp {Data type} GNCComponentHandler void (*) (const char * @var{class}, gint @var{component_id}, gpointer @var{iter_data})
 
386
The component handler is invoked with the @var{class},
 
387
@var{component_id} of a particular component, as well as the
 
388
@var{iter_data} supplied in the iteration API call.
 
389
@end deftp
 
390
 
 
391
@deftypefun void gnc_forall_gui_components (const char * @var{component_class}, GNCComponentHandler @var{handler}, gpointer @var{iter_data})
 
392
Apply @var{handler} to every component in @var{component_class}. If
 
393
@var{component_class} is @code{NULL}, then iteration is performed over
 
394
every registered component. @var{iter_data} is supplied to @var{handler}
 
395
as the third argument.
 
396
@end deftypefun