This is a smallish program which allows you to investigate the value of all the Global Variables which have been defined, and to change, add or delete global variables.
Global Variables are an operating system extension which is introduced in ProWesS. It is a system which is similar to (but not the same as) environment variables on Unix systems. It allows you to assign a value (a string) to a name. This value can be queried by everybody and can also be changed by anybody. It is mainly used to ease the installation process for programs. It is for example used when loading ProWesS. The device and directory where ProWesS is loaded from is stored in a global variable (PWSDIR). The value of this variable is then used to find files.
The libraries (syslib) support the use of global variables when a file is opened. For example, when a file if opened with the name "$PWDIR_doc_loader_html", then the "$PWDIR" is automatically replaced by its value (e.g. "win1_pws").
This program is also a very good example for the ease with which
interactions between several parts of the window can be programmed.
The window contains a few items. The two items labeled "constant" and "value", contains the name and value of a constants. These items can be edited at wish. When the name of a Global Variable is indicated in the menu at the bottom of the window, then that name and the current value will be displayed in the items just above the menu. A Global Variable can be (re)set by indicating the "Set constant" item, or deleted by indicating "Delete constant".
Here is a run through of the source code. Obviously, it starts by including the header files, and some constants which are used to access the Global Variables thing. Also a special macro is defined which helps to catch errors when they occur (hence the name).
#include "str.h" #include "thing.h" #include "ProWesS.h" #define catch(x) if (err=(x)) return err; else #define GLOBAL_NAME "Global Variables" #define GLOBAL_GET 0x47455420 /* "GET " */ #define GLOBAL_SET 0x53455420 /* "SET " */ #define GLOBAL_DELE 0x44454c45 /* "DELE" */ #define GLOBAL_FRST 0x46525354 /* "FRST" */ #define GLOBAL_NEXT 0x4e455854 /* "NEXT" */
The text which is displayed inside the items and as labels is defined separately. This makes it easier to change them (for example to produce a copy of the program in a different language). In fact, they could just as well be made configurable.
#define LabelConstant "constant : " #define LabelValue "value : " #define ItemSet "Set constant" #define ItemDelete "Delete constant"
The maximum length of the strings which can be edited (the name and value of the constant) are defined here. These length of the value is not limited by the Global Variables thing, but limiting it makes them easier to handle. In fact, the "edline" object (which allows you to edit a string) always works with a fixed length string. This length can be defined when the object is created. Otherwise a default length is used (this default length is configurable).
#define MAX_NAME 64 #define MAX_VALUE 256
Because we think it useful to make programs re-entrant, you should not use global variables, as their value will be shared between all the copies of the program (especially when the program is loaded as an executable thing). Therefore a global structure is needed which is used to pass the parameters so that they are accessible in all the functions. The base of this structure can be stored in the ProWesS system.
typedef struct { PWObject menu; PWObject constant, value; } Global; /* forward declarations */ Error readall(PWObject object); Error set(PWObject object); Error delete(PWObject object); Error select(PWObject object, char *item);
The program starts by creating the outline for the window. This outline
includes the title (which is the default, the program name), a wake, quit
and a sleep item. The quit item is also activated when Inside the outline, there are many items. The items are normally all below each
other at the first level of nesting inside the outline, so a box is created
to change the direction as I want the items to be side by side. Inside this
box, there are the two loose items to set and delete a constant. The event
handlers which have to be called when the items are indicated are defined.
It is also specified that the status of the items should not be changed when
they are indicated.
Below these items, there are the two objects to edit the strings with the
name and value of the constant. The maximum length of these strings is
given. Then the two edline objects are connected with each other to make
sure that the user can move the cursor between the two items. This is done
using the up and down keys. Also after editing the string in the first edline,
the user can automatically modify the value for that constant.
Of course, we also need a menu which will contain all the constants which are
defined at a given moment. This menu is separated from the rest of the window
with a separator line. At least six lines are always visible in the menu. All
the items inside the menu are always sorted, using a case independant
compare (compare routine is given). The event handler which has to handle the
selection of an item is specified, but no item can appear to be selected.
The edline objects which were defined a bit higher are not yet labeled.
Therefore, the labels are added to the left of the items. To make sure the
edlines are as large as possible when the window is scaled, we make sure that
the label itself is not scaled.
The labels are added here because otherwise the default ordering of the objects
in the window could no longer be used. The alternative solution for this is
used when defining the loose items above. These are also positioned side by
side, but because they are positioned inside a direction box, the default
positioning rule is not hampered (as this is defined to be a structuring
object).
Before we can start, we have to fill the menu with all the constants which are
defined at the moment. So we call the event handler which will also handle
the wake event. Then the window is activated.
To read all the definition constants, an iterator which loops over all the
Global Variables has to be used. To start we have to extract the global
structure from the ProWesS system (the global auxiliary). The menu is then
cleared to remove the old contents. A little loop is then started which
iterates over all the constants which are defined. The Global Variables
system is accessed using the thing system. Each constant of which the name is
thus obtained, is then adde in the menu. The menu object will automatically
make sure that its contents remains sorted.
When a constant in the menu is indicated, the name and value of that constant
have to be displayed in the edline objects. So to start, we have to retrieve
the object identifiers for the edline objects. These are stored in the Global
structure which is referenced in the global auxiliary for the window. The value
for the constant then has to be queried by calling the Global Variables
thing. The strings with the name and value of the constant then have to be
passed to the edline objects.
Setting a Global Variable is approximately the reverse of the select
routine above. After querying the global auxiliary, the strings which are
stored in the edline objects have to be obtained. Then a Global Variable
is defined with the given name and value. To make sure that the menu stays
synchronized with the existing variables, the contents of the menu is rebuilt.
Deleting a constants is also quite similar with setting one. In this case,
the value for the name is irrelevant, but it is adviseable that the edlines
are cleared after the constant was deleted. Again, the menu is also rebuilt
to stay up to date.
The makefile for this program is quite straightforward. In fact, most of the
makefile is standard, as it originates from a simple template makefile.
The most import lines are the line which starts with "OBJ =". The
parameter is a list of all the object files for the application. In this case,
the entire program is in one file.
Another important line starts with "all :". This lists all the
targets in this directory which have to be created. All dependencies are
automatically checked and everything is rebuilt when necessary.
The line starting with "global" lists first the dependencies, and
then the
programs which have to be called to build the file. This starts by calling
the linker, with all the object files. The output file (-o) is called "global",
and the map and symbol table are produced (-ms). All the necessary libraries
are included (-lpw -lpf -lsms). Because the program which is built will be
an executable, the proper startup file has to be used. This is done with the
-sexec parameter.
After the linking stage, some post processing has to be done to make the
dataspace of the output file correct and add the program name. This is done
with the "mkexec" program which has the file and the program name as
parameters. Optionally, an extra parameter with the requested extra amount of
dataspace can be passed (the default is 4kB). The program name is enclosed in
quotes (a quote has to be preceded by a backslash or the "make" program will
discard it). The yen symbol is used to separate the actual program name from
an extra comment which will be part of the file.
Error init()
{
Global g;
Error err;
PWObject win, box;
catch( PWCreate(NULL, &win, PW_TYPE_OUTLINE,
PW_OUTLINE_SLEEP,
PW_OUTLINE_QUIT,
PW_OUTLINE_QUIT_KEYPRESS, 27,
PW_OUTLINE_ACTION_WAKE, readall,
PW_GLOBAL_AUXILIARY, &g,
NULL) );
catch( PWCreate(win, &box, PW_TYPE_DIRECTION, NULL) );
catch( PWCreate(box, NULL, PW_TYPE_LOOSE_ITEM,
PW_LOOSE_TEXT, ItemSet,
PW_LOOSE_CHANGE_STATUS, FALSE,
PW_LOOSE_ACTION_HIT, set,
NULL) );
catch( PWCreate(box, NULL, PW_TYPE_LOOSE_ITEM,
PW_LOOSE_TEXT, ItemDelete,
PW_LOOSE_CHANGE_STATUS, FALSE,
PW_LOOSE_ACTION_HIT, delete,
NULL) );
catch( PWCreate(win, NULL, PW_TYPE_SEPARATOR, NULL) );
catch( PWCreate(win, &g.constant, PW_TYPE_EDLINE,
PW_EDLINE_MAXLENGTH, MAX_NAME,
NULL) );
catch( PWCreate(win, &g.value, PW_TYPE_EDLINE,
PW_EDLINE_MAXLENGTH, MAX_VALUE,
NULL) );
catch( PWChange(g.constant,
PW_EDLINE_EDLINE_AFTER, g.value,
PW_EDLINE_EDLINE_DOWN, g.value,
NULL) );
catch( PWChange(g.value,
PW_EDLINE_EDLINE_UP, g.constant,
NULL) );
catch( PWCreate(win, NULL, PW_TYPE_SEPARATOR, NULL) );
catch( PWCreate(win, &g.menu, PW_TYPE_MENU,
PW_MENU_VISIBLE_LINES, 6,
PW_MENU_SORT_COMPARE, STRCompareCI,
PW_MENU_ACTION_SELECT, select,
PW_MENU_NONE_SELECTED,
NULL) );
catch( PWCreate(win, NULL, PW_TYPE_LABEL,
PW_POSITION_LEFT_OF, g.constant,
PW_LABEL_TEXT, LabelConstant,
PW_SCALE_FACTOR, 0,
NULL) );
catch( PWCreate(win, NULL, PW_TYPE_LABEL,
PW_POSITION_LEFT_OF, g.value,
PW_LABEL_TEXT, LabelValue,
PW_SCALE_FACTOR, 0,
NULL) );
readall(win);
return PWActivate(win);
}
Error readall(PWObject object)
{
Error err;
Global *g;
char name[MAX_NAME], value[MAX_VALUE];
PWQuery(object,PW_GLOBAL_AUXILIARY,&g);
PWChange(g->menu,PW_MENU_CLEAR,NULL);
err=THINGCall(GLOBAL_NAME,GLOBAL_FRST,3,name,value,MAX_VALUE);
while (!err)
{
catch( PWChange(g->menu, PW_MENU_ADD_COPY, name, NULL) );
err=THINGCall(GLOBAL_NAME,GLOBAL_NEXT,3,name,value,MAX_VALUE);
}
return ERR_OK;
}
Error select(PWObject object, char *item)
{
Error err;
Global *g;
char value[MAX_VALUE];
PWQuery(object,PW_GLOBAL_AUXILIARY,&g);
err=THINGCall(GLOBAL_NAME,GLOBAL_GET,3,item,value,MAX_VALUE);
if (err) item="", value[0]='\0';
PWChange(g->constant, PW_EDLINE_SET, item, NULL);
PWChange(g->value, PW_EDLINE_SET, value, NULL);
return ERR_OK;
}
Error set(PWObject object)
{
Error err;
Global *g;
char name[MAX_NAME], value[MAX_VALUE];
PWQuery(object,PW_GLOBAL_AUXILIARY,&g);
PWChange(g->constant,PW_EDLINE_GET,MAX_NAME,name,NULL);
PWChange(g->value,PW_EDLINE_GET,MAX_VALUE,value,NULL);
THINGCall(GLOBAL_NAME,GLOBAL_SET,2,name,value);
return readall(object);
}
Error delete(PWObject object)
{
Error err;
Global *g;
char name[MAX_NAME];
PWQuery(object,PW_GLOBAL_AUXILIARY,&g);
PWChange(g->constant,PW_EDLINE_GET,MAX_NAME,name,NULL);
THINGCall(GLOBAL_NAME,GLOBAL_DELE,1,name);
PWChange(g->constant, PW_EDLINE_SET, "", NULL);
PWChange(g->value, PW_EDLINE_SET, "", NULL);
return readall(object);
}
The makefile
# makefile for ProWesS application software
# possible flags - none define just yet
DEFINES =
# specify compiler etc
CC = cc
CFLAGS = -c -O
LD = ld
MAC = qmac
OBJ = global_o
all : global
global : ${OBJ}
${LD} -ms -oglobal \
${OBJ} \
-lpw -lpf -lsms -sexec
mkexec global \"globalžv1.00, manipulate \"\"Global Variables\"\", from PROGS, Belgium\"
_c_o : ; ${CC} ${CFLAGS} ${DEFINES} $<
_s_o : ; ${CC} -c $<
_asm_rel : ; ${MAC} $<
PROGS, Professional & Graphical Software
last edited June 28, 1996
originally published in QL Today May/June 1996