home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pmos2002.zip / SRC / ROWEDITO.MOD < prev    next >
Text File  |  1996-10-02  |  20KB  |  557 lines

  1. IMPLEMENTATION MODULE RowEditor;
  2.  
  3.         (********************************************************)
  4.         (*                                                      *)
  5.         (*              Screen data capture                     *)
  6.         (*                                                      *)
  7.         (*  Programmer:         P. Moylan                       *)
  8.         (*  Last edited:        2 October 1996                  *)
  9.         (*  Status:                                             *)
  10.         (*      Basic features working.  Known faults are:      *)
  11.         (*        1.    (fixed)                                 *)
  12.         (*        2.    The criterion for deciding in which     *)
  13.         (*              field to start editing could be better. *)
  14.         (*                                                      *)
  15.         (********************************************************)
  16.  
  17. FROM SYSTEM IMPORT
  18.     (* type *)  ADDRESS,
  19.     (* proc *)  ADR;
  20.  
  21. FROM Trace IMPORT
  22.     (* proc *)  InTrace, OutTrace, Pause;
  23.  
  24. FROM Windows IMPORT
  25.     (* type *)  Window,
  26.     (* proc *)  SetCursor, SaveCursor,
  27.                 (* and for debugging: *)
  28.     (* proc *)  OpenSimpleWindow, CloseWindow, WriteLn, WriteString;
  29.  
  30. FROM NumericIO IMPORT                           (* for debugging *)
  31.     (* proc *)  WriteAddress, WriteCard;
  32.  
  33. FROM Keyboard IMPORT
  34.     (* proc *)  InKey, PutBack;
  35.  
  36. FROM FieldEditor IMPORT
  37.     (* type *)  FieldType,
  38.     (* proc *)  WriteField, EditField, SameType, DefineFieldType;
  39.  
  40. FROM Menus IMPORT
  41.     (* type *)  Menu,
  42.     (* proc *)  DisplayMenu, SelectFromMenu;
  43.  
  44. FROM ListEditor IMPORT
  45.     (* type *)  List, ListFormat,
  46.     (* proc *)  WriteList, EditList;
  47.  
  48. FROM Storage IMPORT
  49.     (* proc *)  ALLOCATE, DEALLOCATE;
  50.  
  51. FROM LowLevel IMPORT
  52.     (* proc *)  AddOffset;
  53.  
  54. (************************************************************************)
  55.  
  56. CONST testing = FALSE;
  57.  
  58. CONST Esc = CHR(27);
  59.  
  60. TYPE
  61.     ListPtr = POINTER TO List;
  62.  
  63.     (* The fields in a Field record are:                                *)
  64.     (*          pointer:        address of the variable in this field   *)
  65.     (*          column:         screen position                         *)
  66.     (*          size:           # of char positions to use on screen    *)
  67.     (*          type:           type of the variable                    *)
  68.     (*          left, right:    pointers to adjacent fields             *)
  69.     (* Special case: if size=0 then it is up to the component editor to *)
  70.     (* handle the cursor movement keys and to give the user feedback    *)
  71.     (* on where the cursor is.  This special case arises when the       *)
  72.     (* field has internal structure of its own.                         *)
  73.  
  74.     FieldPointer = POINTER TO FieldRecord;
  75.     FieldRecord =   RECORD
  76.                         pointer: ADDRESS;
  77.                         column: CARDINAL;
  78.                         size: CARDINAL;
  79.                         type: FieldType;
  80.                         left, right: FieldPointer;
  81.                     END (*RECORD*);
  82.  
  83.     StructureRow = FieldPointer;
  84.  
  85.     (* For a menu, the pointer field in the Field record points at a    *)
  86.     (* "MenuHead" record rather than directly at the user variable.     *)
  87.     (* The MenuHead record contains the address of the user variable,   *)
  88.     (* the specification of which Menu to use, and the size of the      *)
  89.     (* space to allocate on the screen.                                 *)
  90.  
  91.     MenuHead =  RECORD
  92.                     address: POINTER TO CARDINAL;
  93.                     menu: Menu;
  94.                     lines, width: CARDINAL;
  95.                 END (*RECORD*);
  96.     MenuPointer = POINTER TO MenuHead;
  97.  
  98.     (* For a linear list, the pointer field in the Field record points  *)
  99.     (* at a "ListHead" record rather than directly at the user          *)
  100.     (* variable.  The ListHead record contains the address of the user  *)
  101.     (* variable (which is itself a pointer to the head of the list),    *)
  102.     (* and the format to use when writing or editing the list.          *)
  103.  
  104.     ListHead =  RECORD
  105.                     address: ListPtr;
  106.                     format: ListFormat;
  107.                 END (*RECORD*);
  108.     ListHeadPtr = POINTER TO ListHead;
  109.  
  110. VAR MenuType, ListType: FieldType;
  111.  
  112. (************************************************************************)
  113. (*                            SCREEN OUTPUT                             *)
  114. (************************************************************************)
  115.  
  116. PROCEDURE WriteMenuField (w: Window;  p: ADDRESS;  dummy: CARDINAL);
  117.  
  118.     VAR headptr: MenuPointer;
  119.  
  120.     BEGIN
  121.         headptr := p;
  122.         WITH headptr^ DO
  123.             DisplayMenu (w, menu, lines, width, address^);
  124.         END (*WITH*);
  125.     END WriteMenuField;
  126.  
  127. (************************************************************************)
  128.  
  129. PROCEDURE WriteListField (w: Window;  p: ADDRESS;  dummy: CARDINAL);
  130.  
  131.     VAR headptr: ListHeadPtr;
  132.  
  133.     BEGIN
  134.         headptr := p;
  135.         WriteList (w, headptr^.address^, headptr^.format);
  136.     END WriteListField;
  137.  
  138. (************************************************************************)
  139.  
  140. PROCEDURE WriteRow (w: Window;  R: StructureRow;  line: CARDINAL);
  141.  
  142.     (* Writes R on row "line" of window w.      *)
  143.  
  144.     BEGIN
  145.         WHILE R <> NIL DO
  146.             WITH R^ DO
  147.                 SetCursor (w, line, column);
  148.                 WriteField (w, pointer, type, size);
  149.             END (*WITH*);
  150.             R := R^.right;
  151.         END (*WHILE*);
  152.     END WriteRow;
  153.  
  154. (************************************************************************)
  155.  
  156. PROCEDURE StartColumn (R: StructureRow): CARDINAL;
  157.  
  158.     (* Returns the screen column of the first field in R.       *)
  159.  
  160.     BEGIN
  161.         RETURN R^.column;
  162.     END StartColumn;
  163.  
  164. (************************************************************************)
  165. (*                      THE BUILT-IN FIELD EDITORS                      *)
  166. (************************************************************************)
  167.  
  168. PROCEDURE EditMenuField (w: Window;  VAR (*INOUT*) p: ADDRESS;
  169.                                                         dummy: CARDINAL);
  170.  
  171.     VAR result, row, col: CARDINAL;  headptr: MenuPointer;
  172.  
  173.     BEGIN
  174.         headptr := p;
  175.         SaveCursor (w, row, col);
  176.         WITH headptr^ DO
  177.             result := SelectFromMenu (menu);
  178.             IF result = 0 THEN
  179.                 SetCursor (w, row, col);
  180.                 DisplayMenu (w, menu, lines, width, address^);
  181.             ELSE
  182.                 address^ := result;
  183.             END (*IF*);
  184.         END (*WITH*);
  185.     END EditMenuField;
  186.  
  187. (************************************************************************)
  188.  
  189. PROCEDURE EditListField (w: Window;  VAR (*INOUT*) p: ADDRESS;
  190.                                                         dummy: CARDINAL);
  191.  
  192.     VAR headptr: ListHeadPtr;
  193.  
  194.     BEGIN
  195.         headptr := p;
  196.         EditList (w, headptr^.address^, headptr^.format);
  197.     END EditListField;
  198.  
  199. (************************************************************************)
  200. (*                 INTRODUCING A NEW FIELD TO THE SYSTEM                *)
  201. (************************************************************************)
  202.  
  203. PROCEDURE NewRow (VariableAddress: ADDRESS;  ftype: FieldType;
  204.                         screencolumn, width: CARDINAL): StructureRow;
  205.  
  206.     (* Creates a new row containing a single field.     *)
  207.  
  208.     VAR result: StructureRow;
  209.  
  210.     BEGIN
  211.         NEW (result);
  212.         WITH result^ DO
  213.             pointer := VariableAddress;
  214.             column := screencolumn;
  215.             size := width;  type := ftype;
  216.             left := NIL;  right := NIL;
  217.         END (*WITH*);
  218.         RETURN result;
  219.     END NewRow;
  220.  
  221. (************************************************************************)
  222.  
  223. PROCEDURE NewMenu (VAR (*IN*) variable: CARDINAL;  M: Menu;
  224.                         screencolumn, rows, columns: CARDINAL): StructureRow;
  225.  
  226.     (* Creates a new row containing a menu field.  The screencolumn     *)
  227.     (* field specifies the leftmost column within the screen window,    *)
  228.     (* the rows and columns fields give the size on the screen.         *)
  229.  
  230.     VAR MP: MenuPointer;
  231.  
  232.     BEGIN
  233.         NEW (MP);
  234.         WITH MP^ DO
  235.             address := ADR (variable);  menu := M;
  236.             lines := rows;  width := columns;
  237.         END (*WITH*);
  238.         RETURN NewRow (MP, MenuType, screencolumn, 0);
  239.     END NewMenu;
  240.  
  241. (************************************************************************)
  242.  
  243. PROCEDURE NewList (VAR (*IN*) variable: List;  f: ListFormat;
  244.                                 screencolumn: CARDINAL): StructureRow;
  245.  
  246.     (* Creates a new row containing a list field.       *)
  247.  
  248.     VAR listhead: ListHeadPtr;
  249.  
  250.     BEGIN
  251.         NEW (listhead);
  252.         WITH listhead^ DO
  253.             address := ADR (variable);  format := f;
  254.         END (*WITH*);
  255.         RETURN NewRow (listhead, ListType, screencolumn, 0);
  256.     END NewList;
  257.  
  258. (************************************************************************)
  259. (*                          TEST PROCEDURES                             *)
  260. (************************************************************************)
  261.  
  262. PROCEDURE DumpField (w: Window;  F: FieldPointer);
  263.  
  264.     (* For debugging: writes a representation of F^ to the screen.      *)
  265.  
  266.     BEGIN
  267.         WriteLn (w);
  268.         WriteAddress (w, F);  WriteString (w, "> ");
  269.         WITH F^ DO
  270.             WriteAddress (w, pointer);  WriteString (w, ", ");
  271.             WriteCard (w, column);  WriteString (w, ", ");
  272.             WriteCard (w, size);  WriteString (w, ", ");
  273.             WriteAddress (w, left);  WriteString (w, ", ");
  274.             WriteAddress (w, right);
  275.         END (*WITH*);
  276.     END DumpField;
  277.  
  278. (************************************************************************)
  279.  
  280. PROCEDURE DumpRow (w: Window;  R: StructureRow);
  281.  
  282.     (* For debugging: writes a representation of R to the screen.       *)
  283.  
  284.     BEGIN
  285.         IF R = NIL THEN
  286.             WriteLn (w);  WriteString (w, "  <empty row>");
  287.         END (*IF*);
  288.         WHILE R <> NIL DO
  289.             DumpField (w, R);  R := R^.right;
  290.         END (*WHILE*);
  291.     END DumpRow;
  292.  
  293. (************************************************************************)
  294.  
  295. PROCEDURE DebugDump (caller: ARRAY OF CHAR;  R: StructureRow);
  296.  
  297.     (* For debugging: identifies the caller and dumps R.        *)
  298.  
  299.     VAR w: Window;
  300.  
  301.     BEGIN
  302.         OpenSimpleWindow (w, 0, 10, 0, 79);
  303.         WriteString (w, "DebugDump called from ");
  304.         WriteString (w, caller);
  305.         DumpRow (w, R);
  306.         Pause;
  307.         CloseWindow (w);
  308.     END DebugDump;
  309.  
  310. (************************************************************************)
  311. (*                CONSTRUCTING COMPLEX STRUCTURE TYPES                  *)
  312. (************************************************************************)
  313.  
  314. PROCEDURE CombineRows (VAR (*INOUT*) A: StructureRow;  B: StructureRow);
  315.  
  316.     (* Merges the row to which B is pointing with the row to which A is *)
  317.     (* pointing, leaving A pointing to the result.  (The structure of   *)
  318.     (* the B row is lost in the process of doing this).                 *)
  319.  
  320.     VAR previous, current: FieldPointer;
  321.  
  322.     BEGIN
  323.         InTrace ("CombineRows");
  324.         previous := NIL;  current := A;
  325.         WHILE B <> NIL DO
  326.  
  327.             (* Find a place to insert the first element on the B list. *)
  328.  
  329.             LOOP
  330.                 IF current = NIL THEN EXIT (*LOOP*) END (*IF*);
  331.                 IF B^.column < current^.column THEN EXIT (*LOOP*) END (*IF*);
  332.                 previous := current;  current := current^.right;
  333.             END (*LOOP*);
  334.  
  335.             (* Take the whole of the B list, insert it after previous^, *)
  336.             (* then take what remains of the original destination list  *)
  337.             (* and call it the B list.  Swapping lists like this is a   *)
  338.             (* little unconventional, but it works, and in many cases   *)
  339.             (* it speeds up the merge.                                  *)
  340.  
  341.             IF previous = NIL THEN
  342.                 A := B;
  343.             ELSE
  344.                 previous^.right := B;
  345.             END (*IF*);
  346.             B^.left := previous;
  347.             previous := B;  B := current;  current := previous^.right;
  348.  
  349.         END (*WHILE*);
  350.         OutTrace ("CombineRows");
  351.     END CombineRows;
  352.  
  353. (************************************************************************)
  354.  
  355. PROCEDURE CopyOfRow (R: StructureRow): StructureRow;
  356.  
  357.     (* Makes a duplicate copy of R.  The variables to be edited are not *)
  358.     (* duplicated; we simply set up a duplicate set of pointers.        *)
  359.  
  360.     VAR result: StructureRow;  newfield: FieldPointer;
  361.         oldheadptr, headptr: ListHeadPtr;
  362.         oldmenuheadptr, menuheadptr: MenuPointer;
  363.  
  364.     BEGIN
  365.         InTrace ("CopyOfRow");
  366.         result := NIL;
  367.         WHILE R <> NIL DO
  368.             NEW (newfield);
  369.             WITH newfield^ DO
  370.                 IF SameType (R^.type, MenuType) THEN
  371.                     oldmenuheadptr := R^.pointer;
  372.                     NEW (menuheadptr);  menuheadptr^ := oldmenuheadptr^;
  373.                     pointer := menuheadptr;
  374.                 ELSIF SameType (R^.type, ListType) THEN
  375.                     oldheadptr := R^.pointer;
  376.                     NEW (headptr);  headptr^ := oldheadptr^;
  377.                     pointer := headptr;
  378.                 ELSE
  379.                     pointer := R^.pointer;
  380.                 END (*IF*);
  381.                 column := R^.column;
  382.                 size := R^.size;  type := R^.type;
  383.                 left := NIL;  right := NIL;
  384.             END (*WITH*);
  385.             CombineRows (result, newfield);
  386.             R := R^.right;
  387.         END (*WHILE*);
  388.         OutTrace ("CopyOfRow");
  389.         IF testing THEN
  390.             DebugDump ("CopyOfRow", result);
  391.         END (*IF*);
  392.         RETURN result;
  393.     END CopyOfRow;
  394.  
  395. (************************************************************************)
  396.  
  397. PROCEDURE DeleteRow (R: StructureRow);
  398.  
  399.     (* Deallocates the storage which was used in setting up row R.      *)
  400.     (* Note that this has nothing to do with the space used by          *)
  401.     (* variables to which R gives access; we delete only the overhead   *)
  402.     (* space which was originally allocated by this module.             *)
  403.  
  404.     VAR temp: FieldPointer;
  405.  
  406.     BEGIN
  407.         InTrace ("DeleteRow");
  408.         WHILE R <> NIL DO
  409.             temp := R^.right;
  410.             IF SameType (R^.type, MenuType) THEN
  411.                 DEALLOCATE (R^.pointer, SIZE(MenuHead));
  412.             ELSIF SameType (R^.type, ListType) THEN
  413.                 DEALLOCATE (R^.pointer, SIZE(ListHead));
  414.             END;
  415.             DISPOSE (R);
  416.             R := temp;
  417.         END (*WHILE*);
  418.         OutTrace ("DeleteRow");
  419.     END DeleteRow;
  420.  
  421. (************************************************************************)
  422.  
  423. PROCEDURE AdjustRow (R: StructureRow;  addroffset, columnoffset: CARDINAL);
  424.  
  425.     (* Adjusts the pointer and column fields of all elements of R       *)
  426.     (* by adding the specified offsets to those fields.                 *)
  427.  
  428.     VAR headptr: ListHeadPtr;  menuheadptr: MenuPointer;
  429.  
  430.     BEGIN
  431.         InTrace ("AdjustRow");
  432.         WHILE R <> NIL DO
  433.             WITH R^ DO
  434.                 IF SameType (type, MenuType) THEN
  435.                     menuheadptr := pointer;
  436.                     WITH menuheadptr^ DO
  437.                         address := AddOffset (address, addroffset);
  438.                     END (*WITH*);
  439.                 ELSIF SameType (type, ListType) THEN
  440.                     headptr := pointer;
  441.                     WITH headptr^ DO
  442.                         address := AddOffset (address, addroffset);
  443.                     END (*WITH*);
  444.                 ELSE
  445.                     pointer := AddOffset (pointer, addroffset);
  446.                 END (*IF*);
  447.                 INC (column, columnoffset);
  448.             END (*WITH*);
  449.             R := R^.right;
  450.         END (*WHILE*);
  451.         OutTrace ("AdjustRow");
  452.     END AdjustRow;
  453.  
  454. (************************************************************************)
  455. (*                         EDITING A STRUCTURE                          *)
  456. (************************************************************************)
  457.  
  458. PROCEDURE SetStartingPoint (VAR (*INOUT*) R: FieldPointer;
  459.                                                 screencolumn: CARDINAL);
  460.  
  461.     (* On entry, R points to the first field in a row.  On exit, R      *)
  462.     (* points to the field in that row whose "column" field most        *)
  463.     (* closely matches the second argument to this procedure.           *)
  464.  
  465.     VAR next: FieldPointer;
  466.  
  467.     BEGIN
  468.         IF R = NIL THEN RETURN END (*IF*);
  469.         LOOP
  470.             next := R^.right;
  471.             IF next = NIL THEN EXIT(*LOOP*) END (*IF*);
  472.             IF 2*screencolumn < R^.column + R^.size + next^.column THEN
  473.                 EXIT (*LOOP*);
  474.             END (*IF*);
  475.             R := next;
  476.         END (*LOOP*);
  477.     END SetStartingPoint;
  478.  
  479. (************************************************************************)
  480.  
  481. PROCEDURE EditRow (w: Window;  R: StructureRow;  screenrow: CARDINAL);
  482.  
  483.     (* Displays structure R in window w, and allows the keyboard user   *)
  484.     (* to edit the components of R.  It is assumed that w is already    *)
  485.     (* open and that R has already been fully defined.                  *)
  486.     (* On return, the screen cursor is at the start of the field        *)
  487.     (* corresponding to the selected record of the structure.  The key  *)
  488.     (* which caused us to return remains available to the caller.       *)
  489.  
  490.     CONST Return = CHR(13);
  491.  
  492.     VAR dummy, screencolumn: CARDINAL;  nextchar: CHAR;
  493.  
  494.     BEGIN
  495.         IF R = NIL THEN
  496.             RETURN;
  497.         END (*IF*);
  498.         SaveCursor (w, dummy, screencolumn);
  499.         WriteRow (w, R, screenrow);
  500.         SetStartingPoint (R, screencolumn);
  501.  
  502.         LOOP
  503.             (* Call the field editor for the current component. *)
  504.  
  505.             WITH R^ DO
  506.                 SetCursor (w, screenrow, column);
  507.                 EditField (w, pointer, type, size);
  508.                 nextchar := InKey();
  509.             END (*WITH*);
  510.  
  511.             (* The Return key has a special function: we interpret it   *)
  512.             (* as "cursor right" if there is another field to the       *)
  513.             (* right, and otherwise we return it to the caller.         *)
  514.  
  515.             IF nextchar = Return THEN
  516.                 IF R^.right <> NIL THEN R := R^.right
  517.                 ELSE EXIT (*LOOP*);
  518.                 END (*IF*);
  519.             ELSIF nextchar <> CHR(0) THEN
  520.                 EXIT (*LOOP*);
  521.             ELSE
  522.  
  523.                 (* Now check for "cursor left" and "cursor right".      *)
  524.                 (* Any other key will be given back to the caller.      *)
  525.  
  526.                 nextchar := InKey();
  527.                 IF nextchar = "K" THEN  (* cursor left *)
  528.                     IF R^.left <> NIL THEN
  529.                         R := R^.left;
  530.                     END (*IF*);
  531.                 ELSIF nextchar = "M" THEN       (* cursor right *)
  532.                     IF R^.right <> NIL THEN
  533.                         R := R^.right;
  534.                     END (*IF*);
  535.                 ELSE
  536.                     PutBack (nextchar);  nextchar := CHR(0);
  537.                     EXIT (*LOOP*)
  538.                 END(*IF*);
  539.  
  540.             END (*IF*);
  541.  
  542.         END (*LOOP*);
  543.  
  544.         PutBack (nextchar);
  545.  
  546.     END EditRow;
  547.  
  548. (************************************************************************)
  549. (*                              MAIN PROGRAM                            *)
  550. (************************************************************************)
  551.  
  552. BEGIN
  553.     MenuType := DefineFieldType (WriteMenuField, EditMenuField);
  554.     ListType := DefineFieldType (WriteListField, EditListField);
  555. END RowEditor.
  556.  
  557.