Application programmers' guide to the graph library

Who should read this document

This document is for application programmers who wish to use Version 1 of the graph package to implement GUI's, it covers the public interface to the package, the internals of the graph package are not discussed in detail.

Although variants of the Version 1 package do exist for the Macintosh and for Windows, areas specific to these operating systems (e.g. how to build an application) are not covered in this document. This document covers the graph package in general and how to build applications in the unix environment only.

The document covers Version 1 of the graph package which will be frozen from ACEDB release 4 onwards. A new version of the graph package, Version 2, will be developed from ACEDB release 5, which will make use of the wxWindows package to implement better windows support for Unix, MS windows and the Macintosh. This package is C++ based and will require applications to compile their main functions with a C++ compiler and then use the C++ compiler to link the final application to ensure that the C++ environment is correctly set up.

Introduction

The graph package provides a device independent ANSI-C library of subroutines for access to graphical user interfaces. It was developed by Richard Durbin and Jean Thierry-Mieg for the ACEDB genomic database program. Originally the graph program was intimately linked to ACEDB but in a recent development the ACEDB specific code has been moved from the graph package so that it has become a stand alone library (it is currently used like this in the Image gel slab analysis package). Applications needing to use both ACEDB and the graph package can use the ACEDB-graph interface package to initialise the graph/acedb interface.

Currently there are three main versions of the graph package:

Only the unix version is dicussed in detail here.

There is an example program graphtest.c which shows how to use the graph package.

Using the Graph library

Initialisation & termination

The graph library follows the initialisation protocol of X Windows and other graphical libraries in that the graph library must be initialised before use. This is done with a single call to the function graphInit(), note that further calls to graphInit will produce undefined results, i.e. don't do it. graphInit() sets up the connection to the display and this must only be done once. You should also note that calls to graph functions will produce undefined results if graphInit() has not been called. Currently it is not safe to have multiple sequences of graphInit(), graphFinish():
     {
     some code

     graphInit()

     some code

     graphFinish()

     some code

     graphInit()

     some code

     graphFinish()
     }
This will fail, the graph package is not stateless and cannot be shut down and then reinitialised in the same application.

The graph package should be closed down by calling graphFinish(), this is particularly important if the application makes use of other facilities such as messubs to issue messages and needs to send messages to stdout after the connection to the display has been closed. graphFinish() will tidy up its links with these other packages so that they will continue to function as they did before graph initialisation.

Basic concepts - handles and drawing

The basic concept is that each window has associated with it a Graph object. The window is made with graphCreate(), which returns the Graph handle. Items such as lines and text are drawn onto a drawing stack attached to the handle, which is then used to draw those parts of the graph that are currently visible. (Of course this changes when, e.g. an obscuring window is removed.) The stack can also be used to provide postscript or gif output. Note that because the drawing commands go to the stack, a call to graphRedraw() is necessary at the end of drawing to display material on the screen, the normal sequence will be something like:


	graphBox(params) ;
	graphCircle(params) ;
	graphMenu(params) ;

	graphRedraw(params) ;

Basic concepts - the active graph

At any one time there is one active graph. Almost all the functions in the package act on the active graph. The active graph can be changed explicitly using graphActivate(), but most changes are implicitly determined by user actions: when events occur, such as mouse clicks, keyboard typing, or menu selections, the window in which they took place is made active and a call is made to a callback function. Callback functions are registered with the graph package via graphRegister() when the graph is created.

Basic concepts - user coordinates

User coordinates: All drawing and communication with respect to position is in a user coordinate system, normally with units corresponding to single characters (TEXT_FIT and TEXT_SCROLL graph types - see graphCreate()).

Basic concepts - text boxes

The graph package provides a number of styles of text editting boxes which allow the application to get information in the form of strings from the user, this includes the usual cut/paste of text. There are two very important points to remember about using these text boxes:
  1. The application must allocate and hold on to the text buffer used by any graph text box until that graph text box is destroyed.
  2. The length supplied in the call to create the graph text box is the length (in bytes) of the whole buffer, this means that the user will be allowed to enter a maximum of (length - 1) characters into the text box, one byte must be reserved for the terminating null character of the string.

Basic concepts - boxes

A fundamental concept during drawing is that one or more drawing items can be bundled together into a box. This is done by surrounding the drawing calls with graphBoxStart() and graphBoxEnd() calls. Boxes can be nested. Each box has a unique identifier within its graph. These are consecutive integers, starting at 0: the whole drawing space is box 0. Each box keeps track of its limits, which are defined by the maximum extent of its contents. There are two main uses of boxes: (1) they can be independently redrawn with new background and foreground colours using graphBoxDraw(), (2) a PICK pseudoevent can be registered via graphRegister(), which causes the graph package to find which box a LEFT_MOUSE_DOWN event has occurred in, and pass this as a parameter to the registered. It is these two properties that allow selected items to be highlighted, and followed in a hypertext like fashion.

Basic concepts - interrupting long operations

The graph package provides two different interrupt handlers allowing the user to interrupt long operations:
Signal based
This interrupt uses the ANSI standard SIGINT signal to stop the program regardless of what it is doing. The signal is initiated by the user pressing the 'interrupt' key on their keyboard (BREAK or CTRL-C). The graph package will then give the user the chance to abort or continue where they left off.
Key based
This interrupt is based on the graph package detecting that the user has pressed the F4 key, this is done by monitoring the keys pressed and hence will not interrupt a program that is looping or busy processing some data. The precise action taken by the graph package will depend on which part of the application is being executed at the time.

