Programmer's notes on Draw
==========================

(** Bits in brackets like this are remarks and opinions. **)

Source and header files
-----------------------
(An approximate guide)

c files:
 BezierArc  - arc<-->bezier conversion
 Draw       - main interface to WIMP and sundries
 DrawAction - miscellaneous minor functions
 DrawCheck  - Draw file verification
 DrawDispl  - object and metagraphics display
 DrawEdit   - path edit
 DrawEnter  - object entry
 DrawFileIO - loading and saving
 DrawGrid   - grid drawing and snapping
 DrawHelp   - help messsages
 DrawMenu   - menu and key processing
 DrawObject - object construction
 DrawPrint  - printer interface
 DrawScan   - iteration over objects
 DrawSelect - select mode operations
 DrawTextC  - text columns
 DrawTrans  - object transformations
 DrawUndo   - undo handler
 print      - interface to printer driver SWIs (should really have been in
              RISCOSlib)

BezierArc and print are not Draw specific. There are corresponding header
files for most of the above, and in addition h.drawMenuD which contains
symbolic codes for the menu subdivisions used in DrawMenu.

(** The division of functions into c files, and the use of header files is a
bit of a mess at the moment. **)

The principal data structures
-----------------------------
The major data structures are:
 diagrec: "diagrams". Each diagram contains a single picture (Draw file)
plus additional status information. The diagrams are linked in a chain.
 viewrec: "views". There is one view for each window on a diagram. The views
of a diagram are linked in a chain, which is also linked into a diagram.
Views contain a pointer back to the parent diagram. For this reason, some
functions which need both a diagram and a view as parameters are passed just
the view.

It will probably be useful to study the diagrec and viewrec data structures
(and the lower level structures that they point to).

The contents of a diagram (diag) are held in a flex block located at
diag->paper. The 'misc' structure of the diagram contains a number of
pointers into this area, held as offsets from the start of the block. (This
is in case the block moves). 'solidstart' and 'solidlimit' indicate the
start and end of the region containing objects which have been completed.
'ghoststart' and 'ghostlimit' indicate the start and end of the region in
which objects are being constructed. 'stacklimit' is an offset to the end of
a (FD-type) stack of offsets to objects. 'bufferlimit' is the offset of the
end of the paper, i.e. the total space allocated for 'paper'.

Each diagram has a "mainstate" and a "substate". The mainstate indicates
what the current operation on the diagram is:
 entering a path object
 entering a text object
 select mode
 path edit mode
 entering a rectangle
 entering an ellipse
 dragging the zoom box
 setting the printer limits
The substate indicates which step we are at within this. mainstate==substate
means we are idle in the given mode.

Obscure global data
-------------------
Two pieces of global data need explaining:
 draw_displ_scalefactor: a factor, based on the current zoom factor for a
view, which is used in displaying objects and constructions. It is set up
from a viewrec as needed. (** Probably somewhat redundantly. **)

 draw_selection: a structure allocated in a flex block, and containing
information abou the current selection. Specifically, it contains the
selection diagram, an array of offsets for selected objects, the size of
this array, and the current number of entries. The array is always sorted by
offset, and is extended or contracted as required. Note that I have
gradually been changing the code to make references to this go via functions
rather than directly extracting elements from it, with a view to allowing
more than one selection. There are still a number of direct references. I
have not been following this through in the functions in
c.drawSelect itself.

Access to objects
-----------------
Objects are referred to in two ways: either by an offset into the 'paper' of
the diagram, or using a draw_objptr, which is a union of pointers to the
various object types. To form an object pointer from a diagram and an
offset, you do something like:
  draw_objptr hdrptr;
  hdrptr.bytep = diag->paper + offset;
When you are using a pointer, it is important to make sure that you know of
any flex operations that could occur. This is because the paper is a flex
block, and so it could be moved, thus invalidating the pointer. There are a
number of places in the code where the above line of code appears more than
once, apparently redundantly, because there is an intervening call which may
have cause a flex block to move.

You use object pointers to access the structures for the different object
types, by using the appropriate member of the union (e.g. hdrptr.textcolp
for a pointer to a text column). See h.draw for details.

