@#
@# This is the source for Adime Internal Stuff document, in Allegro's custom
@# format. Read allegro/docs/makedoc.c for a description of what is going
@# on...
@#
@# Read internal.txt, internal.info, internal.html or internal.rtf instead
@# of this file!
@#
@ignore_css
@#multiplefiles
@multiwordheaders
@#locale=latin1
@document_title=Adime - Internal Stuff
@html_footer=Back to Contents
@rtfh=Adime Internal Stuff
@# Note: this version number is patched by fixver.sh
@manh="version 2.2.0" "Adime" "Adime Internal Stuff"
@mans=#include <adime/adimeint.h>
@$\input texinfo
@$@setfilename internal.inf
@$@settitle Adime Internal Stuff
@$@setchapternewpage odd
@$@paragraphindent 0
@$
@$@ifinfo
@$@direntry
@$* Internal: (internal). Internal stuff from Adime.
@$@end direntry
@$This is the Info version of the Adime Internal Stuff document.
@$
@$By Sven Sandberg
@$@end ifinfo
@$
@$@node Top, , (dir), (dir)
@<pre>
@!indent

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

                           Allegro Dialogs Made Easy

                                Internal Stuff

                               by Sven Sandberg


@indent
@</pre>

@!text
@heading
Contents

@shortcontents



@text
@heading
Introduction

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.



@heading
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.<br>
   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);
@xref Advanced Stuff
   Function that you must implement if you want to write your own
   adime_dialogf() format.<br>
   This will be called when `adime_dialogf()' constructs the dialog and
   should set up the `DIALOG *' structures appropriately.<br>
   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.

@@void @_adime_store_MyFormat(DIALOG *dialog);
   Function that you may implement if you want to write your own
   adime_dialogf() format.<br>
   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.<br>
   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.<br>
   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.



@heading
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:<br>
      _ADIME_ARG_COMMA - The reading was interrupted by a comma in the string.<br>
      _ADIME_ARG_END - The reading was interrupted by end of string.<br>
      _ADIME_ARG_NONDIGIT - The reading was interrupted by something that was
         not a digit.<br>
      _ADIME_ARG_DIGIT - The reading was interrupted by a digit.<br>
      _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.



@heading
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));
@xref What You Need to Write
   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.



@heading
Advanced Stuff

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

Calling `adime_dialogf()' Recursively from `adime_dialogf()'<br>
   `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);
@xref Advanced Stuff, _adime_dialogf_run, _adime_dialogf_store_results
@xref _adime_dialogf_reset_dialog, _adime_dialogf_end
   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'.

@@int @_adime_dialogf_run(DIALOGF_DIALOG *dd);
@xref Advanced Stuff, _adime_dialogf_start, _adime_dialogf_store_results
@xref _adime_dialogf_reset_dialog, _adime_dialogf_end
   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.*

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

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

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

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


@hnode 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.


@hnode 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.


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


@hnode Debugging
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.


@hnode 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.


@hnode Porting
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:
<ul><li>
   Modify include/adime/adimecfg.h so that it defines the macros
   appropriately. See the comment in the top of that file for more
   information, and ask me if there is anything more you want to know.
<li>
   Create appropriate subdirectories of lib/ and obj/.
<li>
   Create a suitable makefile. The easiest way is to copy one of the existing
   makefiles, from the platform you think is most similar to the new one.
<li>
   If the platform needs to create a windows dll, modify misc/fixdll.sh
   accordingly.
<li>
   Think of anything else your platform might need.
<li>
   Check how your platform handles floating point errors (e.g., by
   calculating 0.0/0.0 and 1.0/0.0). You may need to modify src/nan.c to
   handle this. (This is needed by the calculator.)
<li>
   This ought not to be any problem, but check what the definition of
   `va_list' is (it should be defined in stdarg.h in the system's include
   directory). Adime will get problems if it would be defined to be const,
   but this would be very silly and I don't know any platform that does it.
<li>
   Modify the fix.sh and possibly fix.bat scripts accordingly (note
   that fix.sh must handle all platforms, even if users on that
   platform would use another script).
<li>
   Modify misc/zipup.sh accordingly.
<li>
   Modify docs/readme._tx to list the new platform, docs/changes._tx to list
   the changes, and docs/thanks._tx to list yourself.
<li>
   Write installation instructions in docs/build/[platform].txt.
</ul>
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.


@hnode Releasing

How to release a new version of Adime:
<ul><li>
   Update misc/makedoc/* from the latest stable version of Allegro.
<li>
   If new files were added since last release, update zipup.sh to
   include them.
<li>
   Compile the chm version of the documentation under windows.
<li>
   Go to Linux.
<li>
   Run "sh misc/fixver.sh [version]". (Updates the version number.)
   The version number has the form major.minor.patch, where patch is
   increased for releases that fix bugs, minor is increased for
   releases that add features, and major is increased for releases
   that change binary compatibility.  The minor is always even for
   releases, and always odd in CVS; thus, the second parameter to
   fixver.sh should be even. Do this on the same day as you make the
   release, since it modifies ADIME_DATE in adime.h.
<li>
   Run "sh misc/zipup.sh". (Creates the archive.)
<li>
   Make sure all files are in the archive (manually and by testing on
   all platforms). Make sure no junk files (e.g., *~) are in the
   archive.
<li>
   Run "sh misc/fixver.sh [CVS-version]".
   Update the version of CVS by adding one to the "minor" part of the
   version number, as explained above.
<li>
   Commit to CVS (fixver.sh changed version numbers in a few files,
   and misc/dllsyms.lst may have changed).
<li>
   Tag CVS with version name, on the form vX-Y-Z, where X.Y.Z is the
   version number.
<li>
   Update version number in adime/index.html, both in the text and in
   the zip filename. Update file sizes in index.html. Add links to the
   build instructions for any new ports.
<li>
   Update the screenshot if that changed.
<li>
   Copy html documentation to
   shell.sf.net:/home/groups/a/ad/adime/htdocs/
<li>
   Make a new sourceforge release: go to
   https://sourceforge.net/projects/adime/ while logged in, click
   "Admin", and click "File Releases".  See the documentation
   nearby: the package is called adime and the release name should be
   the version number (something like "2.2.0").
<li>
   Notify [AL] and create a news item at allegro.cc.  Include the
   version number and the homepage.  The first three paragraphs of
   readme.txt can be used as a description.  Also include the latest
   changes (if there are many changes, include only a summary on
   allegro.cc).
</ul>

@!text
@!html
@!rtf
@!man
