<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> <html><head><title> Adime - Internal Stuff </title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> </head><body bgcolor=white text=black link="#0000ee" alink="#ff0000" vlink="#551a8b"> <pre> _ _ _ /_\ __| (_)_ __ ___ / _ \/ _` | | ' \/ -_) /_/ \_\__,_|_|_|_|_\___| Allegro Dialogs Made Easy Internal Stuff by Sven Sandberg </pre> <p> <h1><a name="Contents">Contents</a></h1> <p> <ul> <li><a href="#Introduction">Introduction</a> <li><a href="#What You Need to Write">What You Need to Write</a> <ul> <li><a href="#_adime_count_MyFormat">_adime_count_MyFormat</a> <li><a href="#_adime_create_MyFormat">_adime_create_MyFormat</a> <li><a href="#_adime_destroy_MyFormat">_adime_destroy_MyFormat</a> <li><a href="#_adime_reset_MyFormat">_adime_reset_MyFormat</a> <li><a href="#_adime_store_MyFormat">_adime_store_MyFormat</a> </ul> <li><a href="#Helper Functions">Helper Functions</a> <ul> <li><a href="#_adime_get_double_arg">_adime_get_double_arg</a> <li><a href="#_adime_get_int_arg">_adime_get_int_arg</a> <li><a href="#_adime_get_string_arg">_adime_get_string_arg</a> <li><a href="#_adime_get_strlist_arg">_adime_get_strlist_arg</a> <li><a href="#_adime_inwards_bevel">_adime_inwards_bevel</a> </ul> <li><a href="#Registering the Format">Registering the Format</a> <ul> <li><a href="#_adime_register_dialogf_format">_adime_register_dialogf_format</a> </ul> <li><a href="#Advanced Stuff">Advanced Stuff</a> <ul> <li><a href="#_adime_dialog">_adime_dialog</a> <li><a href="#_adime_dialogf_end">_adime_dialogf_end</a> <li><a href="#_adime_dialogf_reset_dialog">_adime_dialogf_reset_dialog</a> <li><a href="#_adime_dialogf_run">_adime_dialogf_run</a> <li><a href="#_adime_dialogf_start">_adime_dialogf_start</a> <li><a href="#_adime_dialogf_store_results">_adime_dialogf_store_results</a> </ul> </ul> <p><br> <h1><a name="Introduction">Introduction</a></h1> <p> 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. <p> 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. <p><br> <h1><a name="What You Need to Write">What You Need to Write</a></h1> <p> 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. <p> 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 <tt>`NULL'</tt> for it when you call `_adime_register_dialogf_format()'. <p><br> <hr><font size="+1"><b>int *<a name="_adime_count_MyFormat">_adime_count_MyFormat</a>(const char *desc, const char *modifiers, adime_va_list args, void **args_out);</b></font><br> 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. <tt>`modifiers'</tt> is the modifiers given by the person who called `adime_dialof()' inside the brackets after the format text. <tt>`desc'</tt> is the description text given by the user. This function is responsible for reading all its arguments from the <tt>`args'</tt> 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). <p><br> <hr><font size="+1"><b>int <a name="_adime_create_MyFormat">_adime_create_MyFormat</a>(DIALOG *dialog, const char *desc, const char *modifiers, void *args);</b></font><br> 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 <tt>`dialog[0]'</tt> (and possibly <tt>`dialog[1]'</tt>, <tt>`dialog[2]'</tt> 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 <tt>`handle_desc'</tt> 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). <tt>`desc'</tt> is the description text passed to `adime_dialogf()' which belongs to this format. <tt>`modifiers'</tt> is the modifier string between the brackets after the format. Both <tt>`modifiers'</tt> and <tt>`desc'</tt> 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. <tt>`args'</tt> 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. <blockquote><font size="-1" face="helvetica,verdana"><em><b>See also:</b></em> <a href="#Advanced Stuff">Advanced Stuff</a>.</font></blockquote> <hr><font size="+1"><b>void <a name="_adime_store_MyFormat">_adime_store_MyFormat</a>(DIALOG *dialog);</b></font><br> 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. <p><br> <hr><font size="+1"><b>void <a name="_adime_reset_MyFormat">_adime_reset_MyFormat</a>(DIALOG *dialog);</b></font><br> 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. <p><br> <hr><font size="+1"><b>void <a name="_adime_destroy_MyFormat">_adime_destroy_MyFormat</a>(DIALOG *dialog);</b></font><br> 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. <p><br> <h1><a name="Helper Functions">Helper Functions</a></h1> <p> There are some helper functions that you will probably find useful for parsing the modifier string. <p><br> <hr><font size="+1"><b>int <a name="_adime_get_int_arg">_adime_get_int_arg</a>(const char **arg_text, int *out);</b></font><br> <font size="+1"><b>int <a name="_adime_get_double_arg">_adime_get_double_arg</a>(const char **arg_text, double *out);</b></font><br> <font size="+1"><b>int <a name="_adime_get_string_arg">_adime_get_string_arg</a>(const char **arg_text, char **out_p);</b></font><br> <font size="+1"><b>int <a name="_adime_get_strlist_arg">_adime_get_strlist_arg</a>(const char **arg_text, int *out_num, ***out_list);</b></font><br> 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 <tt>`arg_text'</tt> 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. <p><br> <hr><font size="+1"><b>void <a name="_adime_inwards_bevel">_adime_inwards_bevel</a>(DIALOG *dialog, int ofs, int white_space);</b></font><br> Draws the kind of bevel that is around Adime's text boxes in the area given by <tt>`d'</tt>. If ofs is not 0, the bevel will grow by that number of pixels. If the <tt>`white_space'</tt> flag is set, an extra one pixel wide border of white will be drawn inside the bevel. <p><br> <h1><a name="Registering the Format">Registering the Format</a></h1> <p><br> <hr><font size="+1"><b>void <a name="_adime_register_dialogf_format">_adime_register_dialogf_format</a>( 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));</b></font><br> 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 <tt>`handle_desc'</tt> 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 <tt>`outside_func'</tt>, 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 <tt>`store_result'</tt> and <tt>`reset_dialog'</tt>. <tt>`destroy_data'</tt> can also be NULL, in case you don't allocate any custom data when creating the dialog. <p> 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. <blockquote><font size="-1" face="helvetica,verdana"><em><b>See also:</b></em> <a href="#What You Need to Write">What You Need to Write</a>.</font></blockquote> <h1><a name="Advanced Stuff">Advanced Stuff</a></h1> <p> There are some obscure features that you normally won't need to know about: <p> 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. <p><br> <hr><font size="+1"><b>_ADIME_DIALOGF_DIALOG *<a name="_adime_dialogf_start">_adime_dialogf_start</a>(const char *title, int x_pos, int y_pos, int edit_w, const char *format, adime_va_list_p args);</b></font><br> Sets up a `_ADIME_DIALOGF_DIALOG *', which can be used by `_adime_dialogf_run()' to open a dialog. This will traverse the <tt>`args'</tt> so that it points to after the last argument. A <tt>`adime_va_list_p'</tt> is like a pointer to a <tt>`va_list'</tt>, 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 <tt>`va_list'</tt>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 <tt>`va_list'</tt> to a <tt>`adime_va_list_p'</tt>. <blockquote><font size="-1" face="helvetica,verdana"><em><b>See also:</b></em> <a href="#Advanced Stuff">Advanced Stuff</a>, <a href="#_adime_dialogf_run">_adime_dialogf_run</a>, <a href="#_adime_dialogf_store_results">_adime_dialogf_store_results</a>, <a href="#_adime_dialogf_reset_dialog">_adime_dialogf_reset_dialog</a>, <a href="#_adime_dialogf_end">_adime_dialogf_end</a>.</font></blockquote> <hr><font size="+1"><b>int <a name="_adime_dialogf_run">_adime_dialogf_run</a>(DIALOGF_DIALOG *dd);</b></font><br> 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.* <blockquote><font size="-1" face="helvetica,verdana"><em><b>See also:</b></em> <a href="#Advanced Stuff">Advanced Stuff</a>, <a href="#_adime_dialogf_start">_adime_dialogf_start</a>, <a href="#_adime_dialogf_store_results">_adime_dialogf_store_results</a>, <a href="#_adime_dialogf_reset_dialog">_adime_dialogf_reset_dialog</a>, <a href="#_adime_dialogf_end">_adime_dialogf_end</a>.</font></blockquote> <hr><font size="+1"><b>void <a name="_adime_dialogf_store_results">_adime_dialogf_store_results</a>(DIALOGF_DIALOG *dd);</b></font><br> Stores the data that the user entered in the dialog in the appropriate output fields. <blockquote><font size="-1" face="helvetica,verdana"><em><b>See also:</b></em> <a href="#Advanced Stuff">Advanced Stuff</a>, <a href="#_adime_dialogf_start">_adime_dialogf_start</a>, <a href="#_adime_dialogf_run">_adime_dialogf_run</a>, <a href="#_adime_dialogf_reset_dialog">_adime_dialogf_reset_dialog</a>, <a href="#_adime_dialogf_end">_adime_dialogf_end</a>.</font></blockquote> <hr><font size="+1"><b>void <a name="_adime_dialogf_reset_dialog">_adime_dialogf_reset_dialog</a>(DIALOGF_DIALOG *dd);</b></font><br> Resets all the dialog objects to reflect the default values as given by the `adime_va_list_p args' arguments to `_adime_dialogf_start()'. <blockquote><font size="-1" face="helvetica,verdana"><em><b>See also:</b></em> <a href="#Advanced Stuff">Advanced Stuff</a>, <a href="#_adime_dialogf_start">_adime_dialogf_start</a>, <a href="#_adime_dialogf_run">_adime_dialogf_run</a>, <a href="#_adime_dialogf_store_results">_adime_dialogf_store_results</a>, <a href="#_adime_dialogf_end">_adime_dialogf_end</a>.</font></blockquote> <hr><font size="+1"><b>void <a name="_adime_dialogf_end">_adime_dialogf_end</a>(DIALOGF_DIALOG *dd);</b></font><br> Destroys all mallocated memory for the dialog. <blockquote><font size="-1" face="helvetica,verdana"><em><b>See also:</b></em> <a href="#Advanced Stuff">Advanced Stuff</a>, <a href="#_adime_dialogf_start">_adime_dialogf_start</a>, <a href="#_adime_dialogf_run">_adime_dialogf_run</a>, <a href="#_adime_dialogf_store_results">_adime_dialogf_store_results</a>, <a href="#_adime_dialogf_reset_dialog">_adime_dialogf_reset_dialog</a>.</font></blockquote> <hr><font size="+1"><b>DIALOG *<a name="_adime_dialog">_adime_dialog</a>;</b></font><br> Pointer to the first object in the current dialog, in case any function needs to access it. <p><br> <br><center><h2><a name="Calling `_adime_create_MyFormat()' Several Times">Calling `_adime_create_MyFormat()' Several Times</a></h2></center><p> 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. <p><br> <br><center><h2><a name="Exiting from the Dialog">Exiting from the Dialog</a></h2></center><p> If an object wishes to exit the dialog, then it should set the <tt>`return_value'</tt> field of the global <tt>`_adime_dialog'</tt> 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. <p><br> <br><center><h2><a name="Allocation">Allocation</a></h2></center><p> Use the _ADIME_MALLOC(pointer, size, TYPE) macro to allocate memory. This takes care of out of memory (by exiting the program). <p><br> <br><center><h2><a name="Debugging">Debugging</a></h2></center><p> 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. <p> 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. <p><br> <br><center><h2><a name="Stuff that Only the Chain Format Needs">Stuff that Only the Chain Format Needs</a></h2></center><p> 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 <tt>`fo'</tt> pointer won't be updated. <p><br> <br><center><h2><a name="Porting">Porting</a></h2></center><p> 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 <tt>`va_list'</tt> 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. <p><br> <br><center><h2><a name="Releasing">Releasing</a></h2></center><p> <p> 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> Verify that docs/src/changes._tx is ok. <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> </body> </html>