_      _ _
                             /_\  __| (_)_ __  ___
                            / _ \/ _` | | '  \/ -_)
                           /_/ \_\__,_|_|_|_|_\___|

                           Allegro Dialogs Made Easy

                                Internal Stuff

                               by Sven Sandberg



This file describes some issues that developers but not users will need. Adime's internal mechanisms allow you to add custom adime_dialogf() formats without modifying the library. Most of this file describes how to write an own Adime format. You are probably required to look at the code for some of the simpler formats that come with Adime in order to figure out how things work anyway (dbool.c and dstring.c should be the most descriptive ones). I'm not exactly sure that this will be useful for anybody except possibly myself, as a reference in the future when I have forgotten everything about Adime.

Since these are all internal functions, I can't promise that their APIs are set in stone. In particular, I'm planning to change the format of `_adime_create_MyFormat()' and `_adime_count_MyFormat()', and possibly some of the other related functions.

What You Need to Write

A custom adime_dialogf() format consists of a set of functions and a call to `_adime_register_dialogf_format()'. This will be given the format string that you want associated with the new format, along with function pointers to your functions and some more data which will be described later on.

The functions you need to write are listed below. Not all of them need to be implemented; if you don't need one of them, just pass `NULL' for it when you call `_adime_register_dialogf_format()'.

int *_adime_count_MyFormat(const char *desc, const char *modifiers, adime_va_list args, void **args_out);
Function that you must implement if you want to write your own adime_dialogf() format.
This function will be called exactly once for each format specifier before anything else, and should return the number of `DIALOG *' structures that need to be allocated. `modifiers' is the modifiers given by the person who called `adime_dialof()' inside the brackets after the format text. `desc' is the description text given by the user. This function is responsible for reading all its arguments from the `args' list. `*args_out' will be passed to `_adime_create_MyFormat()', so this is where you want to save the arguments (they can only be read once).

int _adime_create_MyFormat(DIALOG *dialog, const char *desc, const char *modifiers, void *args);
Function that you must implement if you want to write your own adime_dialogf() format.
This will be called when `adime_dialogf()' constructs the dialog and should set up the `DIALOG *' structures appropriately.
You should fill in `dialog[0]' (and possibly `dialog[1]', `dialog[2]' etc, depending on the number of objects returned by `_adime_count_MyFormat()') with the actual object(s) that should appear in the dialog. Unless you specified the `handle_desc' flag when you registered the format (see below), the description text is already created for you, so you don't need to do that. The objects will be automatically aligned vertically after you created them. Their final position on screen will be determined only after all `_adime_create_MyFormat()' functions have been called (so that the dialog can be automatically centred, etc). `desc' is the description text passed to `adime_dialogf()' which belongs to this format. `modifiers' is the modifier string between the brackets after the format. Both `modifiers' and `desc' are guaranteed to be allocated memory and will not be freed until the dialog is destroyed, so you are free to reference directly to these. `args' points to whatever your `_adime_count_MyFormat()' function saved in its `*args_out' parameter. The function should normally return 0 (see Advanced Stuff for more info on what other return values are possible). It is guaranteed to be called only once unless it returns non-zero.
See also: Advanced Stuff.

void _adime_store_MyFormat(DIALOG *dialog);
Function that you may implement if you want to write your own adime_dialogf() format.
Will be called when the user clicks the OK button in the dialog. Its purpose is to copy the information given by the user to wherever the result should be stored.

void _adime_reset_MyFormat(DIALOG *dialog);
Function that you may implement if you want to write your own adime_dialogf() format.
This function should take care of resetting the dialog object to reflect the default value, as given by the parameters to `adime_dialogf()'. Note that since this function does that job, the `_adime_create_MyFormat()' function does not need to do it.

void _adime_destroy_MyFormat(DIALOG *dialog);
Function that you may implement if you want to write your own adime_dialogf() format.
Will be called in the shut down code for `adime_dialogf()'. If your `_adime_create_MyFormat()' function mallocates any data, this is the place to free it.

Helper Functions

There are some helper functions that you will probably find useful for parsing the modifier string.

