Extending ProWesS

BEWARE : only persons already capable of writing programs which use ProWesS should continue. This document is only intended for persons who want to add or modify ProWesS types.

How to write your own types

Shape and Form

A ProWesS type is simply an external module, as is supported by syslib. These modules can easily be built by using some lines in your makefile, similar to these (example for the menu type) :

type_menu : type_menu_o core-dll_o
        ${LD} -ms -otype_menu type_menu_o \
        core-dll_o -lpw -lpf -lsms -sxmod
        mkxmod type_menu \"ProWesS External Type Definition\"

The init bit in the module should be a structure which defines the type and its functionality. This structure is defined in the PWTypeDef.h header file. This header file is always included when you include the PWHandler.h header file. So a generic definition for the init structure would be :

PWTypeDef init={
    PW_TYPEDEF_FLAG, PW_TYPE_CANVAS, PW_TYPE_EVENT_REGION,
    &CanvasCreate, &CanvasChange, &Query, &CanvasRemove,
    &CanvasEntry, &CanvasExit, &CanvasHandle,
    NULL, &CanvasDraw, &CanvasDetermineSize, &DoScale
};

This mechanism also allows you to define more than one type in one module file. This is for example used to combine the container, separator and direction types in one module. In this particular case, the definition of the types looks like this :

PWTypeDef keep={
    PW_TYPEDEF_FLAG, PW_TYPE_DIRECTION, PW_TYPE_EVENT_REGION,
    &KEEPCreate, NULL, NULL, &Remove, NULL, NULL, NULL, NULL,
    NULL, GetColour, NULL
};

PWTypeDef cont={
    (int)&keep, PW_TYPE_CONTAINER, PW_TYPE_EVENT_REGION,
    &CONTCreate, NULL, NULL, &Remove, NULL, NULL, NULL, NULL,
    &CONTDraw, GetColour, NULL
};

PWTypeDef init={
    (int)&cont, PW_TYPE_SEPARATOR, PW_TYPE_EVENT_REGION,
    &SEPACreate, NULL, NULL, &Remove, NULL, NULL, NULL, &Define,
    &SEPADraw, &SEPADetermineSize, NULL
};

You can see that you can link several types by using the pointer to a different type definition instead of the identifier flag. This example also shows that many of the fields in the type definition can be NULL.

Each type has a name (new names should be registered at PROGS to prevent name clashes). You also have to specify whether the type is used to build region or keypress objects. Apart from that, the type is completely defined by a set of routines which are called when certain events occur.

#define PW_TYPEDEF_MODULE "ProWesS External Type Definition"

#define PW_TYPEDEF_FLAG 0x58505753  /* flag for PWXType */

#define PW_TYPE_EVENT_REGION    1   /* region associated with object type */
#define PW_TYPE_EVENT_KEYPRESS  2   /* keypress associated with object type */

typedef struct _PWTypeDef {
    int flag;                       /* flag for external object 'XPWS' */

    PWType identifier;              /* type identifier, e.g. 'OUTL' */

    char event_type;                /* identifies type of event associated */
                                    /* with object of this type */

    /* the routines to handle calls which affect this type of object */
    Error (*PWCreate)(PWObject owner, PWObject *ret, PWEvent event, int *taglist);
    Error (*PWChange)(PWObject object, int *taglist);
    Error (*PWQuery)(PWObject object, int what, void *ret);
    Error (*PWRemove)(PWObject object);

    /* handler routines */
    Error (*PWRegionEntry)(PWObject object);
    Error (*PWRegionExit)(PWObject object);
    Error (*PWHandle)(PWObject object, PWWait event);

    /* routine to define the parameters according to the configuration file */
    Error (*PWDefine)(char *name, char *value);

    /* draw routine for object (if it is drawable) */
    Error (*PWDraw)(PWObject object);

    /* routines to handle the size aspects of the object */
    Error (*PWDetermineSize)(PWObject object);
    Error (*PWScale)(PWObject object, pt xinc, pt yinc);
};

The use of each of the member functions is explained in detail below. In principle, each function is called to handle a particular event which may occur. The exception to this is the PWHandle function. This function is actually called for all events which don't have a specific function. The event itself is in that case identified in the event parameters.

When defining a type, you can determine which information is contained in each object. Each object automatically contains some information which references (amongst other things) the system in which the object is contained, and a description of the object as positioned inside the system. This last reference differs depending on whether the objects are windowing or keypress objects.

To make sure that ProWesS can use this information, the start of the object is fixed, and should always be PWBaseObject. All the things which have to be known when writing a ProWesS type are defined when you include the PWHandler.h header file.

#include "PWHandler.h"

typedef struct _Object {
    PWBaseObject;
    some extra variables, specific to each object
} Object;

Events

In ProWesS there are three types of events. Some events have a specific handler routine. Those events are described below. All the other events have to be handled by the PWHandle routine which is part of the type. For these events, there are two types. Some events are only generated when you ask to be notified for those events. Some events can always be generated.

Each windowing object has a link to a structure of type PWEvent. This sctructure contains a variable called wait_event. This variable is a mask which indicates which events have to be passed and which don't. You can add events by ORing the event code to the value. You should never mask an event which is always generated in this value !

Events which can be enabled/disabled
PW_EVENT_HIT
This event is generated when the user generates a HIT inside the object.
PW_EVENT_DO
This event is generated when the user generates a DO inside the object.
PW_EVENT_TIMEOUT
In the region, you can also set the timeout which should be used. When this event is enabled and a timeout occurs while the pointer is inside this object, then this event will occur. This event can be useful for the display of information when the user has not moved the pointer for some (short) time.
PW_EVENT_MOVE
This event is generated when the pointer moves (and is still inside the window). This event can for example be used while rubber banding, to display the new position of a line. The pointer position inside the window can be determined using the following lines
    PWSystem *system=object->event.region->system;
    pix ptrxpos=system->ptr_xpos-system->win_xpos-reg->hit_xorg+system->xorg;
    pix ptrypos=system->ptr_ypos-system->win_ypos-reg->hit_yorg+system->yorg;
    
PW_EVENT_HITDRAG
When this event is activated, ProWesS will check whether the user did a simple HIT or started dragging. When the user started dragging, this event will be generated. Often, wyou will then start checking for either move or timeout events to make visible what happens while dragging.
PW_EVENT_DODRAG
This event is similar to PW_EVENT_HITDRAG, except that is generated when the user is dragging using a DO.
PW_EVENT_DRAGADJUST
While dragging, ProWesS does not allow the pointer to move outside the current object. When the pointer position is adjusted to stay inside the region, and this event is active, it will be generated. This can be used to automatically scroll the contents of the region.

When this event is generated, you can determine how much the pointer was adjusted by reading the Xdragadjust and Ydragadjust variables in the PWsystem structure.

Events which are always active
PW_EVENT_DRAGEND
When the user ends a dragging operation, this event is generated. Although this event is always active, it can only be generated when you actually activated one of the hit or do dragging events.
PW_EVENT_CATCH
When a keypress in a window can not be matched to a keypress object in the system, the keypress will be redirected to the current catch object, which can then try to take some action. The key which was pressed can be read from the PWSystem structure using the catch field.
PW_EVENT_CATCHSTART
This event is generated when the object is selected as the current catch object for the unmatched keypresses.
PW_EVENT_CATCHEND
When a new object is selected as catch object, then the previous object will receive this event to indicate that it can no longer receive keypresses.
PW_EVENT_AUTOSIZE
At most one object in the window can be given a chance to change the window size to match the data which has to be displayed in the object. This object can be designated using the PW_OBJECT_AUTOSIZE tag. When an object receives this event, it is intended to check whether the window size has to be adjusted. If it is usefull, the SSTATUS_WINCHANGE bit in the status field of the PWSystem whould be set. The object can check what the maximum increase of the window size is by checking the Xspaceleft and Yspaceleft fields.

Behind the scenes

To allow the objects to function properly, you get access to a part of the ProWesS data structures. These data structures are described here. All these data structures are defined when you include the PWHandler.h file.

