_____ _ _ | __ \| | (_) | |__) | |_ _ __ _ _ _ __ ___ | ___/| | | | |/ _` | | '_ \/ __| | | | | |_| | (_| | | | | \__ \ |_| |_|\__,_|\__, |_|_| |_|___/ __/ | |___/ 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!