How to add new object types
---------------------------
Suppose you want to add a new object type. What do you do?

 1. Assign a tag value, and add this to the type draw_tagtyp.
 2. Add new data structures for the object, on the pattern of the existing
ones, e.g. draw_textstr, and add new fields for pointers to these in the
union draw_objptr.
 3. Find all the places in the code where a test is made on tag types, and
change these to allow for the new type. Most places already have safe
defaults if a tag is not recognised. Broadly speaking, there are two main
code structures that use tag types: switch statements, and 'despatch
tables', of the sort used by the drawScan functions. It is probably easiest
to find the places by doing a search for existing references to draw_OBJ:
this will find all of the switches and other places where tags are used.
Then do a search for all occurences of the type 'despatch_tab'. You will
also need to add the type into the table driven parts of drawCheck.
 4. If the object type has any restylable attributes, you will probably also
want to change the restyle menu: see the next section on adding new
operations.
 5. Update the file format version numbers in h.draw. The change and the new
object type should also be registered in the reference document (in net#arf
somewhere?).

How to add new operations
-------------------------
Suppose you want to add a new operation. What do you do?

 1. Decide on a place in the menus. Record this (changing other values as
necessary) in h.drawMenuD.
 2. Add appropriate tests to create the menu and set the menu tick and fade
flags in c.drawMenu.
 3. Add the new menu entry into draw_menu_proc in c.drawMenu. If the new
entry is on some of the submenus (e.g. select or enter), the code for
handling it is in a separate function called from draw_menu_proc.
 4. If you want to add a keystroke equivalent, add it into
draw_menu_processkeys. Note that you MUST have a check in this case that the
operation is valid. (The check is not needed when you select an operation
from the menu, provided you have written the code for fading the entry when
the operation is not valid: much of the existing code rechecks it anyway).

Scan operations
---------------
c.drawScan contains a number of functions for iterating over objects. They
are used principally for the transformation operations, such as translation,
rotating and scaling. The functions take start and end pointers as
parameters. As explained in the header file, these can be used to iterate
over the following:
 selected objects
 a single object
 the objects within a group
 a range of objects

You also supply a 'despatch table', which for each object specified a
function to be applied to it. The function may be NULL: see h.drawScan for
what this means.

Note that the drawScan routines work by keeping address pointers for objects
rather than offsets. So if any of the routines called by them causes flex
block movement, they will fail. (** This could be improved by a bit of
judicious rewriting **).

Undo
----
The undo code was originally based on the code from Edit, but is now
somewhat more complex. The basic difference is that whereas each undo
operation put by Edit is complete in itself, Draw puts several undo entries
in a buffer to reverse a single operation. A batch of entries like this is
called a 'chunk'. Each chunk is headed by a special undo element called a
'major separator'. There is no corresponding end marker. The major separator
contains information about where the size of the chunk. When an undo is
carried out, information for redoing the operation is also placed in the
buffer, with a special element called a 'minor separator' to mark its start
point. Note that the buffer is circular, with offsets indicating the head
and tail of the used area.

I believe the code in c.drawUndo to be reasonably solid now, but it could
perhaps be improved. Some of the pointers into the undo buffer can take on
special values to indicate certain states (in particular distinguishing a
completely empty buffer from an exactly full one, which was the source of
the last major bug in the undo code to be solved): it would be nice to sort
out the various tricks like this which work but are inelegant.

Relationship with DrawFile
--------------------------
RISCOSlib (in C release 3) contains a number of functions for creating,
manipulating and displaying Draw files. This code was derived from the
source of Draw version 0.44, and development has continued independently
since then. There have been only a few changes to DrawFile, but Draw's own
code has developed. The two are therefore a little out of step: it may be
worth resynchronising them in future releases of RISCOSlib.

(** One thing that could be done is to put lots of #ifdefs into the Draw
sources that contribute for DrawFile, and use the preprocessor to extract
one version or another. The result might be a bit messy, though **).

-- David Elworthy, 20th Sepember 1989