int _adime_get_int_arg(const char **arg_text, int *out);
int _adime_get_double_arg(const char **arg_text, double *out);
int _adime_get_string_arg(const char **arg_text, char **out_p);
int _adime_get_strlist_arg(const char **arg_text, int *out_num, ***out_list);
Return the next argument from `*arg_text', in different formats. `_adime_get_int_arg()' fills `*out' with an integer parsed from the `*arg_text'. `_adime_get_double_arg()' does the same for a double. `_adime_get_string_arg()' allocates a new string which ends when the `arg_text' ends or at the next comma not escaped with '%', and saves it in `*out_p'. `_adime_get_strlist_arg()' reads a list of strings, separated by ';' (actual semicolons can be escaped by '%'), up to the next comma (actual commas can be escaped by `%') or end of string. All these functions return a combination of the following flags:
_ADIME_ARG_COMMA - The reading was interrupted by a comma in the string.
_ADIME_ARG_END - The reading was interrupted by end of string.
_ADIME_ARG_NONDIGIT - The reading was interrupted by something that was not a digit.
_ADIME_ARG_DIGIT - The reading was interrupted by a digit.
_ADIME_ARG_READ - Something was actually read before the reading was interrupted.

void _adime_inwards_bevel(DIALOG *dialog, int ofs, int white_space);
Draws the kind of bevel that is around Adime's text boxes in the area given by `d'. If ofs is not 0, the bevel will grow by that number of pixels. If the `white_space' flag is set, an extra one pixel wide border of white will be drawn inside the bevel.

Registering the Format

void _adime_register_dialogf_format( char *specifier, int handle_desc, int (*_adime_count_MyFormat)(DIALOGF_DIALOG *dialog, const char *desc, const char *arg_text, adime_va_list_p args), int (*_adime_create_MyFormat)(DIALOG *dialog, const char *desc, const char *arg_text, adime_va_list_p args), void (*_adime_store_MyFormat)(DIALOG *object), void (*_adime_reset_MyFormat)(DIALOG *object), void (*_adime_destroy_MyFormat)(DIALOG *object));
When you have written the appropriate functions that take care of the format, you just need to make `adime_dialogf()' aware of your functions. This is done by calling `_adime_register_dialogf_format()'. First argument is the format text. So e.g. the built-in format %int[] uses "int" here. If the `handle_desc' flag is not set, `adime_dialogf()' will take care of making a dialog object for the description text, but if you want to handle that yourself you could pass 0 instead. The rest of the parameters are the pointers to the functions that you created earlier. `_adime_count_MyFormat()' and `_adime_create_MyFormat()' must always be implemented. You will normally want to pass NULL for the `outside_func', but see the description for that function for info on when you need it. If the dialog object doesn't have any custom data (like %line[] and %nothing[]), you may pass NULL for `store_result' and `reset_dialog'. `destroy_data' can also be NULL, in case you don't allocate any custom data when creating the dialog.

Note that this applies if you are making a format _outside_ Adime, i.e. one that is not linked into the library. If you would like to write a format _in_ Adime, then you need to do it differently: in adime.h, define the macro ADIME_FORMAT_MyFormat in the same way as the other macros are defined. This should be an element in a `struct _ADIME_DIALOGF_FORMAT', which is defined just above the list of macros. You must also declare all functions that this macro uses. Then add this macro to register.c, and finally add some documentation, both in the help for `adime_dialogf()' and in the list of formats near the very end of adime._tx.

See also: What You Need to Write.

Advanced Stuff

There are some obscure features that you normally won't need to know about:

Calling `adime_dialogf()' Recursively from `adime_dialogf()'
`adime_dialogf()' is written so that it is safe to call `adime_dialogf()' recursively from an `_adime_outside_MyFormat()' function (this is of course required for the %dialogf[] format, but also for the %wlist[], %wstrlist[] and %wdatafile[] formats, which are implemented by opening a new `adime_dialogf()' window). In addition, a call to `adime_dialogf()' can be split up into five parts. `_adime_dialogf_start()' must be called first, and you are not allowed to use the `_ADIME_DIALOGF_DIALOG *' after `_adime_dialogf_end()' has been called, but between those calls you can happily call `_adime_dialogf_run()', `_adime_dialogf_store_results()' and `_adime_dialogf_reset_dialog()' any number of times you like and in any order you like.

_ADIME_DIALOGF_DIALOG *_adime_dialogf_start(const char *title, int x_pos, int y_pos, int edit_w, const char *format, adime_va_list_p args);
Sets up a `_ADIME_DIALOGF_DIALOG *', which can be used by `_adime_dialogf_run()' to open a dialog. This will traverse the `args' so that it points to after the last argument. A `adime_va_list_p' is like a pointer to a `va_list', but may be implemented in another way. The reason for this is that some platforms (currently I only know about Watcom, but there may be others) define `va_list's in a brain-dead way so that you can't create a pointer to it. See the implementation of `adime_vdialogf()' in dialogf.c for info on how to convert a `va_list' to a `adime_va_list_p'.
See also: Advanced Stuff, _adime_dialogf_run, _adime_dialogf_store_results, _adime_dialogf_reset_dialog, _adime_dialogf_end.

int _adime_dialogf_run(DIALOGF_DIALOG *dd);
Runs a dialog until the user closes it, but doesn't store any results or free any memory. Returns negative to indicate that the results should be saved (and the sign then removed) and positive to indicate that the results should not be saved. *This changed in version 1.9.2.*
See also: Advanced Stuff, _adime_dialogf_start, _adime_dialogf_store_results, _adime_dialogf_reset_dialog, _adime_dialogf_end.

void _adime_dialogf_store_results(DIALOGF_DIALOG *dd);
Stores the data that the user entered in the dialog in the appropriate output fields.
See also: Advanced Stuff, _adime_dialogf_start, _adime_dialogf_run, _adime_dialogf_reset_dialog, _adime_dialogf_end.

void _adime_dialogf_reset_dialog(DIALOGF_DIALOG *dd);
Resets all the dialog objects to reflect the default values as given by the `adime_va_list_p args' arguments to `_adime_dialogf_start()'.
See also: Advanced Stuff, _adime_dialogf_start, _adime_dialogf_run, _adime_dialogf_store_results, _adime_dialogf_end.

void _adime_dialogf_end(DIALOGF_DIALOG *dd);
Destroys all mallocated memory for the dialog.
See also: Advanced Stuff, _adime_dialogf_start, _adime_dialogf_run, _adime_dialogf_store_results, _adime_dialogf_reset_dialog.

DIALOG *_adime_dialog;
Pointer to the first object in the current dialog, in case any function needs to access it.

Calling `_adime_create_MyFormat()' Several Times

Some of the built in formats (%line[] and %nothing[]) need to know the max width of the whole dialog. Naturally, this can not be known until all the objects have been created. For this purpose, any `_adime_create_MyFormat()' function that returns 1 will be called again after all other creation functions have been called. The return value is actually a flag field, where the zeroth and first bit may both be set or cleared, so the possible values are 0, 1, 2 and 3. If bit #1 is set, the `_adime_create_MyFormat()' function will be called in a final pass, after all other creation routines have returned a number in which the first bit is cleared.

Exiting from the Dialog

If an object wishes to exit the dialog, then it should set the `return_value' field of the global `_adime_dialog' object to a number in the same format as returned from the callback function for %button[]. I.e., a nonzero, non-INT_MIN number, whose sign indicates whether the contents of the dialog should be saved (as for ok buttons) or discarded (as for cancel buttons), and whose absolute value will be returned from `dialogf()'. Then, to actually exit the dialog, it should return D_CLOSE from a dialog proc.


Use the _ADIME_MALLOC(pointer, size, TYPE) macro to allocate memory. This takes care of out of memory (by exiting the program).


If you compile with -D_ADIME_DEVELOPING (or make with ADIME_DEVELOPING=1), then you get access to the macros Q, S(a), I(b), R(c), P(d), that print the current source file line number, the string a, the integer b, the RGB c, and the pointer d, respectively.

If you compile with -D_ADIME_FORTIFY (or make with ADIME_FORTIFY=1), then the memory debugger Fortify will be used. This is good to do after any big changes in the library, to verify that it probably doesn't introduce any memory leaks or corrupts memory.

Stuff that Only the Chain Format Needs

You don't need to read this, it's really only for my own reference. `_adime_dialogf_create_objects()' can be called recursively from `_adime_create_MyFormat()'. If bit #2 (0x4) is set, then `_adime_create_MyFormat()' the `fo' pointer won't be updated.


This has nothing to do with writing your own format, but I didn't know where else to put it. This is roughly what you need to do if you want to port Adime to a new platform:

Of course, I'm happy to help with whatever questions you may have. And it's definitely sufficient if you get the build to work, I can easily do the rest myself. See readme.txt for contact information.


How to release a new version of Adime: