_____ _ _
| __ \| | (_)
| |__) | |_ _ __ _ _ _ __ ___
| ___/| | | | |/ _` | | '_ \/ __|
| | | | |_| | (_| | | | | \__ \
|_| |_|\__,_|\__, |_|_| |_|___/
__/ |
|___/
by Shawn Hargreaves
Introduction
------------
It is very easy to extend the grabber and other datafile tools by writing
new plugin modules. These can add support for totally new types of
object, or they can just provide some additional import/export routines
to make one of the existing types work with a wider range of file
formats. Plugins are also capable of hooking into the grabber menu
system, either by adding new functionality to one of the existing menus
or by creating whole new menu options of their own.
Most of the standard grabber functionality is in fact implemented as
plugin modules. Try commenting out the contents of all the .inc files in
this directory, and then recompiling the grabber: you will find that
several of the menu items have gone missing, and it won't recognise
anything other than the basic file and binary data objects!
This file is not a complete manual for the plugin architecture. It
assumes that you already have a pretty good idea about how datafiles and
the GUI system operate, and that you aren't afraid of reading sources to
learn how they work. Your best bet is to have a look through datedit.h to
see what functions are available, and then use one of the existing
plugins as a basis for your addition. Using this file together with the
sources should be enough to get you going, but as always, feel free to
ask for help if you get stuck...
The Basics
----------
A plugin is implemented as a collection of files which are copied into
the tools/plugins directory, and will automatically be linked into the
grabber, dat utility, and other datafile tools. To install a plugin, you
can simply copy it to the plugins directory and then rebuild Allegro.
At the very least, you must provide a .c source file and a .inc setup
file. The .inc file will be included directly into datedit.c, and will be
executed once when the system initialises. From here you can call
whatever routines are needed to activate your plugin, which will usually
include at least one of the datedit_register_*() functions.
If a plugin makes use of external libraries, it can also provide a script
file, which contains additional linker options to be used when building
the datafile programs. This script has a different extension for each
supported platform, so that your plugin can use different linker options
depending on the compiler. The djgpp version uses .scr scripts, Watcom
uses .scw, MSVC/BCC use .scv, MinGW uses .scm and Unix uses .scu.
There are no restrictions on what names you can give these files other
than the required extensions, but to minimise the danger of namespace
conflicts I recommend that you stick to my convention of calling them
dat{type}.*, where {type} is the ID for the type of object being
implemented by this plugin.
There are three types of plugin interface, which can be used together or
in isolation. Object plugins add totally new types of datafile object,
and provide all the functions needed to manipulate and display this data.
Import/Export plugins provide the ability to read and write data from
external file formats, and can be used to add import and export functions
for both the predefined and custom object types. Finally, menu plugins
respond to user interface actions in the grabber, and can either hook
into the existing menu system or add completely new menu options of their
own.
Object Plugins
--------------
An object plugin is installed by calling the function:
void datedit_register_object(DATEDIT_OBJECT_INFO *info);
These plugins are defined by a header structure in the form:
typedef struct DATEDIT_OBJECT_INFO
{
int type;
char *desc;
void (*get_desc)(DATAFILE *dat, char *s);
void *(*makenew)(long *size);
int (*save)(DATAFILE *dat, ..., PACKFILE *f);
void (*plot)(DATAFILE *dat, int x, int y);
int (*dclick)(DATAFILE *dat);
void (*dat2s)(DATAFILE *dat, char *name, FILE *file, FILE *header);
} DATEDIT_OBJECT_INFO;
Object plugins add support for completely new types of object, and it
does not make sense to install more than one of these for the same type
ID (if you do, the second plugin will simply never be used).
The fields in this structure are:
'type'
The object type ID, as produced by the DAT_ID() macro.
'desc'
ASCII description of this object type.
get_desc()
Optional. Fills 's' with a description of the object. Implementing
this function allows you to provide a more detailed description (eg.
"32x32x8 bitmap" rather than just a generic "bitmap"). If you leave it
NULL, the 'desc' field will be displayed instead.
makenew()
Creates and returns a pointer to a new object of this type, and if the
object is going to use it, sets 'size' to the object size in bytes.
save()
Saves the object into an Allegro datafile. This takes quite a lot of
parameters, but you can ignore all of them except the 'dat' and 'f'
values: the others are only needed for nested datafiles. Returns TRUE
on success and FALSE on failure.
plot()
Optional. Draws the object onto the grabber screen at the specified
location. You must be careful to make sure that this routine will work
in all screen resolutions and color depths!
dclick()
Optional. Called when the user double-clicks on an object in the
grabber.
dat2s()
Optional. Used by the dat2s utility to convert an object into
assembler source code. If you don't implement this, your custom object
type will not be supported by dat2s.
You will notice that this interface does not provide any support for
loading these objects from datafiles, or for reading/writing external
file formats. That is because the datafile loading is handled internally
by the core Allegro lib (you should use register_datafile_object() to
install your load handler), and the import/export is handled by a
different type of plugin (see below).
The 'datsample_info' interface from datsamp.c is a good basic example of
an object plugin.
Import/Export Plugins
---------------------
An import/export plugin is installed by calling the function:
void datedit_register_grabber(DATEDIT_GRABBER_INFO *info);
These plugins are defined by a header structure in the form:
typedef struct DATEDIT_GRABBER_INFO
{
int type;
char *grab_ext;
char *export_ext;
void *(*grab)(char *filename, long *size, ...);
int (*export)(DATAFILE *dat, char *filename);
} DATEDIT_GRABBER_INFO;
Import/export plugins add file format support to existing types of
object. You will usually need at least one of these for each object
plugin that you install, but you can also use them to extend the
predefined object types. There is no limit on how many import/export
plugins can be attached to a single type of object, or on how many file
formats can be supported by a single plugin.
The fields in this structure are:
'type'
The object type ID, as produced by the DAT_ID() macro.
'grab_ext'
List of the file extensions that are supported for reading data,
separated by semicolons. May be left blank if this plugin only
provides export functions.
'export_ext'
List of the file extensions that are supported for saving data,
separated by semicolons. May be left blank if this plugin only
provides import functions.
grab()
Reads a new object from the named file, returning a pointer to it. If
this type of object requires it, also sets 'size' to the size of the
object in bytes.
export()
Saves an object into the named file. Returns TRUE on success and FALSE
on failure.
The 'datfont_grabber' interface from datfont.c is a good example of an
import/export plugin that supports several different file formats.
Menu Plugins
------------
A menu plugin is installed by calling the function:
void datedit_register_menu(DATEDIT_MENU_INFO *info);
These plugins are defined by a header structure in the form:
typedef struct DATEDIT_MENU_INFO
{
MENU *menu;
int (*query)(int popup);
int flags;
int key;
} DATEDIT_MENU_INFO;
Menu plugins are based on the Allegro GUI menu structure. If you install
a plugin for a menu option that already exists, it will hook into the
existing menu, but you can also add completely new options by specifying
a totally new menu text string.
The fields in this structure are:
'menu'
Points to the Allegro menu structure for this option. Note that this
is only one single menu item, not a whole array of entries. The flags
and dp fields from this structure will be overridden by the grabber,
but you must fill in the text field with your menu text string, and
either the proc function pointer with your action routine (for simple
command menus like the "grab" function), or the child field with a
pointer to a nested menu (for hierarchical structures like the "new"
menu).
query()
Optional. If not NULL, this routine will be called to check whether
this plugin command is suitable for use in the current situation. For
normal pulldown menus, the 'popup' parameter will be FALSE, and you
have a chance to either return TRUE, in which case your command will
be used, or FALSE, in which case the default processing will be
carried out (this allows a plugin to take over standard commands like
"grab", but reject them when anything other than their specific type
of object is selected, so that the normal grab command will still work
for other object types). For popup (right mouse button) menus, this
routine will be called immediately before the menu is displayed, with
the 'popup' parameter set to TRUE. If you return FALSE from this call,
your command will not be included in the popup (this is how things
like 'autocrop' are able to only appear when you right-click on a
bitmap object).
'flags'
Indicates where in the menu system you would like your command to be
added. This is a bitfield containing any combination of the values:
DATEDIT_MENU_FILE - add it to the File menu
DATEDIT_MENU_OBJECT - add it to the Object menu
DATEDIT_MENU_HELP - add it to the Help menu
DATEDIT_MENU_POPUP - include it in right-click popup menus
DATEDIT_MENU_TOP - a new entry in the topmost menu bar
'key'
If non-zero, this is the ASCII code of a keyboard shortcut for your
command. Please be cautious in your use of these, because if several
plugins try to hook the same keyboard shortcut, obviously only one of
them is going to be able to get it :-)
The 'datworms_menu' interface from datworms.c is a good example of how to
add a new menu command, and 'datpal_grabber_menu' from datpal.c shows how
to hook into an existing command (it adds a custom function so that
palettes can be grabbed from a previously read bitmap, rather than
directly from a disk file).
Misc
----
Bear in mind that these plugins are used by tha dat, dat2s, and pat2dat
utilities as well as the grabber, so you cannot assume anything about the
system setup while your code is run. The menu commands, view, and dclick
functions will only ever be used in a graphics mode (which could be any
resolution and color depth), but the other functions may equally well be
called from a graphics or text mode environment.
For displaying messages, errors, prompting the user for input, etc, use
the datedit_msg(), datedit_error(), and datedit_ask() functions. These
are implemented as printf() calls by the text mode utilities and as alert
boxes by the grabber, so they will work correctly in any situation.
There are lots of useful helper functions in datedit.h. Perhaps most
importantly when implementing menu commands, grabber_single_selection()
returns a pointer to the currently selected object (NULL if there is a
multiple selection), and grabber_foreach_selection() iterates through
every selected object, using a callback function to let you process
multiple objects with a minimum of hassle.
And Finally
-----------
Good luck!