Please note that only the fields which have a plus sign at the beginning of the description are allowed to be modified. The other fields should be treated as read only !

typedef struct _PWSystem PWSystem;
typedef union {
    struct _PWRegion *region;       /*    region */
    struct _PWKeyPress *keypress;   /* OR keypress */
} PWEvent;

/* -- definition of the dummy object (base of the real object) -- */

#define OSTATUS_REDRAW      (short)0x0001   /* set when redraw needed */
#define OSTATUS_REMOVE      (short)0x0002   /* set when should be removed */
#define OSTATUS_INITIALISED (short)0x0004   /* set when initialised */
#define OSTATUS_POINTER     (short)0x4000   /* pointer in object on activate */
#define OSTATUS_AUTOSIZE    (short)0x2000   /* increase size to fit contents */

#define PWBaseObject \
    PWTypeDef *type;                /* type of object */                    \
    PWEvent event;                  /* event associated with object */      \
    void *auxiliary;                /* auxiliary parameter for client */    \
    short status                    /*+object status */

struct _PWDummyObject {
    PWBaseObject;                   /* common start for all objects */
};

The status field contains a mask of statusses for the object (OSTATUS_xxx).

typedef unsigned char Scale;        /* scale factor */
typedef int pix;                    /* pixel values */
typedef short PWWait;               /* type for events */

struct _PWRegion {
    PWSystem *system;               /* global data structure for window */
    PWObject parentObject;          /* parent (owner when creating file) */
    PWObject owner;                 /* owner/creator of this region */
    struct _PWRegion *parent;       /* parent region */
    struct _PWRegion *next;         /* next region on this level */
    struct _PWRegion *prev;         /* previous region on this level */
    struct _PWRegion *children;     /* list on next level of nesting */
    short level;                    /* nesting level (==parent->level+1)
                                    /* parameters for region handler */
    PWWait wait_event;              /*+events to wait for */
    TimeOut timeout;                /*+timeout for events */
                                    /* total area covered by region */
    pt tot_xpos, tot_ypos;          /* origin of region in parent */
    pt tot_xsiz, tot_ysiz;          /*+size of region */
                                    /* hit area, only this area is handled by */
                                    /* the object type handle routine */
    pix hit_off_lft, hit_off_rgt;   /*+margins of hit area in total area */
    pix hit_off_top, hit_off_bot;   /*+margins of hit area in total area */
    pix hit_xorg, hit_yorg;         /* top left coordinate of area on screen */
    pix hit_xend, hit_yend;         /* bottom right coordinate */
                                    /* scale parameters for object */
                                    /* the size of the margins around the hit */
                                    /* area stays the same */
    Scale scale;                    /* scale factor for size (primary dir.) */
    char windowfit;                 /* secondary direction */
    char composite;                 /*+this is a composite object */
    char dragging;                  /* are we dragging in this region ?? */
};

struct _PWKeyPress {
    PWSystem *system;               /* global data structure for window */
    PWObject parentObject;          /* parent (owner when creating file */
    PWObject owner;                 /* owner/creator of this keypress */
    struct _PWKeyPress *next;       /* next keypress in list of keyspresses */
    char key;                       /* key which should be pressed */
    char alternative;               /* alternative for this action */
};

typedef struct _PWRegion PWRegion;
typedef struct _PWKeyPress PWKeyPress;
/* Each type can have a general state, to keep information about it */
/* Normally the base type is followed by extra type specific information */
/* The PWTypeState information is kept in a list with sentinel (type=NULL) */

#define PWBaseTypeState                                     \
    PWType type;                    /* type identifier */   \
    struct _PWDummyTypeState *next  /* next item */

typedef struct _PWDummyTypeState {
    PWBaseTypeState;                /* common start for all typestates */
} PWDummyTypeState;

/* System data structure */

#define PWPublicSystem                                                        \
    unsigned long status;           /*+system status (values SSTATUS_xxx) */  \
                                                                              \
    Gstate gstate;                  /* PROforma gstate is used (or NULL) */   \
    Window window;                  /* Window identifier */                   \
                                                                              \
    PWObject catchkeys;             /* object which has to catch keypresses */\
    char catch;                     /* key for which catching is needed */    \
    char activated;                 /* TRUE when system activated */          \
                                                                              \
    short ptr_xpos, ptr_ypos;       /* current pointer position */            \
    pix win_xpos, win_ypos;         /* position of window */                  \
    pix xorg, yorg;                 /* origin of area in window */            \
                                                                              \
    Heap heap;                      /* heap with/for this system */           \
                                                                              \
    int Xdragadjust, Ydragadjust;   /* adjustment of pointer while dragging */\
    int Xspaceleft, Yspaceleft      /* space left for auto increment of obj */

#ifndef _PW_PRIVATE
struct _PWSystem {
    PWPublicSystem;
};
#endif _PW_PRIVATE

#define SSTATUS_REDRAW      0x00000001  /* set when part of window needs redraw */
#define SSTATUS_WINCHANGE   0x00000004  /* set when window build has changed */
#define SSTATUS_PAGESHOW    0x00000040  /* set when PFPageShow should be done */

Create routine

.....

Change routine

.....

Query routine

.....

Remove routine

.....

RegionEntry routine

.....

RegionExit routine

.....

Handle routine

.....

Define routine

.....

Draw routine

.....

DetermineSize routine

.....

Scale routine

.....

ProWesS Core routines

To aid in the development of types, some hooks back into ProWesS are provided. These hooks can be used by linking the 'core-dll_o' file into your external module. When you use these functions you should include the line

#include "PWHandler_h"
in your source file.

PWHandleDefine

To make it easier to handle definition constants, the PWHandleDefine function is provided. Starting from a table of possible constants and their type, place to store the new value etc. The type for the constant can be either a builtin type, or you can provide a routine to parse this value.

To aid in using the definition constants, you can also provide a post processing routine (e.g. to assure that a certain value is always even), and you can pass the address of a character which is set to TRUE when that constant was defined. This can be used to know whether the value as defined should be used, or whether a default value has to be obtained (probably by querying the ProWesS system).

typedef struct _Defines {
    char *name;                 /* name of constant, no prefix */
    Error (*Handle)(struct _Defines *possible, char *value);
    void (*Post)(void *data);   /* post processing routine */
    void *data;                 /* place where data should be put */
    char *set;                  /* set to TRUE when data set (if !NULL) */
} Defines;

#define PW_DEFINE_COLOUR        ((Error (*)(Defines*,char*))1)
#define PW_DEFINE_BOOLEAN       ((Error (*)(Defines*,char*))2)
#define PW_DEFINE_CHARACTER     ((Error (*)(Defines*,char*))3)
#define PW_DEFINE_SHORT         ((Error (*)(Defines*,char*))4)
#define PW_DEFINE_INT           ((Error (*)(Defines*,char*))5)
#define PW_DEFINE_PT            ((Error (*)(Defines*,char*))6)
#define PW_DEFINE_FONT          ((Error (*)(Defines*,char*))7)

Error PWHandleDefine(char *prefix, Defines *possible, char *name, char *value);

This function is used in most of the builtin types. For example in the scroll type. A small extract of that code is shown here :

Error SetTRUE(Defines *this, char *value)
{
    *(char *)this->data=TRUE;
    return ERR_OK;
}
Error SetFALSE(Defines *this, char *value)
{
    *(char *)this->data=FALSE;
    return ERR_OK;
}
void cwidth(void *data)
{
    xwidth=PF2xpix(width);
    ywidth=PF2ypix(width);
}

Defines defines[]={
{ "ARROWS-LEFT",            SetTRUE,            NULL,   &arrowsleft,    NULL },
{ "ARROWS-RIGHT",           SetFALSE,           NULL,   &arrowsleft,    NULL },
{ "BAR-MARGINWIDTH",        PW_DEFINE_PT,       cwidth, &width,         NULL },
{ "BAR-COLOUR",             PW_DEFINE_COLOUR,   NULL,   &colour,        &Kcolour },
{ 0 }
};