Basic concepts - the routines

There are some 60 routines, which are listed alphabetically with notes at the end, but can be divided into several classes:
Initialisation/Cleanup:
  graphInit(), graphFinish(),             - initialise/terminate graph package
  graphCleanUp()		          - kills all but one graph
  

Event loop control:
  graphLoop()			          - start loop
  graphLoopReturn()		          - end loop
  graphProcessEvents()		          - process all pending X events

Management of individual graphs:
  graphCreate(), graphDestroy()           - creation/destruction
  graphActive(), graphActivate(), graphExists()
                                          - set/test the active graph
  graphRetitle()                          - change title of a graph
  graphPop()			          - deiconify and raise
  graphEvent()			          - create pseudo-event to go in loop
  graphRegister()		          - set up callback functions
  graphMenu(), graphFreeMenu()	          - add a menu (graphMenu preferred)

Association of extra properties with the graph:
  graphAssociate(), graphAssFind(), graphAssRemove()

Coordinate system management:
  graphTextBounds(), graphFitBounds(), graphPlainBounds()
  graphTextInfo(), graphScreenSize()
  graphGoto()

Global drawing management:
  graphClear()
  graphRedraw()			          - draw stack on screen
  graphPS(), graphPrint()	          - postscript

Basic drawing commands (go on stack):
  graphLine(), graphRectangle(), graphFillRectangle()
  graphCircle(), graphText(), graphTextPtr()
  graphPoint()			          - small square

Drawing status control (go on stack):
  graphLinewidth(), graphPointsize(), 
  graphTextHeight(), graphFontFormat(),	  - FontFormat is new
  graphColor()

Box control:
  graphBoxStart(), graphBoxEnd()
  graphBoxDraw()
  graphBoxClear()
  graphBoxDim()			          - to get back box location/size
  graphBoxDrag(), graphBoxShift()
  graphBoxSetPick()		          - toggle pickability

Complex prepackaged boxes:
  graphTextEntry(), graphTextScrollEntry,
  graphCompletionEntry(), graphCompletionScrollEntry(), 
  graphEntryDisable()
  graphButton(), graphButtons(), graphColouredButton(), graphColouredButtons()

XOR functions (not on stack):
  graphXorLine(), graphXorBox()

Message windows:
  graphMessage(), graphUnMessage()        - non-blocking message, use graphUnMessage
                                            to remove it
  graphOut()			          - blocking message with OK button
  graphError()                            - blocking message with OK button
  graphEnd()                              - blocking message with EXIT button
  graphQuery()			          - blocking yes/no query
  graphPrompt()			          - blocking request for data
  graphSelect()			          - blocking forced choice

Basic concepts - the source files

Broadly speaking the various graph source files contain the following code:
  graph.h                                 public header file
  graphcon.c, graphsub.c                  display-method independent code
  graphps.c graphprint.c                  postscript creation/print code
  graphxt.c, graphxlib.c, xtsubs.c        X-dependent code
  gifsubs.c, graphgd.c                    gif code
  filquery.c                              file query box implementation
  graphhelp.c, graphxwww.c, xnetscape_remote.c
                                          help display, browser control code
  chrono.c                                source code profiling code
  viewedit.c                              control display of various types of graph
  graphselect.c                           provides lists of items to be selected
  graphAcedbInterface.c                   acedb/graph interface code
  colcontrol.c                            controls layout of columns in fmap etc.
  graphramp.c                             control intensity of a greyscale image.

Building an application using the Graph library

Although the graph package can be built independently of ACEDB, it does require some of the packages also used by ACECB:

The .h files are required for compilation of the application and the module arraysubs.o and the library libfree.a are required for the final linking of the application.

Graph applications can either be built that write to an X window or to a gif file, it is not possible to do both from the same application (writing to a postscript file or printed is possible from either type of application). Building either application requires the basic graph library (libgraph.a), and this is combined with other object modules to give either the X windows or gif flavour of the final library. The makefile (truemake) distributed with acedb gives examples of how to build an X windows application (e.g. xace) or a gif application (e.g. giface).

The Graph Interface

Graph graphActive (void)

Returns the handle for the currently active graph. This is normally used in a subsequent graphActivate() call.

BOOL graphActivate (Graph g)

If graphExists(g) then it makes g the active graph and returns TRUE, else returns FALSE.

BOOL graphActivateChild (void)

Attempts to make the child of the currently active graph the new active graph, returns TRUE if successful, FALSE otherwise.

BOOL graphArc (float xcen, float yxen, float rad, float ang, float angDiff)

Draws an arc of the specified length, note degrees are anticlockwise from 3 o'clock.

BOOL graphAssociate (void* xin, void* xout)

Uses the associator package (see the arraysubs utilities) to associate xin to xout for the active graph. It will return FALSE and do nothing if there is already an entry for xin (use graphAssRemove() first), otherwise it makes the association and returns TRUE.

This function can be used to attach an arbitrary number of new attributes to a graph, by giving each an identifier that is used as xin.

Implemented via a macro so that the compiler is not so fussy about the void* argument typing (well what can you say...sigh).

BOOL graphAssFind (void* xin, void* *pxout)

If xin has been associated with the active graph using graphAssociate(), then it sets *pxout and returns TRUE, else returns FALSE.

BOOL graphAssRemove (void *in)

If xin has been associated with the active graph using graphAssociate(), then it removes the entry and returns TRUE, else returns FALSE.

void graphBoundsPrint (float uw, float uh, void (*draw)(void))

Allow caller to set special page sizes for multipage prints.

void graphBoxAt (float x, float y, float *rx, float *ry)

Returns id of box at given graph coordinates.

void graphBoxClear (int k)

This is a toggle function. The first time it is called the box will be hidden, and the corresponding area of screen redrawn (possibly revealing material previously underneath). The next time the box will be restored and so on. Crashes if k is not a valid box id.

void graphBoxDim (int k,float *x1, float *y1, float *x2, float *y2)

This useful function returns in *x1, *x2, *y1, *y2 the bounds of box k in user coordinates. Crashes if k is not a valid box id.

void graphBoxDrag (int k,void (*client)(float*,float*,BOOL))

Call this after a LEFT_DOWN or MIDDLE_DOWN button event that you want to use to allow the user to drag a box. The LEFT and MIDDLE _DRAG and _UP graphEvents are hijacked by this call (and restored at the end of the process). The box's outline will appear in XOR pixels until the drag process finishes (button _UP). An initial call is made to the client routine, with third arg FALSE, giving the current position of the top left corner of the box in user coordinates. For each _DRAG event a further call is made to the client routine, again with third argument FALSE. The client can change the values of *x, *y. e.g. keeping *y fixed forces dragging on a horizontal line. When the up button is reached the client will be called once more with the third argument set TRUE, to allow final processing to be performed.

NB If the user wants to move the contents of the box they must call graphBoxShift() explicitly during the final client invocation.

Crashes if k is not a valid box id.

Example:


static float cursorX ;

void cursorDrag (float *x, float *y, BOOL isUp)
{
  *y = 2.0 ;			/* horizontal dragging only */
  if (isUp)
    { cursorX = *x ;
      graphBoxShift (CURSOR_BOX, *x, *y) ;
    }
}

void myPick (int k)
{
  switch (k)
    { ...
    case CURSOR_BOX:
      graphBoxDrag (k, cursorDrag) ;
      break ;
    ...
    }
}

void graphBoxDraw (int k, int fcol, int bcol)

A very fundamental routine. Box k is redrawn with the specified foreground and background colours. If fcol or bcol is negative the current value is used. This redraw is immediate (i.e. not placed on the event stack). It is used e.g. to highlight a box that has been picked. The colours fcol and bcol should be chosen from the list given in the graphColor() entry, with the addition that the pseudocolour TRANSPARENT can be used for the background. If the background is not TRANSPARENT it is painted solid bcol before drawing starts on the box. The foreground drawing colour can be reset within the box via graphColor(). Crashes if k is not a valid box id.

void graphBoxEnd (void)

Call during drawing to end the current box. Writes on the stack.

void graphBoxMarkAsClear (int box)

Marks a box for redrawing, actual redrawing is done when the current drawing commands are flushed.

void graphBoxShift (int k, float xbase, float ybase)

This translates the specified box so that its top left corner will go to (xbase, ybase) in user coordinates. Anything that was under the original box location will be redrawn. Subboxes will be moved with their parents.

void graphBoxSetPick (int k, BOOL pick)

If pick is FALSE this makes the box unpickable. i.e. a left mouse button down event inside it will not generate a call to the function registered with graphRegister(PICK,...). Calling with pick TRUE restores pickability. By default boxes are pickable.

Rather specialised, huh?

int graphBoxStart (void)

Call this during drawing to start a new box. All drawing commands between this call and the matching graphBoxEnd() will be assigned to the box. The return value is the id of the box (an integer starting at 1 - 0 is the background box). See the general introduction to boxes above. Writes on the stack.

int graphButton (char* text, void (*f)(void),float x, float y)

This creates a button containing the specified text with top left at location (x,y) in user coordinates. The button is a box (of course!). If it is picked the colour is inverted and your function f() is called. On return the colour is inverted back unless the window has been redrawn (there are sometimes minor bugs here, so that the button stays the wrong colour, but this is purely aesthetic - the function stays the same.)

To display several buttons in a row use graphButtons().

void graphButtons (MENUOPT *buttons,float x0, float y0, float xmax)

Uses graphButton to display several buttons in a row, starting at (x0, y0). The line wraps if it extends beyond xmax.

The argument buttons is a null-terminated string of MENUOPT, which are (function, text) pairs (see graphMenu() for a lengthier definition of MENUOPT).

void graphCircle (float xcen, float ycen, float rad)

Draws a hollow circle in the current foreground colour. On the screen the perimeter has width 1 pixel, but in postscript it has width the current line width.

Writes on the stack.

void graphCleanUp (void)

Calls graphDestroy() for all graphs other than the current one. i.e. the "Clean Up" function from the ACEDB main menu.

void graphClear (void)

Empties the drawing stack and the box list for the active graph, clears the window on the screen, resets the drawing color to BLACK and the font to the standard font.

STORE_HANDLE graphClearHandle (void)

Returns the 'handle' by which the currently active graph is held.

int graphColor (int color)

