/*->h.Drawlevel0 */
/****************************************************************************
 * This source file was written by Acorn Computers Limited. It is part of   *
 * the "DrawFile" library for rendering RISCOS Draw files from applications *
 * in C. It may be used freely in the creation of programs for Archimedes.  *
 * It should be used with Acorn's C Compiler Release 2 or later.            *
 *                                                                          *
 * No support can be given to programmers using this code and, while we     *
 * believe that it is correct, no correspondence can be entered into        *
 * concerning behaviour or bugs.                                            *
 *                                                                          *
 * Upgrades of this code may or may not appear, and while every effort will *
 * be made to keep such upgrades upwards compatible, no guarantees can be   *
 * given.                                                                   *
 ***************************************************************************/

/* -> h.DrawLevel0
 *
 * DrawFile module, level 0 (diagram level interface)
 * History:
 * Version 0.0 : 21 Nov 88, DAHE: created
 *         0.0a: 23 Nov 88, DAHE: first working version
 *         0.0b: 24 Nov 88, DAHE: added shift and query
 *         0.0c: 28 Nov 88, DAHE: name changes, box conversion, rebind
 *         0.1 : 30 Nov 88, DAHE: release
 *         0.2 : 15 Feb 89, DAHE: unknown object handler added
 *
 * Needs: h.os
 *
 * This file defines the interface to the simplest version of the
 * DrawFile module. It can read in files to diagrams and render them.
 * There is no checking of whether we have overrun the end of the
 * diagram.
 *
 * To read in Draw files, it is expected that the caller will do the
 * work of the I/O themselves.
 *
 * To dispose of a diagram, the caller can just throw it away, ie,
 * the module does not keep any hidden information about what
 * diagrams it has seen.
 *
 * Note on returning errors: some calls return an offset to the bad data on an
 * error. This is not necessarily the start of an object: it may be bad data
 * part way through it. The offset is relative to the start of the diagram.
 *
 * The module cannot handle rectangle or ellipse objects. Use a path instead.
 */

/*-------------------------- Data types --------------------------*/

/* Type for a diagram: it consists of a pointer to the data and a
   length field. The length must be an exact number of words, and is
   the amount of space used in the diagram, not the size of the
   memory allocated to it.
*/
typedef struct
{ char * data;
  int  length;
} Draw_diag;

/* Abstract handle for an object */
typedef int Draw_object;

/* Rectangular box: bounding boxes, etc. */
typedef struct {int x0, y0, x1, y1;} Draw_box;

/* Redraw structure: similar to a wimp_redrawstr */
typedef struct
{
  int      reserved;
  Draw_box box;         /* Work area box (x0, y1 used) */
  int      scx, scy;    /* Scroll positions */
  Draw_box g;           /* Graphics window box */
} Draw_redrawstr;

/* Error type.
   Where a routine can produce an error, the actual value returned is a BOOL,
   which is TRUE if the routine succeeded. The error itself is returned in a
   block passed by the user; if NULL, then the details of the error are not
   passed back.
   The error block may contain either an operating system error, or an internal
   error. In the latter case, it consists of a code and possibly a pointer to
   the location in the file where the error occurred (if NULL, the location is
   not known or not specified). By convention, this should be reported by the
   caller in the form '<message> (location &xx in file)'. For a list of codes
   and standard errors, see h.DrawErrors. The location is relative to the
   start of the data block in the diagram.
*/

typedef struct
{
  enum { DrawOSError, DrawOwnError, None } type;
  union
  {
    os_error os;
    struct { int  code; int location; } draw;
  } err;
} Draw_error;

/*----------------------------- Macros ---------------------------*/

/* Macros for unit conversion between Draw units and screen units */
#define Draw_drawToScreen(i) ((i) >> 8)
#define Draw_screenToDraw(i) ((i) << 8)

/*--------------------- Function declarations --------------------*/

BOOL Draw_verify_diag(Draw_diag diag, Draw_error *error);
/* Checks a diagram after it has been read in from a file.
   Each object in the file is verified, and the first error returned.
*/

BOOL Draw_append_diag(Draw_diag *diag1, Draw_diag diag2, Draw_error *error);
/* Merge diag2 into diag1. Both diagrams must have been passed through
   Draw_verify_diag() first. The data block for diag1 must be at least
   diag1.length+diag2.length bytes long. The length field of diag1 is updated
   to the actual merged size which will be at most equal to this sum.
   The bounding box is diag1 is set to the union of the bounding boxes of the
   two diagrams.
   Note: after this call, the offsets of objects in diag1 may have changed,
   because the font table object can change in size. No other objects will
   Any errors which refer to a specific location are in diag2.
*/   