Error ScrollDefine(char *name, char *value)
{
    return PWHandleDefine("SCROLL-",defines,name,value);
}

PWHandleKeyPress

This command can force ProWesS to handle the current keypress in the window. ProWesS always handles keypresses as window actions. Only when no keypress handler object is created for a keypress, the current object has the chance to react to that key. However, a HIT or DO is an event which can be acted upon by the region handler. In some cases, it may be useful to pass this on to the window (possibly in the form of a <space> or <enter> keypress). Of course, you are free to activate any keypress in the window.

The current keypress in stored in :

PWSystem *system;       /* the current system */
...
system->ptr_info.keystroke

PWGetType

Get the type definition of another type in the system. This command is needed when you want to use (part of) another already existing type. It can be compared with inheritance in object oriented programming languages. You can thus use the access routines of another type and possibly extend it, or call the existing routines in some cases, and handle some things differently.

An example where this is useful is the implementation of loose items. The loose item needs to get a border when the pointer enters the region. This is no real problem, but there may also be other types of items, like edit text which also needs such a border. However, I don't want to repeat the method of drawing this border for each type, and I want all these borders to look the same. Therefore the item type is used for the entry and exit routines of the type, and the item routine can also determine the size of the border around the hit area. The size and colour of the border can be configured, and the system background colour is used to remove the border.

PWAllocate & PWRelease

These commands allow you to allocate and release some memory for a ProWesS type. It is advised that these routines are used specifically when allocating and releaseing the object itself.

PWSkipTag

Some of the type handler routines have a taglist as parameter. Some of these tags may have to be processed, and some just have to be skipped. The PWSkipTag routine skips a tag and all the parameters. This is important because there are many kinds of tags (fixed number of parameters, list or taglist as parameter), and because there has to be a correct method of skipping unrecognised tags.

PWpixel2point, PWpoint2pixel

Get the conversion factors to convert points (PROforma coordinates) into pixels (window coordinates), or vice versa.

{
    pt Xpt2pix, Ypt2pix;
    pt Xpix2pt, Ypix2pt;
    pt pix, x, pf;
    short Pix, X;

    PWpixel2point(&Xpix2pt,&Ypix2pt);
    PWpoint2pixel(&Xpt2pix,&Ypt2pix);

    /* convert x (PROforma coor), into equivalent in pixels (pt or short) */
    pix=fixmul(x,Xpix2pt);
    Pix=x/Xpt2pix;
    Pix=pt2short(fixmul(x,Xpix2pt));

    /* convert X (pixel coor, short), into PROforma coor (pt) */
    pf=X*Xpix2pt;
    pf=fixmul(short2pt(X),Xpix2pt);

}

PF2xpix, PF2ypix

These routines will convert PROforma coordinates (in pt) into window coordinates (in pixels, short). The pixel coordinates are not rounded, but truncated.

xpix2PF, ypix2PF

These routines will convert window coordinates (in pixels, short) into PROforma coordinates (in pt).

PFxround, PFyround

These routines can truncate PROforma coordinates to an integral number of pixels. You could do this by using the conversion factors returned by the PWpixel2point and PWpoint2pixel routines, or applying xpix2PF(PF2xpix(X)), but these calls are more efficient. Also, doing the calculations yourself with the conversion factors can cause degradation. For example, in the following function
char test(pt x)
{
    pt Xpt2pix, Ypt2pix;
    pt Xpix2pt, Ypix2pt;
    pt y;

    PWpixel2point(&Xpix2pt,&Ypix2pt);
    PWpoint2pixel(&Xpt2pix,&Ypt2pix);

    y=XROUND(x);
    return XROUND(x)==XROUND(y);
}
the result would be TRUE if XROUND is defined as
#define XROUND(x) PFxround(x)
but it would be false with the following definition
#define XROUND(x) (Xpix2pt*pt2short(fixmul(Xpt2pix,(x))))

PROGS, Professional & Graphical Software
last edited April 28, 1996