1
@node Component Manager
2
@chapter Component Manager
3
@cindex Component Manager
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!}
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.
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.
19
Components may also provide a 'close' handler so that the component
20
may be closed through the CM API.
22
The CM also provides the ability to search for existing components.
26
* Component Manager Introduction::
28
* CM Initialization and Shutdown::
31
* Registering and Unregistering Components::
32
* Watching Engine Objects::
33
* Controlling Refreshes::
34
* Finding Components::
35
* Iterating over Components::
39
@node Component Manager Introduction, Refresh Mechanism, Component Manager, Component Manager
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:
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.
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.
58
@item Closing components.
63
In particular, keeping components updated in the face of changes in the
64
Engine is a difficult problem. Requirements for handling refreshes
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.
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.
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.
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.
85
@item The Engine should not be managing GUI components.
87
@item The refresh mechanism should be extendable to a multi-user
88
situation in which changes can come from external components.
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.
97
@node Refresh Mechanism, CM Initialization and Shutdown, Component Manager Introduction, Component Manager
98
@section Refresh Mechanism
99
@cindex Refresh Mechanism
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
108
The diagram below illustrated the design of the GnuCash refresh
119
|--- Events (from Engine)
122
-------------------------
124
| Component Manager |
126
-------------------------
128
/ | \--- including refresh
129
/ \|/ \ invocations (from CM)
130
----------------- -----------------
132
| GUI Component | | GUI Component | ...
134
----------------- -----------------
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.
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').
148
@node CM Initialization and Shutdown, Refresh Handlers, Refresh Mechanism, Component Manager
149
@section Initialization and Shutdown
151
@deftypefun void gnc_component_manager_init (void)
152
Initialize the Component Manager. This should be called
153
before using an other CM functions.
156
@deftypefun void gnc_component_manager_shutdown (void)
157
Shutdown the Component Manager. This should be called
158
to release Component Manager resources.
162
@node Refresh Handlers, Close Handlers, CM Initialization and Shutdown, Component Manager
163
@section Refresh Handlers
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:
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.
179
When a refresh handler is invoked, it should perform the following actions:
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.
191
Components must test for the destruction of critical Engine objects
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.
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.
213
There is a utility function for accessing the @var{changes} hash:
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.
222
If the @var{changes} hash is NULL, then the first test is sufficient
223
to determine whether an object has been destroyed.
225
If the refresh handler determines the component should be destroyed,
226
it should destroy the component and return.
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.
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
242
@node Close Handlers, Registering and Unregistering Components, Refresh Handlers, Component Manager
243
@section Close Handlers
245
A close handler is a function with the following type signature:
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.
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.
258
@node Registering and Unregistering Components, Watching Engine Objects, Close Handlers, Component Manager
259
@section Registering and Unregistering Components
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.
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}.
269
@var{refresh_handler} and @var{close_handler} specify the refresh and close
270
handlers, respectively. Either or both may be @code{NULL}.
272
The @var{user_data} is supplied as an argument when the handlers are invoked.
274
The function returns the id of the newly-registered component, or
275
@code{NO_COMPONENT} if there was an error.
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
285
@deftypefun void gnc_unregister_gui_component (gint @var{component_id})
286
Unregister the component with id @var{component} from the CM.
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.
295
@node Watching Engine Objects, Controlling Refreshes, Registering and Unregistering Components, Component Manager
296
@section Watching Engine Objects
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
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.
312
@deftypefun void gnc_gui_component_clear_watches (gint @var{component_id})
313
Clear all watches for the component with id @var{component_id}.
317
@node Controlling Refreshes, Finding Components, Watching Engine Objects, Component Manager
318
@section Controlling Refreshes
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.
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.
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}.
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.
344
@deftypefun gboolean gnc_gui_refresh_suspended (void)
345
Returns TRUE if GUI refreshing is currently suspended.
349
@node Finding Components, Iterating over Components, Controlling Refreshes, Component Manager
350
@section Finding Components
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
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
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.
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
375
@node Iterating over Components, , Finding Components, Component Manager
376
@section Iterating over Components
378
The Component Manager API provides a function for iterating over all
379
components in a class as well as all registered components regardless
382
In either case, a generic component handler is invoked for each
383
component. The handler has the following signature:
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.
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.