BOOL Draw_render_diag(Draw_diag diag, Draw_redrawstr *r, 
                            double xscale, double yscale, Draw_error *error);
/* Render a diagram (which must have been passed through Draw_verify_diag()
  first) at a factor 'scale' of its definition size. The object is rendered
  into the region of the screen indicated by the redrawstr.
  Note: the redrawstr is equivalent to a wimp_redrawstr, which may be cast to
  it. The window handle is NOT used. For applications not using the wimp, the
  structure should be set up as follows:
  the graphics box determines the region in screen units which will be drawn.
  Any objects wholly or partly in this region are rendered. Clipping must be
  set by the caller;
  the part of the diagram rendered is determined by mapping the top left of the
  diagram, in Draw coordinate space, onto a point
  (r->box.x0 - r->scx, r->box.y1 - r->scy), in screen coordinate space.

  After the call, the VDU5 character size is left at the default for the
  current mode.

  Note: very large, very small and negative scale factors will give a run-time
  error. This is not trapped by the routine. The lowest scale factor that is
  usually ok is about 0.00009. It is up to the caller to do range checks on the
  scale factor.
*/

typedef int  (*Draw_allocate)(void **anchor, int n);
typedef int  (*Draw_extend)(void **anchor, int n);
typedef void (*Draw_free)(void **anchor);

void Draw_registerMemoryFunctions(Draw_allocate alloc,
                                  Draw_extend   extend,
                                  Draw_free     free);
/* Register memory allocation/free function.
   This call is only needed if text area objects are to be rendered. It
   specifies three functions, used to allocate, extend and free blocks of
   memory. The memory is used only during the rendering of a text area, and is
   freed before the rendering code returns to the caller.

   If this routine is never called, or if memory allocation fails, then
   attempting to render a text area will produce no output.

   The specification required for the three routines is:
    int alloc (void **anchor, int n): allocate n bytes of store and set *anchor
    to point to them, ie the memory is at (*anchor)[0] to (*anchor)[n-1].
    Return 0 if the memory cannot be allocated, otherwise non-zero.

    int extend (void **anchor, int n): extend the block of memory which starts
    at *anchor to a total size of n bytes. Return 0 if the memory cannot be
    allocated, else non-zero. Note that n will always be positive. The new
    memory should be appended to the existing block (which may be moved by the
    operation).

    void free(void **anchor): free the block of memory which starts at *anchor,
    and set *anchor to 0.

   These specifications are the same as the flex routines flex_alloc,
   flex_extend and flex_free.
*/

void Draw_shift_diag(Draw_diag diag, int xMove, int yMove);
/* Shift a diagram. All coordinates in the diagram are moved by the given
   distance.
*/

void Draw_queryBox(Draw_diag diag, Draw_box *box, BOOL screenUnits);
/* Find the bounding box of the file. If the flag 'screenUnits' is TRUE, this
   is returned in screen units, otherwise in draw units.
*/

void Draw_convertBox(Draw_box *from, Draw_box *to, BOOL toScreen);
/* Convert a box to/from screen coordinates. from and to may point to the same
   box. If toScreen is TRUE, the box is taken as being in Draw units, else it
   is in screen units.
*/

void Draw_rebind_diag(Draw_diag diag);
/* Force the header in the bounding box to be exactly that of the union of the
   objects in it. The diagram must have first been verified.
*/

/*-------------------------- Unknown object handling -------------------------*/
/* New types of object can be added by registering an unknown object handler.
The handler is called whenever an attempt is made to render an object whose
tag is not one of the standard ones known to DrawFile. It is passed a pointer
to the object to be rendered (cast to a void *), and a pointer to a block
into which to write any error status. The object pointer may be cast to one
of the standard Draw types (defined in the level 1 interface), or to a
client-defined type. If an error occurs, the handler must return FALSE and
set up the error block; otherwise it must return TRUE. Unknown objects must
conform to the standard convention for object headers, i.e. 1-word object
tag; 1-word object size; 4-word bounding box. The unknown object handler is
only called if the object is visible, i.e. of there is an overlap between its
bounding box and the region of the diagram being rendered. The object size
field must be correct, otherwise catastrophes will probably result. */

/* Type for unknown object handler */
typedef BOOL (*Draw_unknown_object_handler)(void *object,
                                            Draw_error *error);

Draw_unknown_object_handler Draw_set_unknown_object_handler
                           (Draw_unknown_object_handler handler);

/* Sets the unknown object handler. To remove the unknown object handler,
call with NULL as parameter. The previous handler is returned. */
