mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-16 18:25:11 -06:00
397 lines
16 KiB
Plaintext
397 lines
16 KiB
Plaintext
@node Component Manager
|
|
@chapter Component Manager
|
|
@cindex Component Manager
|
|
|
|
@strong{This whole document is completely outdated. Don't read this. All
|
|
function names and every described structure has changed
|
|
completely. Only read this if you want to know how gnucash looked like
|
|
in 1999. This is completely outdated!}
|
|
|
|
The Component Manager (hereafter referred to as the CM) is a framework
|
|
for managing GUI objects in GnuCash. The CM provides several services.
|
|
|
|
First, components may request automatic invocation of a 'refresh'
|
|
handler that is called when a component may need to be redrawn. This
|
|
mechanism is tied into the Engine's Event mechanism (@pxref{Events}),
|
|
so that GUI components are notified when relevant Engine entities are
|
|
created, modified, or destroyed.
|
|
|
|
Components may also provide a 'close' handler so that the component
|
|
may be closed through the CM API.
|
|
|
|
The CM also provides the ability to search for existing components.
|
|
|
|
|
|
@menu
|
|
* Component Manager Introduction::
|
|
* Refresh Mechanism::
|
|
* CM Initialization and Shutdown::
|
|
* Refresh Handlers::
|
|
* Close Handlers::
|
|
* Registering and Unregistering Components::
|
|
* Watching Engine Objects::
|
|
* Controlling Refreshes::
|
|
* Finding Components::
|
|
* Iterating over Components::
|
|
@end menu
|
|
|
|
|
|
@node Component Manager Introduction, Refresh Mechanism, Component Manager, Component Manager
|
|
@section Introduction
|
|
|
|
The GnuCash GUI must manage many different components (registers,
|
|
reconcile windows, reports, graphs, main window, etc.). Functions which
|
|
need to be performed on or with GUI components include:
|
|
|
|
|
|
@itemize
|
|
|
|
@item Refreshing components. When Engine objects (Accounts,
|
|
Transactions, Splits, etc.) are created, modified, or destroyed,
|
|
the GUI components which reference those objects must be refreshed.
|
|
|
|
@item Searching for existing components. For example, when the
|
|
user chooses to 'Reconcile' an account with an existing reconcile
|
|
dialog, that dialog should be raised to the top in lieu of creating
|
|
a new reconcile dialog.
|
|
|
|
@item Closing components.
|
|
|
|
@end itemize
|
|
|
|
|
|
In particular, keeping components updated in the face of changes in the
|
|
Engine is a difficult problem. Requirements for handling refreshes
|
|
include:
|
|
|
|
@itemize
|
|
|
|
@item The Engine should be able to inform user code when any account
|
|
or transaction is changed (including changing their respective splits).
|
|
This requirement is satisfied by the Engine Events.
|
|
|
|
@item The refresh mechanism should not have specific knowledge of
|
|
individual windows and other GUI elements. It should be easy to
|
|
add new refreshable elements to the mechanism.
|
|
|
|
@item Changes should be batchable with respect to refreshes, so that
|
|
a sequence of changes only cause a refresh after the last change
|
|
in a batch. Batching should be nestable, for coding simplicity.
|
|
|
|
@item For very large batches of changes (loading files) we need to be
|
|
able to turn off the mechanism entirely, perform the changes, and then
|
|
perform a global, forced refresh. This should also be nestable.
|
|
|
|
@item The Engine should not be managing GUI components.
|
|
|
|
@item The refresh mechanism should be extendable to a multi-user
|
|
situation in which changes can come from external components.
|
|
|
|
@item Components should be able to specify which Engine entities
|
|
can cause refreshes. This requirement allows the implementation
|
|
to avoid unnecessary refreshing.
|
|
|
|
@end itemize
|
|
|
|
|
|
@node Refresh Mechanism, CM Initialization and Shutdown, Component Manager Introduction, Component Manager
|
|
@section Refresh Mechanism
|
|
@cindex Refresh Mechanism
|
|
|
|
The major design decisions of the CM relate to the refresh
|
|
mechanism. The refresh mechanism consists of two parts, the Engine
|
|
component and the GUI component. The Engine component is the
|
|
Event mechanism (@pxref{Events}), while the GUI component is the
|
|
Component Manager, which provide refresh functionality as well
|
|
as other services.
|
|
|
|
The diagram below illustrated the design of the GnuCash refresh
|
|
mechanism.
|
|
|
|
@example
|
|
----------
|
|
| |
|
|
| Engine |
|
|
| |
|
|
----------
|
|
/|\
|
|
|
|
|
|--- Events (from Engine)
|
|
|
|
|
\|/
|
|
-------------------------
|
|
| |
|
|
| Component Manager |
|
|
| |
|
|
-------------------------
|
|
/ /|\ \ GUI Commands
|
|
/ | \--- including refresh
|
|
/ \|/ \ invocations (from CM)
|
|
----------------- -----------------
|
|
| | | |
|
|
| GUI Component | | GUI Component | ...
|
|
| | | |
|
|
----------------- -----------------
|
|
@end example
|
|
|
|
The top-level component is the Engine, which emits Events to the
|
|
Component Manager. In fact, the Engine will send events to any
|
|
registered handler, but in GnuCash, only the CM registers with
|
|
the engine. All other GUI components register with the CM.
|
|
|
|
The CM invokes the refresh handlers of GUI components based on the
|
|
Engine events received the CM has received as well as information
|
|
provided by the GUI components (such as which specific Engine
|
|
entities the components are 'watching').
|
|
|
|
|
|
@node CM Initialization and Shutdown, Refresh Handlers, Refresh Mechanism, Component Manager
|
|
@section Initialization and Shutdown
|
|
|
|
@deftypefun void gnc_component_manager_init (void)
|
|
Initialize the Component Manager. This should be called
|
|
before using an other CM functions.
|
|
@end deftypefun
|
|
|
|
@deftypefun void gnc_component_manager_shutdown (void)
|
|
Shutdown the Component Manager. This should be called
|
|
to release Component Manager resources.
|
|
@end deftypefun
|
|
|
|
|
|
@node Refresh Handlers, Close Handlers, CM Initialization and Shutdown, Component Manager
|
|
@section Refresh Handlers
|
|
@tindex EventInfo
|
|
|
|
When a component registers itself with the CM, it may specify two
|
|
different handlers: a refresh handler and a close handler. A refresh
|
|
handler is a function with the following type signature:
|
|
|
|
@deftp {Data type} GNCComponentRefreshHandler void (*) (GHashTable *@var{changes}, gpointer @var{user_data})
|
|
This is the type signature of a refresh handler. The @var{changes} hash
|
|
describes the Engine events which have occurred since the last refresh.
|
|
It is used to determine whether a refresh is actually needed. It may,
|
|
however, be @code{NULL}, meaning the component must perform a refresh.
|
|
The @code{user_data} pointer is the data pointer supplied when the
|
|
component was registered.
|
|
@end deftp
|
|
|
|
When a refresh handler is invoked, it should perform the following actions:
|
|
|
|
@enumerate
|
|
|
|
@item Check if the component should be closed. When a refresh handler
|
|
is invoked, any and all of the Engine objects which the component was
|
|
referencing may have been destroyed, possibly making the component
|
|
obsolete. For example, a dialog to edit the parameters of a specific
|
|
Account should be automatically closed when the account is deleted. On
|
|
the other hand, a list of all Accounts in a hierarchy should be updated
|
|
when an Account is deleted, but not necessarily closed.
|
|
|
|
Components must test for the destruction of critical Engine objects
|
|
in two ways.
|
|
|
|
@enumerate
|
|
|
|
@item Use the @code{GUID} lookup functions (such as
|
|
@code{xaccAccountLookup}), to determine if the engine object is still
|
|
bound to its @code{GUID}. Of course, this means that components should
|
|
store the @code{GUID}s of critical Engine objects instead of simply
|
|
storing their C pointers.
|
|
|
|
@item If the first test succeeds and the @var{changes} hash table
|
|
of the refresh handler is non-NULL, the component should use the hash to
|
|
determine of the GNC_EVENT_DESTROY event has occurred for the Engine
|
|
object in question. The @var{changes} hash is a mapping from
|
|
@code{GUID}s to @code{EventInfo} structures. An @code{EventInfo}
|
|
structure has a single member, @code{event_mask}, of type
|
|
@code{GNCEngineEventType}. The @code{event_mask} is a logical or of the
|
|
@code{GNC_EVENT_CREATE}, @code{GNC_EVENT_MODIFY}, and
|
|
@code{GNC_EVENT_DESTROY} event types. Since refreshes may not occur with
|
|
every Engine event, @code{event_mask} may have all three values.
|
|
|
|
There is a utility function for accessing the @var{changes} hash:
|
|
|
|
@deftypefun {const EventInfo *} gnc_gui_get_entity_events (GHashTable * @var{changes}, const GUID * @var{entity})
|
|
Return the event info for the entity specified by @var{entity}. If there
|
|
are no events for that entity, @code{NULL} is returned.
|
|
@end deftypefun
|
|
|
|
@end enumerate
|
|
|
|
If the @var{changes} hash is NULL, then the first test is sufficient
|
|
to determine whether an object has been destroyed.
|
|
|
|
If the refresh handler determines the component should be destroyed,
|
|
it should destroy the component and return.
|
|
|
|
@item Check if the component should be refreshed. If the @var{changes}
|
|
hash is @code{NULL}, then the component must refresh itself. Otherwise,
|
|
it may use the @var{changes} hash to determine whether or not a refresh
|
|
is actually necessary. However, since the component may specify which
|
|
particular Engine objects are relevant (see "Watching Components"
|
|
below), generally a component will simply refresh unconditionally.
|
|
|
|
@item Refresh the component if necessary. This includes updating the
|
|
GUI as well as internal structures to reflect the new state of Engine
|
|
objects.
|
|
|
|
@end enumerate
|
|
|
|
|
|
@node Close Handlers, Registering and Unregistering Components, Refresh Handlers, Component Manager
|
|
@section Close Handlers
|
|
|
|
A close handler is a function with the following type signature:
|
|
|
|
@deftp {Data type} GNCComponentCloseHandler void (*) (gpointer @var{user_data})
|
|
This is the type signature of a close handler. The @code{user_data}
|
|
pointer is the data pointer supplied when the component was registered.
|
|
@end deftp
|
|
|
|
The invocation of a close handler is a command to the component to close
|
|
itself. The component must close itself -- the handler should not be
|
|
ignored. The component is still responsible for unregistering itself
|
|
with the Component Manager.
|
|
|
|
|
|
@node Registering and Unregistering Components, Watching Engine Objects, Close Handlers, Component Manager
|
|
@section Registering and Unregistering Components
|
|
|
|
@deftypefun gint gnc_register_gui_component (const char * @var{component_class}, GNCComponentRefreshHandler @var{refresh_handler}, GNCComponentCloseHandler @var{close_handler}, gpointer @var{user_data})
|
|
Register a gui component with the Component Manager.
|
|
|
|
The variable @var{component_class} is a string specifying a class of
|
|
components. Certain CM functions can be performed on all components in a
|
|
class. For that reason, components in the same class should all use the
|
|
same type for @var{user_data}.
|
|
|
|
@var{refresh_handler} and @var{close_handler} specify the refresh and close
|
|
handlers, respectively. Either or both may be @code{NULL}.
|
|
|
|
The @var{user_data} is supplied as an argument when the handlers are invoked.
|
|
|
|
The function returns the id of the newly-registered component, or
|
|
@code{NO_COMPONENT} if there was an error.
|
|
@end deftypefun
|
|
|
|
After a refresh handler is registered, the component must use the API
|
|
calls under "Watching Engine Objects" below to inform the Component
|
|
Manager which engine entities are being watched, i.e., which engine
|
|
entities may cause the component to need refreshing. When a component is
|
|
first registered, it is not watching anything, and thus will not receive
|
|
refresh events.
|
|
|
|
@deftypefun void gnc_unregister_gui_component (gint @var{component_id})
|
|
Unregister the component with id @var{component} from the CM.
|
|
@end deftypefun
|
|
|
|
@deftypefun void gnc_unregister_gui_component_by_data (const char * @var{component_class}, gpointer @var{user_data})
|
|
Unregister all gui components in the class @var{component_class} which have
|
|
@var{user_data} as a user data pointer.
|
|
@end deftypefun
|
|
|
|
|
|
@node Watching Engine Objects, Controlling Refreshes, Registering and Unregistering Components, Component Manager
|
|
@section Watching Engine Objects
|
|
|
|
@deftypefun void gnc_gui_component_watch_entity (gint @var{component_id}, const GUID * @var{entity}, GNCEngineEventType @var{event_mask})
|
|
If @var{event_mask} is non-zero, add the Engine entity specified by
|
|
@var{entity} to the list of entities being watched by the component with
|
|
id @var{component_id}. Only the events specified by @var{events} are
|
|
watched. If @var{event_mask} is 0, the call turns off watching for the
|
|
entity.
|
|
@end deftypefun
|
|
|
|
@deftypefun void gnc_gui_component_watch_entity_type (gint @var{component_id}, GNCIdType @var{entity_type}, GNCEngineEventType @var{event_mask})
|
|
if @var{event_mask}, turn on watching for all entities of @var{entity_type}.
|
|
Only events specified by @var{event_mask} are watched. If @var{event_mask}
|
|
is 0, turns off watching for the entity type.
|
|
@end deftypefun
|
|
|
|
@deftypefun void gnc_gui_component_clear_watches (gint @var{component_id})
|
|
Clear all watches for the component with id @var{component_id}.
|
|
@end deftypefun
|
|
|
|
|
|
@node Controlling Refreshes, Finding Components, Watching Engine Objects, Component Manager
|
|
@section Controlling Refreshes
|
|
|
|
@deftypefun void gnc_suspend_gui_refresh (void)
|
|
Suspend the invocation of refresh handlers by the Component Manager.
|
|
This routine may be called multiple times. Each call increases the
|
|
suspend counter which starts at zero. When refreshes are not suspended,
|
|
any engine event causes a refresh cycle in which the refresh handler for
|
|
every component watching for that event is invoked.
|
|
@end deftypefun
|
|
|
|
@deftypefun void gnc_resume_gui_refresh (void)
|
|
Resume the invocation of refresh handlers by the Component Manager.
|
|
Each call reduces the suspend counter by one. When the counter reaches
|
|
zero, all events which have occurred since the last refresh are collected
|
|
and passed to refresh handlers via the @var{changes} argument. Refresh
|
|
handlers will still be excluded based on their watches.
|
|
@end deftypefun
|
|
|
|
@deftypefun void gnc_gui_refresh_all (void)
|
|
Force all components to refresh, i.e., invoke all refresh handlers
|
|
with a @code{NULL} value for @var{changes}.
|
|
|
|
This routine may only be invoked when the suspend counter is zero. It
|
|
should never be mixed with the suspend/resume refresh routines.
|
|
@end deftypefun
|
|
|
|
@deftypefun gboolean gnc_gui_refresh_suspended (void)
|
|
Returns TRUE if GUI refreshing is currently suspended.
|
|
@end deftypefun
|
|
|
|
|
|
@node Finding Components, Iterating over Components, Controlling Refreshes, Component Manager
|
|
@section Finding Components
|
|
|
|
The Component Manager API provides two functions that allow components
|
|
to be searched for. Each function uses a find handler to perform the
|
|
actual search work. A find handler is a function with the following
|
|
signature:
|
|
|
|
@deftp {Data type} GNCComponentFindHandler gboolean (*) (gpointer @var{find_data}, gpointer @var{user_data})
|
|
A find handler is invoked with the @var{find_data} specified in the search
|
|
API call, and the @var{user_data} of a particular component. The handler
|
|
should return TRUE if the component matches the search criteria and FALSE
|
|
otherwise.
|
|
@end deftp
|
|
|
|
@deftypefun {GList *} gnc_find_gui_components (const char * @var{component_class}, GNCComponentFindHandler @var{find_handler}, gpointer @var{find_data})
|
|
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.
|
|
@end deftypefun
|
|
|
|
@deftypefun gpointer gnc_find_first_gui_component (const char * @var{component_class}, GNCComponentFindHandler @var{find_handler}, gpointer @var{find_data})
|
|
Like @code{gnc_find_gui_components} above, but return the user data pointer
|
|
of the first matching component, or @code{NULL} if there are no matching
|
|
components.
|
|
@end deftypefun
|
|
|
|
|
|
@node Iterating over Components, , Finding Components, Component Manager
|
|
@section Iterating over Components
|
|
|
|
The Component Manager API provides a function for iterating over all
|
|
components in a class as well as all registered components regardless
|
|
of class.
|
|
|
|
In either case, a generic component handler is invoked for each
|
|
component. The handler has the following signature:
|
|
|
|
@deftp {Data type} GNCComponentHandler void (*) (const char * @var{class}, gint @var{component_id}, gpointer @var{iter_data})
|
|
The component handler is invoked with the @var{class},
|
|
@var{component_id} of a particular component, as well as the
|
|
@var{iter_data} supplied in the iteration API call.
|
|
@end deftp
|
|
|
|
@deftypefun void gnc_forall_gui_components (const char * @var{component_class}, GNCComponentHandler @var{handler}, gpointer @var{iter_data})
|
|
Apply @var{handler} to every component in @var{component_class}. If
|
|
@var{component_class} is @code{NULL}, then iteration is performed over
|
|
every registered component. @var{iter_data} is supplied to @var{handler}
|
|
as the third argument.
|
|
@end deftypefun
|