This changes the current foreground drawing colour, with a scope of the current box. It is placed on the stack. The preferred way to control colour within boxes is using graphBoxDraw(), since anything following the graphColor() call will always be the specified colour, i.e. will not be changeable dynamically via graphBoxDraw(). graphColor() is normally used to draw features of different colours in the background that are non-pickable.

Allowed colours are from:


enum Colour    {WHITE, BLACK, LIGHTGRAY, DARKGRAY,
                RED, GREEN, BLUE,
                YELLOW, CYAN, MAGENTA,
		LIGHTRED, LIGHTGREEN, LIGHTBLUE,
		DARKRED, DARKGREEN, DARKBLUE,
               }

int graphColouredButton(char* text, void (*f)(void *arg), void *arg, int fg, int bg, float x, float y)

Like graphButton except that the foreground and background of the button are specified (by fg and bg) and the callback function receives an argument (specified by arg, this is a void *, so you can put a pointer to anything here, except a pointer to func.)

void graphColouredButtons(COLOUROPT *buttons, float x0, float y0, float xmax)

Like graphButtons but uses the COLOUROPT structure to specify button colours and callback arguments.

typedef struct colouropt
{ void (*f)(void *arg);
  void *arg;
  int fg;
  int bg;
  char *text;
  struct colouropt *next;
} COLOUROPT;
The arrangement and use of a COLOUROPT structure is somewhat idionsyncratic; this is because it is intended to represent both statically defined and dynamically built sets of buttons. The following rules interpret COLOUROPTS: 1) To get to the next button; follow the next pointer, if the next pointer is zero, go to the next element in an array of COLOUROPTS. 2) The last button is marked by having its next pointer equal to either the address originally passed to graphColouredButtons, or to the address of the last button's record, or having a NULL text field. 3) Passing a NULL pointer to graphColouredButtons is legal, and denotes no buttons. This stucture is can be used with static arrays thus :-

static COLOUROPT buttons[] = {
  printf, "red", BLACK, LIGHTRED,  "red button", 0,
  printf, "blue", BLACK, LIGHTBLUE, "blue button", 0,
  printf, "green", BLACK, GREEN, "green button", 0,
  printf, "yellow", BLACK, YELLOW, "yellow button", 0,
  printf, "cyan", BLACK, CYAN, "cyan button", 0,
  printf, "magenta", BLACK, MAGENTA, "magenta button", 0,
  printf, "gray", BLACK, LIGHTGRAY, "gray button", buttons
};
and to build linked lists thus :-

COLOUROPT *buttons = 0;
COLOUROPT **p = &buttons;

while (newbutton) {
  *p = malloc(sizeof(struct colouropt));
  *p->next = *p;

  *p->text = .......

  p = &(*p->next);

}

int graphCompletionEntry (void (*gcf)(char*, int), char* cp, int len, float x, float y, void (*fn)(char*))

This creates a text entry box, using graphTextEntry(), with the addition that the TAB key will generate a call to the function gcf with arguments (cp, len). gcf should be used as a completion function - i.e. it will extend the text buffer cp if there is a unique extension in this context. We use it for completing names within a class -- see ksetClassComplete() and the examples in griddisp.c.

int graphCompScrollEntry (GraphCompletionFunc f, char* text, int len, float x, float y, void (*fn)())

Similar to graphCompletionEntry() with changes in accordance to graphTextScrollEntry().

See graphTextEntry(), graphCompletionEntry(), graphTextScrollEntry()

BOOL graphContainedIn (Graph parent)

Returns TRUE if the the currently active graph is a subgraph of the supplied graph.

Graph graphCreate (int type, char *name, float x, float y, float w, float h)

Creates a new window, which becomes the active window, and returns a handle for it.

name is used on the title bar.

x,y,w,h are the location on the screen in screen units. 1.0 = 900 pixels (typical height). Some window managers ignore x,y (e.g. twm). w and/or h may be negative, in which case the absolute value is taken to be the width or height in co-ordinate units, ie. size of default font for TEXT_* windows, and pixels for PIXEL_* windows. Note that this causes the pixel size of TEXT_* windows to change with changing font size.

type is from


  enum GraphType  { PLAIN, TEXT_SCROLL, TEXT_FIT, MAP_SCROLL, 
		    PIXEL_SCROLL, TEXT_FULL_SCROLL, PIXEL_FIT } ;
You are advised to use either TEXT_SCROLL or TEXT_FIT. Both these set the user coordinates to standard font character size. i.e. you give locations on the graph in character coords from top left = (0,0). graphTextInfo() will tell you the pixel dimensions of a character (and hence the aspect ratio).

TEXT_SCROLL gives a scroll bar, and lets you determine the height of the drawing area with graphTextBounds().

A TEXT_FIT graph normally uses graphRegister(RESIZE,...) to register a RESIZE callback function, which then will call graphFitBounds() to find the current dimensions.

PLAIN lets you set the user bounds to anything you want using graphPlainBounds(). By default they are [0,1]x[0,1]. The user bounds fill the graph, however large on the screen. Resizing events are handled automatically, e.g. resizing the graph bigger will magnify its contents. It is essentially impossible to handle text well in a PLAIN graph, so they are limited to simple line drawings.

MAP_SCROLL was for a horizontally scrolling PLAIN type graph, but is now not used anywhere and should be treated as unsupported.

