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

  1. IMPLEMENTATION MODULE ListEditor;
  2.  
  3.         (********************************************************)
  4.         (*                                                      *)
  5.         (*              "Generic" list editor                   *)
  6.         (*                                                      *)
  7.         (*  Programmer:         P. Moylan                       *)
  8.         (*  Last edited:        2 October 1996                  *)
  9.         (*  Status:             Seems OK                        *)
  10.         (*                                                      *)
  11.         (********************************************************)
  12.  
  13. FROM SYSTEM IMPORT
  14.     (* type *)  ADDRESS;
  15.  
  16. FROM FieldEditor IMPORT
  17.     (* type *)   FieldType,
  18.     (* proc *)  WriteField, EditField;
  19.  
  20. FROM Windows IMPORT
  21.     (* type *)  Window, ColumnRange,
  22.     (* proc *)  SaveCursor, SetCursor, WriteString;
  23.  
  24. FROM Storage IMPORT
  25.     (* proc *)  ALLOCATE, DEALLOCATE;
  26.  
  27. FROM Keyboard IMPORT
  28.     (* proc *)  InKey, PutBack;
  29.  
  30. (************************************************************************)
  31.  
  32. CONST CursorLeftCode = "K";  CursorRightCode = "M";
  33.  
  34. TYPE
  35.  
  36.     String = ARRAY ColumnRange OF CHAR;
  37.  
  38.     (* The following definition shows the general structure of the sort *)
  39.     (* of lists which we can handle.                                    *)
  40.  
  41.     InternalList = POINTER TO InternalListElement;
  42.     InternalListElement = RECORD
  43.                                 next: InternalList;
  44.                                 component: ADDRESS;
  45.                           END (*RECORD*);
  46.  
  47.     (* Details needed for displaying a list on the screen.      *)
  48.  
  49.     ListFormat = POINTER TO ListFormatRecord;
  50.     ListFormatRecord =  RECORD
  51.                            header, separator, trailer: String;
  52.                            ctype: FieldType;
  53.                         END (*RECORD*);
  54.  
  55. (************************************************************************)
  56. (*                      DEFINING A NEW LIST FORMAT                      *)
  57. (************************************************************************)
  58.  
  59. PROCEDURE CopyString (source: ARRAY OF CHAR;  VAR (*OUT*) destination: String);
  60.  
  61.     (* Copies the source string to the destination string.  Care is     *)
  62.     (* needed here in getting the terminator right, because of the      *)
  63.     (* strange way the FTL compiler deals with string constants.        *)
  64.  
  65.     VAR j, last: ColumnRange;
  66.  
  67.     BEGIN
  68.         last := MAX(ColumnRange);
  69.         IF last > HIGH(source) THEN
  70.             last := HIGH(source);
  71.             destination[last+1] := CHR(0);
  72.         END (*IF*);
  73.         FOR j := 0 TO last DO
  74.             destination[j] := source[j];
  75.         END (*FOR*);
  76.     END CopyString;
  77.  
  78. (************************************************************************)
  79.  
  80. PROCEDURE DefineListFormat (s1, s2, s3: ARRAY OF CHAR;
  81.                                 ComponentType: FieldType): ListFormat;
  82.  
  83.     (* Sets up the output format for a class of lists.  *)
  84.  
  85.     VAR result: ListFormat;
  86.  
  87.     BEGIN
  88.         NEW (result);
  89.         WITH result^ DO
  90.             CopyString (s1, header);
  91.             CopyString (s2, separator);
  92.             CopyString (s3, trailer);
  93.             ctype := ComponentType;
  94.         END (*WITH*);
  95.         RETURN result;
  96.     END DefineListFormat;
  97.  
  98. (************************************************************************)
  99.  
  100. PROCEDURE DiscardFormat (format: ListFormat);
  101.  
  102.     (* A notification from the user that this format will not be used   *)
  103.     (* again (unless it is redefined by another call to procedure       *)
  104.     (* DefineListFormat).  Use of this procedure is optional, but is    *)
  105.     (* recommended for the sake of "clean" memory management.           *)
  106.  
  107.     BEGIN
  108.         DISPOSE (format);
  109.     END DiscardFormat;
  110.  
  111. (************************************************************************)
  112. (*                           SCREEN OUTPUT                              *)
  113. (************************************************************************)
  114.  
  115. PROCEDURE WriteTail (w: Window;  L: InternalList;  format: ListFormat);
  116.  
  117.     (* Writes L on the screen, without its header string.  The screen   *)
  118.     (* cursor is left at the character position following the trailer.  *)
  119.  
  120.     BEGIN
  121.         IF L <> NIL THEN
  122.             LOOP
  123.                 WriteField (w, L^.component, format^.ctype, 0);
  124.                 L := L^.next;
  125.                 IF L = NIL THEN EXIT(*LOOP*) END(*IF*);
  126.                 WriteString (w, format^.separator);
  127.             END (*LOOP*);
  128.         END (*IF*);
  129.         WriteString (w, format^.trailer);
  130.     END WriteTail;
  131.  
  132. (************************************************************************)
  133.  
  134. PROCEDURE WriteList (w: Window;  L: List;  format: ListFormat);
  135.  
  136.     (* Writes L on the screen, including its delimiters.  This          *)
  137.     (* procedure is not actually used in this module, but is provided   *)
  138.     (* as something that a client module may find useful.               *)
  139.  
  140.     BEGIN
  141.         WriteString (w, format^.header);
  142.         WriteTail (w, L, format);
  143.     END WriteList;
  144.  
  145. (************************************************************************)
  146. (*                           KEYBOARD INPUT                             *)
  147. (************************************************************************)
  148.  
  149. PROCEDURE FunctionKey (code: CHAR): BOOLEAN;
  150.  
  151.     (* Checks for CHR(0) followed by the given code from the keyboard.  *)
  152.     (* If found, returns TRUE.  If not found, returns FALSE and no      *)
  153.     (* keyboard input is consumed.                                      *)
  154.  
  155.     VAR ch: CHAR;
  156.  
  157.     BEGIN
  158.         ch := InKey();
  159.         IF ch <> CHR(0) THEN
  160.             PutBack (ch);  RETURN FALSE;
  161.         END (*IF*);
  162.         ch := InKey();
  163.         IF ch <> code THEN
  164.             PutBack (ch);  PutBack (CHR(0));  RETURN FALSE;
  165.         END (*IF*);
  166.         RETURN TRUE;
  167.     END FunctionKey;
  168.  
  169. (************************************************************************)
  170. (*                                EDITING                               *)
  171. (************************************************************************)
  172. (*                                                                      *)
  173. (*  Each of the editing procedures in this module has the properties:   *)
  174. (*                                                                      *)
  175. (*   1. We leave the procedure on seeing a keyboard character which     *)
  176. (*      does not belong to us.  The terminating keystroke is returned   *)
  177. (*      to the keyboard driver so that it can still be read by the      *)
  178. (*      caller.                                                         *)
  179. (*   2. The window cursor is left just beyond the "trailer" string      *)
  180. (*      which terminates the written form of the list.                  *)
  181. (*   3. The list being edited may be empty on entry and/or exit.        *)
  182. (*                                                                      *)
  183. (*  The differences among the procedures are in terms of the starting   *)
  184. (*  point on the screen - that is, whether we start by writing a list   *)
  185. (*  separator, a component, or a header string.                         *)
  186. (*                                                                      *)
  187. (************************************************************************)
  188.  
  189. PROCEDURE EditFromSeparator (w: Window;  VAR (*INOUT*) L: InternalList;
  190.                                         format: ListFormat);  FORWARD;
  191.  
  192. (************************************************************************)
  193.  
  194. PROCEDURE EditFromComponent (w: Window;  VAR (*INOUT*) L: InternalList;
  195.                                                 format: ListFormat);
  196.  
  197.     (* This procedure is the one which does most of the real work.      *)
  198.     (* On entry, the screen cursor is at the place where the first      *)
  199.     (* component must be written, i.e. the header or separator, as      *)
  200.     (* appropriate, has already been written.  The screen display is    *)
  201.     (* correct on exit in all cases except where an empty list is       *)
  202.     (* returned and the caller had placed a separator in front of the   *)
  203.     (* displayed list.  In that case, it is the caller's responsibility *)
  204.     (* to get rid of the separator.                                     *)
  205.  
  206.     VAR temp: InternalList;
  207.         startrow, newrow, startcolumn, newcolumn: CARDINAL;
  208.  
  209.     BEGIN
  210.  
  211.         (* If we are at the end of a list, extend it to allow a new     *)
  212.         (* component to be added.  (If the user does not take us up on  *)
  213.         (* that offer, the new element will be deleted - see below.)    *)
  214.  
  215.         IF L = NIL THEN
  216.             NEW (L);
  217.             WITH L^ DO
  218.                 next := NIL;  component := NIL;
  219.             END (*WITH*);
  220.         END (*IF*);
  221.  
  222.         SaveCursor (w, startrow, startcolumn);
  223.         WriteTail (w, L, format);
  224.  
  225.         LOOP
  226.             SetCursor (w, startrow, startcolumn);
  227.  
  228.             (* Edit the current list component.  Assumption: the        *)
  229.             (* component editor will leave the window cursor at the     *)
  230.             (* character position beyond the end of that component.     *)
  231.  
  232.             EditField (w, L^.component, format^.ctype, 0);
  233.  
  234.             (* If the user deleted this component - either as an        *)
  235.             (* explicit deletion or by ignoring the invitation to add a *)
  236.             (* new component - discard the corresponding list element.  *)
  237.  
  238.             IF L^.component = NIL THEN
  239.                 temp := L;  L := L^.next;  DISPOSE (temp);
  240.                 SetCursor (w, startrow, startcolumn);
  241.                 WriteTail (w, L, format);
  242.  
  243.                 (* If the list is now empty, even after we have given   *)
  244.                 (* the user the chance to extend it, we can conclude    *)
  245.                 (* that the user has finished editing this list.        *)
  246.  
  247.                 IF L = NIL THEN
  248.                     EXIT (*LOOP*);
  249.                 END (*IF*);
  250.  
  251.                 (* If the user wants to move right at this stage,       *)
  252.                 (* consume one "cursor right" keypress, because we are  *)
  253.                 (* already at the element which used to be to the right *)
  254.                 (* of the now-deleted one.                              *)
  255.  
  256.                 IF FunctionKey (CursorRightCode) THEN
  257.                     (* do nothing with the keypress     *)
  258.                 END (*IF*);
  259.  
  260.             ELSE        (* component <> NIL *)
  261.  
  262.                 (* Just in case the component editor has messed up      *)
  263.                 (* some of the screen display, write a clean copy.      *)
  264.  
  265.                 SaveCursor (w, newrow, newcolumn);
  266.                 SetCursor (w, startrow, startcolumn);
  267.                 WriteTail (w, L, format);
  268.  
  269.                 (* At this stage, a "cursor right" means that we should *)
  270.                 (* move on to the next group, and any other key should  *)
  271.                 (* be given to the caller to deal with.                 *)
  272.  
  273.                 IF NOT FunctionKey (CursorRightCode) THEN
  274.                     EXIT (*LOOP*);
  275.                 END (*IF*);
  276.  
  277.                 (* We have indeed received a "cursor right" code.       *)
  278.  
  279.                 SetCursor (w, newrow, newcolumn);
  280.                 EditFromSeparator (w, L^.next, format);
  281.                 IF NOT FunctionKey (CursorLeftCode) THEN
  282.                     EXIT (*LOOP*)
  283.                 END (*IF*);
  284.  
  285.             END (*IF*);
  286.  
  287.         END (*LOOP*);
  288.  
  289.     END EditFromComponent;
  290.  
  291. (************************************************************************)
  292.  
  293. PROCEDURE EditFromSeparator (w: Window;  VAR (*INOUT*) L: InternalList;
  294.                                                 format: ListFormat);
  295.  
  296.     (* On entry, the screen cursor is at the place where the separator  *)
  297.     (* must be written, or the trailer in the case L = NIL.  The case   *)
  298.     (* L = NIL is possible on entry and/or exit.  On exit, the screen   *)
  299.     (* display has been correctly updated.                              *)
  300.  
  301.     VAR startrow, startcolumn: CARDINAL;
  302.  
  303.     BEGIN
  304.         SaveCursor (w, startrow, startcolumn);
  305.  
  306.         (* Write the separator, and do the actual editing.      *)
  307.  
  308.         WriteString (w, format^.separator);
  309.         EditFromComponent (w, L, format);
  310.  
  311.         (* If we are left with an empty list, overwrite the redundant   *)
  312.         (* separator.                                                   *)
  313.  
  314.         IF L = NIL THEN
  315.             SetCursor (w, startrow, startcolumn);
  316.             WriteString (w, format^.trailer);
  317.         END (*IF*);
  318.  
  319.     END EditFromSeparator;
  320.  
  321. (************************************************************************)
  322.  
  323. PROCEDURE EditList (w: Window;  VAR (*INOUT*) L: List;  format: ListFormat);
  324.  
  325.     (* Edits a list at the current cursor position in window w.  We     *)
  326.     (* leave this procedure on seeing a keyboard character which does   *)
  327.     (* not belong to us.  The terminating keystroke is returned to the  *)
  328.     (* keyboard driver so that it can still be read by the caller.      *)
  329.     (* The window cursor is left just beyond the "trailer" string which *)
  330.     (* terminates the written form of the list.                         *)
  331.  
  332.     VAR Alias: InternalList;
  333.  
  334.     BEGIN
  335.         Alias := L;
  336.         WriteString (w, format^.header);
  337.         EditFromComponent (w, Alias, format);
  338.         L := Alias;
  339.     END EditList;
  340.  
  341. (************************************************************************)
  342.  
  343. END ListEditor.
  344. 
  345.