mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-30 12:44:01 -06:00
83d14e1c1c
It is split into - /libgnucash (for the non-gui bits) - /gnucash (for the gui) - /common (misc source files used by both) - /bindings (currently only holds python bindings) This is the first step in restructuring the code. It will need much more fine tuning later on.
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 ocurred 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
|