PIXEL_SCROLL is the type used for displaying background images using graphPixels() or graphPixelsRaw(). Using graphPixelBounds() you can set the drawing region which will activate scrollbars if this is bigger than the windowsize. graphWhere will tell you the coordinates of the currently visible section of the drawing field. You can't register a resize call with this graphType (use PIXEL_FIT) .

PIXEL_FIT is a graphType without scrollbars. It can be used for displaying images within fixed window dimensions. A resize register is possible for this type.

void graphDestroy (void)

Destroys the current active graph. If a DESTROY callback has been registered using graphRegister() that will be called from graphDestroy() while the graph is still active.

void graphDisableBlock (void)

Returns currently active graph to blockable state.

void graphEnableBlock (void)

Prevents currently active graph from being blocked by e.g. a blocking message.

void graphEnd (char *text)

Displays blocking message with button labelled "END", can be used for terminating an application gracefully (the application should call this routine from its own termination routine.

void graphEntryDisable (void)

Deactivates all text entry boxes. See graphTextEntry().

void graphError (char *text)

Displays blocking message with button labelled "Continue", can be used for reporting non-fatal errors.

void graphEvent (int action, float x, float y)

This creates a false event that is placed on the event stack. The argument action may either be a GraphEvent value (see graphRegister()) or a printable character. In the latter case the event will be passed to the KEYBOARD callback routine, if one is registered via graphRegister(). (x, y) give the location in user coordinates of the pseudo-event.

Important: the event will not be processed immediately (i.e. before graphEvent() returns). It will be processed after control returns to the current event-handling loop, possibly after other itervening events.

NB graphEvent() may not work for all events for all implementations (the X version currently allows only mouse button up and down events and characters).

BOOL graphExists (Graph g)

Returns TRUE if g is a handle for an alive graph FALSE if g is not a handle, or has been destroyed.

float graphFakeBounds (float ny)

Sets the y size of a graph to be longer (perhaps for printing) and returns the old y size so application can reset to old size later.

void graphFillArc (float xcen, float yxen, float rad, float ang, float angDiff)

Draw a filled arc, note that angles are degrees anticlockwise from 3 o'clock.

void graphFillRectangle (float x0, float y0,

Draws a filled rectangle with opposite corners at (x0,y0), (x1,y1) in user coordinates, in the current foreground colour.

Writes on the stack.

void graphFinish (void)

Call at the end of the application to clean up the graphics properly.

void graphFitBounds (int *nx, int *ny)

For TEXT_FIT graph types only (see graphCreate()). Sets *nx, *ny, reporting on how many whole characters (= user units) fit in the active graph. Used in the drawing routine, particularly after RESIZE events.

void graphFreeMenu (void (*f)(KEY), FREEOPT *options)

Sets up the menu with a list of FREEOPT options. When one is picked f() is called with the corresponding KEY value as an argument. The FREEOPT structure is given in the notes for graphSelect().

NB graphMenu() is the preferred way to establish a menu.

void graphGoto (float x, float y)

Tries to centre (x, y) in user coordinates in the middle of the displayed window. Only relevant for TEXT_SCROLL.

STORE_HANDLE graphHandle (void)

Returns the handle by which the currently active graph is held.

char* graphHelp (char* item)

This call registers the active graph with the help package, linking it to the help page specified by item. Subsequently a call to help() (e.g. from the menu) with this graph active will display the correct help page. The return value is the previous string registered with the help package, if any.

Note that in ACEDB the display manager allows the help page to be specified in wspec/displays.wrm.

void graphInit (int *argcptr, char **argv)

Call this in main() to initialise the graph package.

The arguments should be (&argc,argv), where (int argc, char **argv) were the arguments to main. graphInit() passes them on to the device dependent graphics initialiser. The X initialiser will act on, and strip out, the standard X arguments, changing *argcptr (= argc) and *argv (the argument list). Other graphics initialisers may not change argc, argv.

int graphLastBox (void)

Returns the last box in the active graph.

void graphLine (float x1, float y1, float x2, float y2)

Draws a line from (x1,y1) to (x2,y2) in user coordinates in the current foreground colour with the current line width.

Writes on the stack.

float graphLinewidth (float x)

Sets the width in user coordinates for graphLine(). Returns the old value. If x < 0 then just returns the old value. This may only work partially on graphics systems, but will be exact for postscript output. For postscript it also affects rectangle and circle line width.

float graphLineSegs (Array pts)

Draws a segmented line in the active graph.

int graphLoop (BOOL isBlock)

This starts a new event loop, processing all events until either the graph that was active when it was called is destroyed, or graphLoopReturn() is called during some event on that graph. In the former case graphLoop() returns 0. In the latter, after that event finishes graphLoop() returns with the value given as the argument to graphLoopReturn().

If isBlock is TRUE then PICK, MENU and KEYBOARD events are only handled for the graph that graphLoop() was called on - used e.g. in the file chooser.

graphLoop() is now reentrant, so you can call it at any time. Recursive non-blocking calls could get complex.

BOOL graphLoopReturn (int retval)

see graphLoop(). Returns FALSE if it can't find an unterminated loop initiated using graphLoop() on the current graph.

void graphMapBounds (float ux, float uy, float uw, float uh, float aspect)

Set the size of the current graph.

void graphMenu (MENUOPT *menu)

Sets up the menu. menu must be a null terminated array of MENUOPT structures:

typedef struct menuopt
  { void (*f)(void);
    char *text;
  } MENUOPT ;
When a particular text item is selected on the menu the corresponding function is called. There is a global empty function, menuSpacer() that can be used with a blank title "" to provide a blank line in the menu to separate sets of commands.

void graphMessage (char *text)

Puts up a little message window containing the message and a "Remove" button. The message window will be destroyed if (i) the parent dies, (ii) the button is pressed, (iii) graphUnMessage() is called. If graphRegister(MESSAGE_DESTROY,...) has been issued for the parent then the registered routine is called when the message window dies. Only one message at a time can be displayed for any graph -- additional calls are ignored.

void graphOut (char *text)

graphOut() pops up a little window containing the text and a "Continue" button that must be pressed to continue. i.e. it is blocking.

Graph graphParent (void)

Returns the graph id of the parent of the currently active graph.

void graphPlainBounds (float ux, float uy, float uw, float uh, float aspect)

For PLAIN graph types only (see graphCreate()). Sets the extent of the drawing area in user coordinates. This will automatically fill the available window space, resizing as the window is resized. The aspect ratio of the drawing is preserved, so if the window is made too narrow the drawing does not extend to the bottom, and if too wide then it does not extend to the right hand side. The effect of the aspect argument makes

      graphPlainBounds (0,0,1,2,0.5)
define a square region on the screen.

void graphPixels (char *pixels,

int w, int h, int lineWidth, float x0, float y0, float x1, float y1)

Draw a pixel image expanded or shrunk in a box.

Can be used with PIXEL_SCROLL and PIXEL_FIT graphs.

pixels - is a data array which can be messfree'd after the call.

linewidth - is the length of a line in the array, the array has the form p(x,y) = pixels[y*linewidth+x].

w, h - width and height of the raw pixel image.

x0, y0, x1, y1 - topleft and bottomright corner to positions of the box that the image will be fitted in to.

void graphPixelBounds (int nx, int ny)

Set the graph size in pixel coordinates.

void graphPixelsRaw (char *pixels, int w, int h, int lineWidth, float x0, float y0)

Can be used with PIXEL_SCROLL and PIXEL_FIT graphs.

pixels - is a data array which must be created with MALLOC and must NOT be free'd by the user. DON'T use messalloc !!! since it is freed by free() in XDestroyImage when graphDestroy is called.

linewidth - is the length of a line in the pixel array, the array has the form p(x,y) = pixels[y*linewidth+x] linewidth has to be a multiple of 4 !!!

w, h - width and height of image to display

x0, y0 - topleft corner to position image on the drawing field.

void graphPoint (float x, float y)

Draws a small square at the point (x,y) in the current foreground colour. The width of the square in user units can be set by graphPointsize().

Writes on the stack.

float graphPointsize (float x)

Sets the size for graphPoint(). Returns the old value. If x < 0 then just returns the old value.

void graphPolygon (Array pts)

Draw a filled polygon.

void graphPrint (void)

Merely calls graphPS (graphHelp(0)). graphHelp(0) returns a name that was registered with the help package by the graph. The purpose of this is to allow graphPrint() to be placed directly in the menu for the graph (using graphMenu()), rather than having to write a tiny intermediate routine.

void graphProcessEvents (void)

Will process all pending events in the window system event queue, then return back to the user. This does not wait. For a waiting event loop use graphLoop().

BOOL graphPrompt (char *prompt, char *dfault, char *fmt)

graphPrompt() puts up a blocking window with the text followed by a textEntry box that allows the user to enter the reply, and two buttons: OK and Cancel. The initial text in the textEntry box is given by dfault. If Cancel is pressed it returns FALSE. If the OK box is pressed, or the Return key, then freecheck() is called on the current text buffer to see if it satisfies the format fmt. If it does the buffer becomes the current buffer for freesubs, which allows reading of values using freeint(), freefloat(), freeword() etc, and graphPrompt() returns TRUE. If there is no match message and the

The format is a string containing the following chars:

  i	integer
  f	float
  d	double
  w	single word (if it contains spaces or commas, must be in "")
  t	text - all to the end of the line taken in next freeword()
If the format ends with 'z' then the match must be exact.

e.g. format "iiz" is matched by "23 -19", but not "13" or "13 16 ?", the latter of which does match "ii" and "iiwz".

void graphPS (char *basename)

Writes out the active graph as a PostScript file and prints it.

Uses basename to construct a file name by adding an integer (unique per process) and ".PS". It tries to open this in the subdirectory PS of the home directory, where the home directory is the result of filsetdir(0) (set to $ACEDB in main() for acedb). If this fails it prints out a message and returns. Otherwise the current drawing stack is written into the file as postscript. Following this an attempt is made to print the file. If the environment variable ACEDB_LPR is set, then the value of this is used as the print command, otherwise "lpr" will be used. If ACEDB_LPR is set and null then no printing is attempted. Printing is attempted using system("print_command file_name").

NB Currently Helvetica is used as a font; this is not a fixed-width font. Ideally it should be.

void graphPop (void)

Uniconizes the active graph if iconized, and brings it to the top of the desktop.

BOOL graphQuery (char *text)

graphQuery() pops up a little window with the text and two buttons, "Yes" and "No". One of these must be pressed to continue, and graphQuery() returns TRUE is Yes and FALSE if No.

void graphRectangle (float x0, float y0, float x1, float y1)

Draws a hollow rectangle with opposite corners at (x0,y0), (x1,y1) in user coordinates, in the current foreground colour. On the screen the perimeter has width 1 pixel, but in postscript it has width the current line width.

Writes on the stack.

void graphRedraw (void)

Forces a complet redraw of the entire drawing stack for the active graph. To be called after finishing drawing. This is carried out by creating an X expose event, in contrast to graphBoxDraw(), which acts immediately.

GraphFunc graphRegister (int event, void (*f)())

This is the main routine to register callback functions for a graph. event must come from:

  enum GraphEvent {LEFT_DOWN, LEFT_DRAG, LEFT_UP,
	  	   MIDDLE_DOWN, MIDDLE_DRAG, MIDDLE_UP,
		   PICK, KEYBOARD, RESIZE, 
		   MESSAGE_DESTROY, DESTROY
		  } ;
f is function that will be called back. Before calling f the relevant graph is made active.

The return value is the previous function registered with this event, or 0 if there was none.

Details:
 Mouse buttons: the callback is f (double x, double y), where (x,y)
  gives the coordinates of the event in user units.  Note that x, y
  must be double, since this is not type-checked by the compiler.  
  The RIGHT mouse button events are reserved for the menu.
 PICK: if a pick function is registered then it takes precedence 
  over LEFT_DOWN.  It will call back f (int k, double x, double y)
  where k is the id of the innermost box that contains the cursor 
  point, and (x,y) are user coordinates with respect to the top 
  left corner of the box (NB - RELATIVE COORDINATES).  All picks 
  lie in some box, because the whole drawing area is box 0.  Boxes
  can refuse picks by calling graphBoxSetPick().
 KEYBOARD: The callback is f (int c), where c is normally the ascii
  character that was pressed.  For special keys the #define name 
  given in  in key.h should be used, as follows:

#define      SPACE_KEY           32
#define     ESCAPE_KEY           27
#define     RETURN_KEY           13
#define  BACKSPACE_KEY            8
#define         UP_KEY          257
#define       DOWN_KEY          258
#define       LEFT_KEY          259
#define      RIGHT_KEY          260
#define       HOME_KEY          261
#define        END_KEY          262
#define    PAGE_UP_KEY          263
#define  PAGE_DOWN_KEY          264
#define     INSERT_KEY          265
#define     DELETE_KEY          127

 RESIZE: The callback is f().  The application normally calls 
  graphFitBounds() to determine the new size of the window.
 MESSAGE_DESTROY: The callback is f().
 DESTROY: The callback is f().

void graphRectangle (float x0, float y0, float x1, float y1)

Draw a rectangle in a graph.

void graphRetitle (char *name)

Resets the title on the window for the active graph.

void graphScreenSize (float *sx, float *sy, float *fx, float *fy, int *px, int *py)

Return screen size in graphCreate units, TEXT units and pixels.

BOOL graphSelect (KEY *kpt, FREEOPT *options)

Presents a forced choice from options. This is a blocking call that requires a response.

Cancel returns FALSE. Selection of one of the legitimate options returns TRUE and fills *kpt.

The FREEOPT structure is:


typedef struct freestruct
  { KEY  key ;
    char *text ;
  } FREEOPT ;
options should be an array of FREEOPT. The length of the array is given in the key field of the first item. The text field of the first itme gives a title.

NB Use of this function is discouraged. It is hard to keep it working in all the different environments. Use Buttons or Menu instead if possible.

BOOL graphSetSelectDisplayMode (GraphSelectDisplayMode mode)

Sets the mode for displaying selection lists, the mode is has the values:

typedef enum _GraphSelectDisplayMode
  {GRAPH_SELECT_SCROLLED,
   GRAPH_SELECT_PLAIN} GraphSelectDisplayMode ;
GRAPH_SELECT_SCROLLED (default) shows the selections in a scrolled window, GRAPH_SELECT_PLAIN shows the selections in a plain window.

Graph graphSubCreate (int type, float ux, float uy, float uw, float uh)

Creates a new window, which is a subwindow of the currently active graph and returns a handle for it.. See graphCreate() for a description of the parameters.becomes the active window, and returns a handle for it.

Graph graphText (char *text, float x0, float y0)

Draws text in the currently active graph at the given coordinates.

Graph graphTextBounds (int nx, int ny)

Set the size of a graph in text coordinates.

int graphTextEntry (char* cp, int len, float x, float y, void (*fn)(char*)) ;

This creates a box that the user can type into of length len at coords (x,y). The text buffer is cp, which must be of length at least (len+1). cp can be initialised by the user before calling. graphTextEntry() grabs the KEYBOARD event input, and processes keystrokes by inserting printable characters into the buffer at the current cursor position and responding to arrow keys, delete keys and emacs control keys as appropriate to move the cursor. When the return key is pressed the registered function, fn, is called with current contents of its buffer as an argument.

The return value of graphTextEntry() is the id of the box containing the text. The user can change the contents of the cp buffer in the code and redraw this box (e.g. graphBoxDraw (entryBox, -1, -1) ;).

There can be several text entry boxes in one graph. At any one time at most one is active (grabbing keyboard input). This has a yellow background, while the inactive ones have a green background. The active one is the last for which graphTextEntry (cp,...) has been called. i.e. if you recall graphTextEntry() with the a cp buffer that has already been used it will activate the existing box, not create a new one. This is often done in response to PICK'ing the dormant box. graphEntryDisable() can be used to disable all text entry boxes, and restore the original KEYBOARD callback function.

NB two boxes are actually made by each call to graphTextEntry: the main one and one to hold the cursor that is a subbox.

See also graphCompletionEntry().

int graphTextInfo (int *dx, int *dy, float *cw, float *ch)

Get the size of text in the active graph.

int graphTextScrollEntry (char* text, int len, int wlen,

float x, float y, void (*fn)()) Similar to graphTextEntry with following differences:

This creates a box that the user can type into of length 'wlen' at coords (x,y). The text buffer is 'text', which must be of length at least ('len'+1). 'text' can be initialised by the user before calling.

Because the buffer length may now be greater than the window length (no truncation of input) we now allow for scrolling of text. Text is only partially displayed up to as many characters as the size of the box allows. Left and right keys will scroll if the cursor is at the margins of the box and more text exists in the buffer to the "left" and "right" of the current cursor position.

Control-L will center the text buffer in the window (show the middle) and center the cursor position.

See also graphTextEntry(), graphCompletionEntry(), graphCompScrollEntry()

void graphText (char *text, float x0, float y0)

Writes the given string with top left at the given location. Line breaking must be done by the user code (possibly using the uLines package from arraysubs). See also graphTextPtr().

Writes on the stack.

void graphTextBounds (int nx, int ny)

For TEXT_SCROLL graph types only (see graphCreate()). Sets the drawing area in character coords (columns, rows). Top left is always (0,0). The ny argument is used to set up scrolling if necessary. nx is only used when writing PostScript files with graphPS(); normally the user must extend the window rightwards to see more. graphTextbounds() can be called after drawing (i.e. once you know how big the drawing is), which is useful.

int graphTextFormat (int format)

Sets the format to one of

	enum TextFormat { PLAIN_FORMAT, BOLD, ITALIC }
Returns the old value. If format < 0 then just returns the old value.

float graphTextHeight (float x)

Sets the height in user coordinates for text. Returns the old value. If x < 0 then just returns the old value. Except for postscript the actual height achieved will be only approximately that requested.

Setting height = 0 gives the default font.

void graphTextInfo (int *dx, int *dy, float *cw, float *ch)

Can be called on any graph. Fills *dx, *dy, *cw, *ch. Gives the size of the current font in pixels (*dx, *dy) and in user units (*cw, *ch).

void graphScreenSize (float *sx, float *sy, float *fx, float *fy, int *px, int *py)

Can be called before graphCreate(). (But must be after graphInit of course.) This function provides sizes of the screen, font and pixels in graphCreate units. Fills *sx, *sy, *fx, *fy, *px and *py only if they are non-zero.

*sx - Width  of entire screen in graphCreate units.
*sy - Height of entire screen in graphCreate units.

*fx - Number of text columns over entire width of screen.
*fy - Number of text rows over entire height of screen.

*px - Number of pixels over entire width of screen.
*py - Number of pixels over entire height of screen.
By dividing sx with fx or px, the width of a character or pixel can be calculated in graphCreate units. Dividing px with fx gives the width of a character in pixels. Apologies that this is such an indirect and complicated function, but a more intuitive version was vetoed. Examples:
Create a graph which uses half the screen:

{
  float sx, sy ; 
  graphScreenSize (&sx, &sy, 0, 0, 0, 0) ;
  graphCreate (TEXT_SCROLL, "Half", 0, 0, sx, 0.5*sy) ;
}

Create a graph with 80 columns and 25 rows:

{
  float sx, sy, fx, fy ;
  graphScreenSize (&sx, &sy, &fx, &fy, 0, 0) ;
  graphCreate (TEXT_SCROLL, "80x25", 0, 0, 80*sx/fx, 25*sy/fy) ;
}

Note: unfortunately graphCreate does not make extra space for scrollbars, so you have to do that yourself.

void graphTextPtr (char *text, float x0, float y0, int length)

Similar to graphText() except that a pointer to the text is kept on the stack, rather than the text itself. This allows the text to be changed without redrawing the whole graph. The maximum expected length of the text must be given to determine the box extent.

Writes on the stack.

void graphTextPtrPtr (char *text, float x0, float y0,

As for graphTextPtr() except that a pointer to a pointer to text is stored on the stack.

Graph graphTextUp (char *text, float x0, float y0)

Draw updatable text into current graph, length is length to extend the box.

void graphUnMessage (void)

See graphMessage().

void graphWaitCursor (BOOL)

Set a wait cursor (usually watch or egg-timer shaped) for the active graph.

void graphWaitCursorAll (BOOL)

Set a wait cursor (usually watch or egg-timer shaped) for all graphs.

void graphWhere (float *x1, float *y1, float *x2, float *y2)

Return coordinates of the active graph.

void graphWindowSize (float *xp, float *yp, float *wp, float *hp)

Returns the size of the active graphs window (in user coordinates, not pixels).

void graphXorBox (int kbox, float x, float y)

Draws a rectangle in XOR mode of the same dimensions as box kbox, with top left at (x,y). This does not affect the drawing stack - e.g. it is not seen in the postscript output.

Low level: used by graphBoxDrag(), which should normally be used by the user.

void graphXorLine (float x0, float y0, float x1, float y1)

Draws a line in XOR mode on the screen. This does not affect the drawing stack - it is purely for temporary cursor type operations. Calling a second time on the same line removes it.