List Viewer OPM Module for EPOC (Version 0.8) |
Dr. Thomas Tensi Paganinistraße 60, D-81247 München |
This OPM module implements vertical list viewers for use in OPL on the EPOC machines. A list viewer is a scrollable window which displays entries of some underlying list of data records. Each record is displayed in a single row and those rows are stacked up vertically.
To identify records each record has some integer number associated with it from an interval [1..n]. Normally the list viewer displays only a subrange [i..i+k-1] with the constraint (i = 1 OR (i > 1 AND i+k-1 <= n)).
Fig. 1: Example of a list viewer with associated vertical scrollbar
The list viewer module offers the following services:
My implementation has the following features and misfeatures:
The following steps have to be performed to make the list viewer module available:
To use the list viewer module in your own program you have to do the following:
INCLUDE "OPM.omh" INCLUDE "Scrollbar.omh" INCLUDE "ListViewer.omh"
PROC init1: LOADM OPM_loader$ OPM_loadModule:("Scrollbar") OPM_loadModule:("ListViewer") UNLOADM OPM_loader$ Scrollbar_initMODULE:("init2") ENDP PROC init2: ListViewer_initMODULE:("myFirstProcedure") ENDP ...
The string parameter for ListViewer_initMODULE:
is
the name of the first procedure in your program.
So whenever the list viewer must be redrawn (either by event or by call) your application model is notified and queried.
Note that the names of the procedure and the names of the parameters can be chosen freely.
ListViewer_create%:
with the following signature:
ListViewer_create%:(colourMode%)
This routine has a single parameter and returns a list viewer object identifier (which must be stored in some integer variable for later use).
The only parameter is colourMode%
which defines
the depth of the window bitmap (typically it is
kGCreate4ColourMode%
). A list viewer has this
property fixed over its lifetime.
The routine returns a list viewer id because it is necessary to identify a list viewer in later routines (as you may have several viewers open at a time).
ListViewer_create%:
does not display a list viewer
yet! Additional steps are still necessary: the callbacks and the
outer and row geometry have to be defined and possibly a scrollbar
can be associated.
ListViewer_linkToClient:(listViewer%, recordCountProc$, redrawRecordProc$, selectRecordProc$)
This routine has (besides the mandatory list viewer as the first parameter) several others as follows:
recordCountProc$
is the name of the procedure to
yield the last record number (the first is by convention
always 1).
redrawRecordProc$
is called by the list viewer to
redraw a row showing a specific record. This routine has two
parameters: the first (a long integer) is the record number to
be redrawn, the second (an integer) is the number of a row
bitmap into which the drawing shall be done. The client
program may draw anything into the row bitmap which is later
on copied into the list viewer.
selectRecordProc$
is called by the list viewer
module when some row for a record has been clicked. This
routine has two parameters: the first (a long integer) is the
record number clicked, the second (an integer) is the x-offset
of the click. Normally the second parameter is not relevant
but it could be when you have some column structure in your
rows.
Also note that your client is responsible for handling the selections. The list viewer has no idea whether a selection may cancel another one or whether multiple selections are o.k. and so on.
ListViewer_setGeometry:
which has the following
signature:
ListViewer_setGeometry%:(listViewer%, x%, y%, width%, height%)
The first parameter is the list viewer
affected. x%
and y%
give the position of
the upper left corner in pixels. width%
is the list
viewer's horizontal extent and height%
its vertical
extent (both dimensions in pixels). Note that the extents include
the border which goes inward, i.e. that the outer size is not
affected by the border width.
Hence to set up a list viewer at (200,0) with a width of 300 pixels and a height of 240 pixels you say:
LOCAL listViewer% listViewer% = ListViewer_create%:(kGCreate4ColourMode%) ListViewer_linkToClient:(listViewer%, "countRecords", "redraw", "select") ListViewer_setGeometry:(listViewer%, 200, 0, 300, 240)
Note that ListViewer_setGeometry:
does still not
display a list viewer! This happens as late as possible to prevent
unnecessary list viewer updates.
ListViewer_setVerticalSizes:
with the following signature:
ListViewer_setVerticalSizes:(listViewer%, borderThickness%, rowHeight%, rowSkip%, separationLineHeight%)
The first parameter is the list viewer
affected. borderThickness%
tells how wide the border
is, rowHeight%
is the vertical size of the drawing
area for a row, rowSkip%
is the vertical size of the
separation area between two rows and finally
separationLineHeight%
is the vertical size of the
horizontal line between two rows. All dimensions are in pixels.
You may draw anything you want in the rows, but normally you
will display text. Then the height of a row must be at least the
font height plus the font descent, otherwise some glyphs might
become clipped. It does not look to good when you directly abutt
those text rows (you also would not in an editor), hence you have
to define some space between the rows: this is the row skip. It is
typically about 30% of the row height. Centered in this area lies
the separation line. Its thickness may be 0 (then you don't see
it) up to rowSkip
(then the complete separation area
is black). A value of one or two pixels is typically o.k.
Based on the above parameters the vertical list viewer layout is as follows:
borderThickness
as its height.
rowSkip/2
.
rowHeight
and below a space of height
rowSkip
which may be filled with the separation
line centered in this space.
rowHeight
, but
ideally only a space of height rowSkip/2
below.
borderThickness
as its height.
Hence ideally a list viewer must have a height of k×(rowHeight+rowSkip)+2×borderHeight. Practically you will not always achieve this. Then some row is partially visible to prevent annoying space at the bottom border.
As always when you set those parameters to new values, this does not take effect immediately! You have to do a redraw first (see below).
ListViewer_setScrollbar:
. This routine has the
following signature:
ListViewer_setScrollbar:(listViewer%, scrollbar%)
The first parameter is as always the list viewer
affected. scrollbar%
is the id of a previously
created scrollbar. The list viewer automatically links itself to
the scrollbar as client, hence any other client linkage of the
scrollbar will be lost.
Why doesn't the list viewer contain a scrollbar automatically? At first I had this solution but in the end it was too inflexible. Do I want the scrollbar to the left of the viewer or to the right or at the bottom? What happens when the list viewer gets resized? What happens when the scrollbar is invisible (because there is nothing to scroll)?
Hence you have to handle the scrollbar by yourself.
ListViewer_draw:(listViewer%)
which visually updates the list viewer to reflect the internal data.
The reason for this design is that you may have many updates in geometry and internal data and do a redraw only when done. Also draw is optimized to redraw only those parts which are invalid.
ListViewer_offer%:(windowID%, pointerType&, x%, y%)
The first parameter tells in which window the event has
happened. The second parameter tells whether the event was a pen
down, pen up or pen drag. Finally x%
and
y%
give the position of the pen event. You can take
all the information from the result of a call to GETEVENT32. It is
identical to the information you would give to a call of
TBarOffer
.
Some further remarks:
ListViewer_setTopRecordNumber:(listViewer%, topRecordNumber&)
As always the first parameter is the list viewer and the second is the new top record number. Note that this routine does no update! You have to explicitely call draw or postpone that until you are done with the settings.
ListViewer_visibleRecords(listViewer%, topRecordNumberPtr&, bottomRecordNumberPtr&)
Note that the second and third parameters are pointers to long integers. Hence you have to call this with addresses of variables like e.g.
LOCAL low&, high& ListViewer_visibleRecords(listViewer%, ADDR(low&), ADDR(high&))
The bottom value returned sometimes seems to be off by
one. This comes from the layout rules of the viewer (see above at
the setGeometry
routine). The bottom row is only considered
unclipped if and only if its contents and at least a white space
of rowSkip/2
is displayed.
ListViewer_close:(listViewer%)
The only parameter is the list viewer identifier (just in case you have more than one list viewer open).
A sample program is included which shows how to use the list viewer module. This program is called ListViewerDemo and simulates a clent of a list viewer. It consists of three dialogs:
After completion of that dialog you can click into the scrollbar or the list viewer window and check the effect. It is also possible to see how zooming works and to see the current visible interval by pressing the tabulator key.
Pressing Esc anytime returns you to the previous dialog or - when you are in the outer dialog- exits the program.
The list viewer module is put into the public domain and is
unsupported. If you think there is some bug in the program you can
contact me by electronic mail . Currently I cannot promise an immediate answer or even
any answer at all.
You may modify the source code but you are not allowed to publish the list viewer code as your own work when the changes are marginal. I don't like being confronted with the new OPL 5 list viewer module by Fredi Klabuster which is nearly 100% my code.
Nevertheless you may use this code freely and of course you don't have to mention me when the list viewer is part of your program.
Have fun,Thomas Tensi, August 2001
You can download the list viewer module for OPL (with source code!) for use in your own programs. A detailed instruction for installation and use is available in the SIS-file as "ListViewer_Doc". Also a tiny demo test driver is included where you can see how to practically use a list viewer.