home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / apps / spread / opusprg / opussrc / gl.pas < prev    next >
Pascal/Delphi Source File  |  1988-05-18  |  66KB  |  1,792 lines

  1.  
  2.  
  3. {$M+}
  4. {$E+}
  5. PROGRAM Mock;
  6.  
  7. {$I i:\opus.i}
  8. {$I i:\gctv.inc}
  9.  
  10. {$I i:\vdi_aes.def}
  11. {$I i:\gemsubs.def}
  12. {$I i:\auxsubs.def}
  13. {$I d:\pascal\opus\graphout.def}
  14.  
  15. PROCEDURE REAL_TO_STRING ( real_num: REAL; VAR string_real: STRING;
  16.                            digits: INTEGER; sci_not: BOOLEAN );
  17.    EXTERNAL;
  18. FUNCTION STRING_TO_REAL ( VAR string_real : STR30 ) : REAL;
  19.    EXTERNAL;
  20. PROCEDURE INT_TO_STRING ( a : INTEGER; VAR b : STR10 );
  21.    EXTERNAL;
  22.  
  23. PROCEDURE HANDLE_MESSAGE;
  24.    EXTERNAL;
  25.  
  26. PROCEDURE EVALUATE_FORMULA ( row,col  : INTEGER;
  27.                              force,
  28.                              new_form : BOOLEAN;
  29.                              cell     : CellPtr );
  30.    EXTERNAL;
  31.  
  32. FUNCTION REQUEST_MEMORY ( what : ReqType ) : BOOLEAN;
  33.    FORWARD;
  34. PROCEDURE INIT_CELL ( what : CellPtr; row,col : INTEGER );
  35.    FORWARD;
  36. FUNCTION LOCATE_CELL ( row,col : INTEGER ) : CellPtr;
  37.    FORWARD;
  38. FUNCTION NEW_CELL ( row,col : INTEGER ) : CellPtr;
  39.    FORWARD;
  40. PROCEDURE DELETE_CELL ( row,col    : INTEGER; 
  41.                         total_kill : BOOLEAN  );
  42.    FORWARD;
  43. PROCEDURE FIND_SCREEN_POS (     row,col             : INTEGER;
  44.                             VAR l_scr_row,l_scr_col : INTEGER );
  45.    FORWARD;
  46. PROCEDURE SAVE_ATTR;
  47.    FORWARD;
  48. PROCEDURE RETURN_ATTR;
  49.    FORWARD;
  50. PROCEDURE CELL_ON_SCREEN ( draw_or_toggle,row,col : INTEGER; force : BOOLEAN );
  51.    FORWARD;
  52. PROCEDURE STRING_A_CELL ( row,col : INTEGER; VAR temp : STR10 );
  53.    FORWARD;
  54. PROCEDURE OUT_MEM_CELL ( row,col : INTEGER; specific : STR10 );
  55.    FORWARD;
  56. FUNCTION REL_OVERFLOW ( row,col : INTEGER; VAR what : STR10 ) : INTEGER;
  57.    FORWARD;
  58. PROCEDURE FREE_DEP_LIST ( ptr : CellPtr );
  59.    FORWARD;
  60. FUNCTION LIST_END ( ptr : CellPtr ) : DepPtr;
  61.    FORWARD;
  62. FUNCTION DUPLICATING ( dep_row,dep_col : INTEGER; ptr : CellPtr ) : BOOLEAN;
  63.    FORWARD;
  64. PROCEDURE LIST_INSERT ( fx_row,fx_col,dep_row,dep_col : INTEGER );
  65.    FORWARD;
  66. PROCEDURE LIST_DELETE ( fx_row,fx_col,dep_row,dep_col : INTEGER );
  67.    FORWARD;
  68. PROCEDURE STRIP_NUM ( VAR num_str : LorFstr;
  69.                       VAR str     : LorFstr;
  70.                       VAR str_pos,
  71.                           len     : INTEGER );
  72.    FORWARD;
  73. FUNCTION VALID_COL_NAME ( VAR temp       : STR10;
  74.                           VAR col_number : INTEGER ) : BOOLEAN;
  75.    FORWARD;
  76. PROCEDURE GET_COL ( VAR str : LorFstr; VAR str_pos : INTEGER;
  77.                         len : INTEGER; VAR col : INTEGER;
  78.                     VAR col_rel : BOOLEAN; VAR status : StatusType );
  79.    FORWARD;
  80. PROCEDURE GET_ROW ( VAR str : LorFstr; VAR str_pos : INTEGER;
  81.                         len : INTEGER; VAR row : INTEGER;
  82.                     VAR row_rel : BOOLEAN; VAR status : StatusType );
  83.    FORWARD;
  84. FUNCTION TRANSLATE_CELL ( VAR str      : LorFstr;    { cell_str or formula  }
  85.                           VAR str_pos  : INTEGER;    { position; 1 for cell }
  86.                               len      : INTEGER;    { length of string     }
  87.                           VAR row,col  : INTEGER;
  88.                           VAR row_rel,               { relative reference?  }
  89.                               col_rel  : BOOLEAN ) : StatusType;
  90.    FORWARD;
  91. FUNCTION SCAN_FOR_CELLS ( VAR str       : LorFstr; 
  92.                           VAR str_pos   : INTEGER;
  93.                               len       : INTEGER;
  94.                           VAR cell_pos  : INTEGER;    
  95.                           VAR row,col   : INTEGER;
  96.                           VAR row_rel,
  97.                               col_rel   : BOOLEAN   ) : BOOLEAN;
  98.    FORWARD;
  99. FUNCTION ADJUST_EXPR (  action             : INTEGER;  { add,remove, }
  100.                         ptr                : CellPtr;  { adj_refs    }
  101.                         src_row,src_col,
  102.                         dest_row,dest_col,
  103.                         row_st,col_st,
  104.                         row_end,col_end    : INTEGER  ) : StatusType;
  105.    FORWARD;
  106. PROCEDURE ALL_LISTS ( action : INTEGER; ptr : CellPtr; row,col : INTEGER );
  107.    FORWARD;
  108. PROCEDURE DEFAULT_DRAW_ATTRIBUTES;
  109.    FORWARD;
  110. PROCEDURE REDRAW_MESSAGE ( hdl,x,y,w,h : INTEGER );
  111.    FORWARD;
  112. PROCEDURE Send_Redraw ( all_windows : BOOLEAN;
  113.                         x,y,w,h     : INTEGER  );
  114.    FORWARD;
  115. PROCEDURE ADJUST_MENU ( enable : BOOLEAN );
  116.    FORWARD;
  117. FUNCTION FIND_PREC ( ptr : CellPtr ) : INTEGER;
  118.    FORWARD;
  119. FUNCTION FIND_JUST ( ptr : CellPtr ) : VDI_Just;
  120.    FORWARD;
  121. FUNCTION ASSIGNED ( row,col : INTEGER; VAR ptr : CellPtr ) : AssignedStatus;
  122.    FORWARD;
  123. FUNCTION VALID_NUMBER ( VAR num_str : LorFstr ) : StatusType;
  124.    FORWARD;
  125. PROCEDURE PREPARE_NUM ( ptr : CellPtr; VAR temp : STRING );
  126.    FORWARD;
  127. PROCEDURE MASK_OUT_RECALC;
  128.    FORWARD;
  129. FUNCTION ASSIGN ( VAR temp : LorFstr ) : CellPtr;
  130.    FORWARD;
  131. FUNCTION SIZE ( row,col : INTEGER ) : INTEGER;
  132.    FORWARD;
  133. FUNCTION COMP_ASSIGN ( src_row,src_col,dest_row,dest_col : INTEGER;
  134.                        build                             : BOOLEAN ) : BOOLEAN;
  135.    FORWARD;
  136. PROCEDURE DELETE_RANGE ( s_row,s_col,f_row,f_col : INTEGER; draw : BOOLEAN );
  137.    FORWARD;
  138. PROCEDURE CLEAR_WORKSHEET;
  139.    FORWARD;
  140. PROCEDURE SIMULATE_MESSAGE ( msg_type,three,four : INTEGER );
  141.    FORWARD;
  142. PROCEDURE HOME_CURSOR ( extent : HomeType );
  143.    FORWARD;
  144. PROCEDURE MY_LINE_STYLE ( style : INTEGER );
  145.    FORWARD;
  146. PROCEDURE SWITCH_WINDOW;
  147.    FORWARD;
  148. PROCEDURE DEP_RECALC ( dep : DepPtr );
  149.    FORWARD;
  150. PROCEDURE CLEAR_BUFFER;
  151.    FORWARD;
  152. FUNCTION FIND_FIRST_AND_LAST ( virtual_or_actual : BOOLEAN ) : BOOLEAN;
  153.    FORWARD;
  154. PROCEDURE BLOCK_TOO_BIG ( col,row : STR10 );
  155.    FORWARD;
  156. PROCEDURE HIDE;
  157.    FORWARD;
  158. PROCEDURE UNHIDE ( menu : Tree_Index );
  159.    FORWARD;
  160.  
  161. FUNCTION MOUSE_ROW_COL ( mouse_x,mouse_y     : INTEGER;
  162.                          VAR new_row,new_col : INTEGER ) : BOOLEAN;
  163.    { gives the data[x,y] positions of the cell encompassing the area
  164.      containing the coordinates mouse,x,mouse_y; returns true if within a
  165.      cell. passes back data[x,y] in new_row,new_col. Used by OPUS.PAS and
  166.      window_input }
  167.    VAR i,j           : INTEGER;
  168.        row_ok,col_ok : BOOLEAN;  
  169.    BEGIN
  170.        row_ok := FALSE;
  171.        col_ok := FALSE;
  172.        j := y_1+y_margin;
  173.        i := start_row;
  174.        WHILE i <= finish_row DO BEGIN
  175.           IF (mouse_y > j) AND
  176.              (mouse_y < j+cell_height) THEN BEGIN
  177.              new_row := i;
  178.              row_ok := TRUE;
  179.              i := finish_row
  180.           END;
  181.           j := j+cell_height;
  182.           i := i+1
  183.       END;
  184.       j := 1;
  185.       i := start_col;
  186.       WHILE i <= finish_col DO BEGIN
  187.          IF (mouse_x > vert_grid[j]+4) AND
  188.             (mouse_x < vert_grid[j+1]-4) THEN BEGIN
  189.             new_col := i;
  190.             col_ok := TRUE;
  191.             i := finish_col
  192.          END;
  193.          j := j+1;
  194.          i := i+1
  195.       END;
  196.       IF (row_ok) AND (col_ok) THEN
  197.          mouse_row_col := TRUE
  198.       ELSE
  199.          mouse_row_col := FALSE
  200.    END; { MOUSE_ROW_COL }
  201.    
  202. (*************************************************)
  203. (*  Functions to manipulate main data structure  *)
  204. (*************************************************)
  205.  
  206. FUNCTION REQUEST_MEMORY;
  207.    VAR resulting_free_mem : LONG_INTEGER;
  208.    BEGIN
  209.        IF what = ACell THEN
  210.           resulting_free_mem := working_memory-cell_size
  211.        ELSE
  212.           resulting_free_mem := working_memory-str_size;
  213.        IF resulting_free_mem < 0 THEN BEGIN
  214.           alert := Do_Alert (
  215.              '[1][Running out of memory.|Request denied...][  OK  ]',1 );
  216.           request_memory := FALSE
  217.        END
  218.        ELSE BEGIN
  219.           working_memory := resulting_free_mem;
  220.           request_memory := TRUE
  221.        END
  222.    END; { REQUEST_MEMORY }
  223.  
  224. FUNCTION LOCATE_CELL;
  225.    { searches for a cell in a given row; if it exists, returns the address,
  226.      otherwise returns NIL }
  227.    VAR found,passed : BOOLEAN;
  228.        ptr          : CellPtr;
  229.    BEGIN
  230.        ptr := data[row];
  231.        found := FALSE;
  232.        passed := FALSE;
  233.        WHILE (ptr <> NIL) AND (NOT found) AND (NOT passed) DO
  234.           IF ptr^.c = col THEN
  235.              found := TRUE
  236.           ELSE IF ptr^.c > col THEN
  237.              passed := TRUE
  238.           ELSE
  239.              ptr := ptr^.next;
  240.        IF found THEN
  241.           locate_cell := ptr
  242.        ELSE
  243.           locate_cell := NIL
  244.    END; { LOCATE_CELL }
  245.  
  246. PROCEDURE INIT_CELL;
  247.    { called by NEW_CELL; does NOT handle adjustment of pointers }
  248.    BEGIN
  249.        WITH what^ DO BEGIN
  250.           c := col;
  251.           class := Val;
  252.           num := 0;
  253.           format := default_format;
  254.           status := Empty;
  255.           str := NIL;
  256.           sub := NIL;
  257.           next := NIL
  258.        END
  259.    END; { INIT_CELL }
  260.  
  261. FUNCTION NEW_CELL;
  262.    { creates a new cell or if the cell already exists, returns the address. If
  263.      not enough mem, returns NIL }
  264.    VAR found          : BOOLEAN;
  265.        dumbo,temp,ptr : CellPtr;
  266.    BEGIN
  267.        ptr := locate_cell(row,col);
  268.        IF ptr = NIL THEN
  269.           IF request_memory(ACell) THEN BEGIN
  270.              ptr := data[row];
  271.              found := FALSE;
  272.              IF ptr <> NIL THEN 
  273.                 IF ptr^.c > col THEN BEGIN
  274.                    NEW(dumbo);
  275.                    init_cell(dumbo,row,col);
  276.                    data[row] := dumbo;
  277.                    data[row]^.next := ptr;
  278.                    new_cell := dumbo
  279.                 END
  280.                 ELSE BEGIN   
  281.                    WHILE (ptr^.next <> NIL) AND (NOT found) DO
  282.                       IF ptr^.next^.c > col THEN
  283.                          found := TRUE
  284.                       ELSE
  285.                          ptr := ptr^.next;
  286.                    temp := ptr^.next; { save cell addr to follow new one or NIL }
  287.                    NEW(dumbo);
  288.                    init_cell(dumbo,row,col);
  289.                    ptr^.next := dumbo;
  290.                    new_cell := dumbo;
  291.                    ptr^.next^.next := temp
  292.                 END
  293.              ELSE BEGIN
  294.                 NEW(data[row]);
  295.                 new_cell := data[row];
  296.                 init_cell(data[row],row,col)
  297.              END
  298.           END
  299.           ELSE
  300.              new_cell := NIL
  301.        ELSE
  302.           new_cell := ptr
  303.    END; { NEW_CELL }
  304.  
  305. PROCEDURE DELETE_CELL;
  306.    { removes a cell from the sheet; i.e. a list. However, if the cell has
  307.      dependents, the cell won't be deallocated unless total_kil = TRUE or if
  308.      it already has a NIL dep list }
  309.    VAR i          : INTEGER;
  310.        found      : BOOLEAN;
  311.        dep        : DepPtr;
  312.        ptr,temp   : CellPtr;
  313.    BEGIN
  314.        found := FALSE;
  315.        ptr := locate_cell(row,col);
  316.        IF ptr <> NIL THEN
  317.           all_lists(remove,ptr,row,col);
  318.        { now, all_lists may have removed a cell in front of and
  319.          directly pointing to this cell; this can happen if in the
  320.          cell to have its dep list modified, it turned out that 
  321.          sub = NIL and status = Empty. Thus, ptr^.next no longer
  322.          points to OUR cell, since "ptr" is no longer defined. So, do the 
  323.          all_lists call first. }
  324.        ptr := data[row];
  325.        IF ptr <> NIL THEN BEGIN
  326.           IF ptr^.c <> col THEN BEGIN
  327.              WHILE (ptr^.next <> NIL) AND (NOT found) DO
  328.                 IF ptr^.next^.c = col THEN
  329.                    found := TRUE
  330.                 ELSE
  331.                    ptr := ptr^.next;
  332.              { ptr^.next will represent the desired cell, if found, and
  333.                of course will be non-NIL }
  334.              IF found THEN BEGIN
  335.                 IF ptr^.next^.str <> NIL THEN BEGIN
  336.                    DISPOSE(ptr^.next^.str);
  337.                    ptr^.next^.str := NIL;
  338.                    working_memory := working_memory+str_size
  339.                 END;
  340.                 IF total_kill THEN           { so only destroy dep list if }
  341.                    free_dep_list(ptr^.next); { clearing wks }
  342.                 IF ptr^.next^.sub = NIL THEN BEGIN { no point in keeping the }
  343.                    working_memory := working_memory+cell_size; { cell around }
  344.                    temp := ptr^.next^.next;
  345.                    DISPOSE(ptr^.next);
  346.                    ptr^.next := temp
  347.                 END
  348.                 ELSE
  349.                    ptr^.next^.status := Empty
  350.              END { IF found }
  351.           END { IF ptr^.c <> col }
  352.           ELSE BEGIN { first cell in list }
  353.              found := TRUE;
  354.              IF ptr^.str <> NIL THEN BEGIN
  355.                 DISPOSE(ptr^.str);
  356.                 ptr^.str := NIL;
  357.                 working_memory := working_memory+str_size
  358.              END;
  359.              IF total_kill THEN
  360.                 free_dep_list(ptr);
  361.              IF ptr^.sub = NIL THEN BEGIN
  362.                 working_memory := working_memory+cell_size;
  363.                 temp := ptr^.next;
  364.                 DISPOSE(ptr);
  365.                 data[row] := temp
  366.              END
  367.              ELSE
  368.                 ptr^.status := Empty
  369.           END { ELSE from IF found }
  370.        END
  371.    END; { DELETE_CELL }
  372.    
  373. FUNCTION ASSIGNED;
  374.    { if found, returns address in ptr or NIL }
  375.    BEGIN
  376.        ptr := locate_cell(row,col);
  377.        IF ptr <> NIL THEN
  378.           WITH ptr^ DO
  379.              IF status = Empty THEN
  380.                 assigned := Desolate
  381.              ELSE IF status <> Full THEN
  382.                 assigned := Error
  383.              ELSE IF (class = Val) OR (class = Expr) THEN
  384.                 assigned := Value
  385.              ELSE
  386.                 assigned := NonValue
  387.        ELSE
  388.           assigned := Void
  389.    END; { ASSIGNED }
  390.    
  391. PROCEDURE MASK_OUT_RECALC;
  392.    VAR i : INTEGER;
  393.    BEGIN
  394.        IF did_recalc THEN BEGIN
  395.           FOR i := 1 TO n_rows DO BEGIN
  396.               ptr := data[i];
  397.               WHILE ptr <> NIL DO BEGIN
  398.                  IF ptr^.class = Expr THEN
  399.                     ptr^.format := ptr^.format & no_recalc_mask &
  400.                                    not_pending_mask;
  401.                  ptr := ptr^.next
  402.               END
  403.           END;    
  404.           did_recalc := FALSE
  405.        END
  406.    END; { MASK_OUT_RECALC }
  407.  
  408. PROCEDURE DEP_RECALC;
  409.    VAR ptr : CellPtr;
  410.    BEGIN
  411.        IF dep <> NIL THEN BEGIN
  412.           did_recalc := TRUE;
  413.           WHILE dep <> NIL DO BEGIN
  414.              ptr := locate_cell(dep^.r,dep^.c);
  415.              IF ptr <> NIL THEN
  416.                 IF (ptr^.class = Expr) AND
  417.                    (ptr^.format & recalc_mask = 0) AND 
  418.                    (ptr^.format & pending_mask = 0) THEN
  419.                    evaluate_formula(dep^.r,dep^.c,FALSE,FALSE,ptr);
  420.              dep := dep^.next
  421.           END
  422.        END
  423.    END; { DEP_RECALC }
  424.  
  425. FUNCTION ASSIGN;
  426.    VAR number          : REAL;
  427.        changed,failed  : BOOLEAN;
  428.        old_status      : StatusType;
  429.        ptr             : CellPtr;
  430.    PROCEDURE CAPITALIZE_AND_EAT_UP_SPACES ( VAR temp : LorFstr );
  431.       VAR i : INTEGER;
  432.       BEGIN
  433.           i := 1;
  434.           WHILE i <= LENGTH(temp) DO BEGIN
  435.              IF temp[i] = ' ' THEN
  436.                 DELETE(temp,i,1)
  437.              ELSE IF temp[i] IN low_case THEN BEGIN
  438.                 temp[i] := CHR(ORD(temp[i])-$20);
  439.                 i := i+1
  440.              END
  441.              ELSE
  442.                 i := i+1
  443.           END
  444.       END; { CAPITALIZE_AND_EAT_UP_SPACES }
  445.    BEGIN
  446.        Set_Mouse(M_Bee);
  447.        changed := FALSE;
  448.        ptr := locate_cell(data_row,data_col);
  449.        all_lists(remove,ptr,data_row,data_col);
  450.        ptr := new_cell(data_row,data_col);
  451.        IF ptr <> NIL THEN
  452.           WITH ptr^ DO BEGIN
  453.              CASE class OF
  454.                 Val : BEGIN
  455.                    old_status := status;
  456.                    number := string_to_real(temp);
  457.                    IF format & perc_mask <> 0 THEN
  458.                       number := number/100;
  459.                    IF temp = 'OVERFLOW' THEN
  460.                       IF status <> Overflow THEN BEGIN
  461.                          changed := TRUE;
  462.                          status := Overflow
  463.                       END
  464.                       ELSE
  465.                    ELSE BEGIN
  466.                       status := Full;
  467.                       IF ((num <> number) OR 
  468.                           (old_status <> status)) THEN BEGIN
  469.                          num := number;
  470.                          changed := TRUE
  471.                       END
  472.                    END;
  473.                    IF (auto_recalc) AND (changed) THEN
  474.                       dep_recalc(sub)
  475.                 END; { Val }
  476.                 Labl : BEGIN
  477.                    IF str = NIL THEN
  478.                       IF request_memory(AString) THEN
  479.                          NEW (str)
  480.                       ELSE
  481.                          status := GenError;
  482.                     IF status <> GenError THEN BEGIN
  483.                        str^ := temp;
  484.                        status := Full
  485.                     END
  486.                 END; { Labl }
  487.                 Expr : BEGIN
  488.                    failed := FALSE;
  489.                    IF str = NIL THEN
  490.                       IF request_memory(AString) THEN 
  491.                          NEW (str)
  492.                       ELSE BEGIN
  493.                          status := GenError;
  494.                          failed := TRUE
  495.                       END;   
  496.                    IF NOT failed THEN BEGIN
  497.                       capitalize_and_eat_up_spaces(temp);
  498.                       IF ptr <> NIL THEN BEGIN
  499.                          str^ := temp;
  500.                          { evaluate_formula will recalc dependents if
  501.                            appropriate }
  502.                          REPEAT { user can edit errors in a dialog box }
  503.                             mask_out_recalc; { in case we're doing again }
  504.                             did_recalc := TRUE;
  505.                             old_form := str^; { eval uses global temp }
  506.                             evaluate_formula(data_row,data_col,FALSE,TRUE,ptr);
  507.                             capitalize_and_eat_up_spaces(str^);
  508.                          UNTIL (str^ = old_form) OR (str^ = '');
  509.                          all_lists(add,ptr,data_row,data_col)
  510.                       END   
  511.                    END
  512.                 END { Expr }
  513.              END (* CASE *)
  514.           END; (* WITH *)
  515.        Set_Mouse(M_Arrow);
  516.        assign := ptr
  517.    END; (* ASSIGN *)
  518.  
  519. FUNCTION SIZE;
  520.    VAR cell_mem : INTEGER;
  521.        dep      : DepPtr;
  522.        ptr      : CellPtr;
  523.    BEGIN
  524.        cell_mem := 0;
  525.        IF assigned(row,col,ptr) <> Void THEN BEGIN
  526.           cell_mem := cell_size;
  527.           WITH ptr^ DO BEGIN
  528.              IF str <> NIL THEN
  529.                 cell_mem := cell_mem+str_size;
  530.              dep := sub;
  531.              WHILE dep <> NIL DO BEGIN
  532.                  cell_mem := cell_mem+dep_size;
  533.                  dep := dep^.next
  534.              END
  535.           END
  536.        END;
  537.        size := cell_mem
  538.    END; { SIZE }
  539.  
  540. FUNCTION COMP_ASSIGN;
  541.    { COMPrehensive ASSIGNment between two CELLs; builds dep lists of other
  542.      cells if build is TRUE; note that the dest cell's dep lists will not
  543.      be affected if it already exists }
  544.    VAR src_ptr,dest_ptr : CellPtr;
  545.        dep              : DepPtr;
  546.    BEGIN
  547.        comp_assign := TRUE;
  548.        delete_cell(dest_row,dest_col,FALSE);
  549.        IF assigned(src_row,src_col,src_ptr) <> Void THEN BEGIN
  550.           dest_ptr := new_cell(dest_row,dest_col);
  551.           IF dest_ptr <> NIL THEN BEGIN
  552.              WITH src_ptr^ DO BEGIN
  553.                 dest_ptr^.class := class;
  554.                 dest_ptr^.num := num;
  555.                 dest_ptr^.status := status;
  556.                 dest_ptr^.format := format
  557.              END;
  558.              IF src_ptr^.str <> NIL THEN
  559.                 IF request_memory(AString) THEN BEGIN
  560.                    NEW(dest_ptr^.str);
  561.                    dest_ptr^.str^ := src_ptr^.str^;
  562.                    IF build THEN
  563.                       all_lists(add,dest_ptr,dest_row,dest_col)
  564.                 END
  565.                 ELSE BEGIN
  566.                    comp_assign := FALSE;
  567.                    dest_ptr^.status := GenError
  568.                 END
  569.              ELSE
  570.           END
  571.           ELSE { not enough memory }
  572.              comp_assign := FALSE
  573.        END
  574.    END; { COMP_ASSIGN }
  575.  
  576. (********************************************************)
  577. (*  End of Functions to manipulate main data structure  *)
  578. (********************************************************)
  579.  
  580.  
  581. (************************************)
  582. (* Dependent-cell list manipulation *)
  583. (************************************)
  584.  
  585. PROCEDURE FREE_DEP_LIST;
  586.    VAR temp : DepPtr;
  587.    BEGIN
  588.       IF ptr <> NIL THEN
  589.          WHILE ptr^.sub <> NIL DO BEGIN
  590.             temp := ptr^.sub^.next;
  591.             DISPOSE(ptr^.sub);
  592.             ptr^.sub := temp;
  593.             working_memory := working_memory+dep_size
  594.          END
  595.    END; { FREE_DEP_LIST }
  596.  
  597. FUNCTION LIST_END;
  598.    { returns a POINTER to the element at the end of the list; i.e. the
  599.      element whose next points to the last one }
  600.    VAR dep : DepPtr;
  601.    BEGIN
  602.        IF ptr <> NIL THEN BEGIN
  603.           dep := ptr^.sub;
  604.           IF dep <> NIL THEN
  605.              WHILE dep^.next <> NIL DO
  606.                 dep := dep^.next;
  607.           list_end := dep
  608.        END
  609.        ELSE
  610.           list_end := NIL
  611.    END; { LIST_END }
  612.  
  613. FUNCTION DUPLICATING;
  614.    { traverses a cell's dependency list and locates any pre-existing entries }
  615.    { for the cell to be added to the list, i.e. prevents duplicates }
  616.    VAR
  617.        found : BOOLEAN;
  618.        dep   : DepPtr;
  619.    BEGIN
  620.        found := FALSE;
  621.        dep := ptr^.sub;
  622.        WHILE (dep <> NIL) AND (NOT found) DO
  623.           IF (dep^.r = dep_row) AND (dep^.c = dep_col) THEN
  624.              found := TRUE
  625.           ELSE
  626.              dep := dep^.next;
  627.        duplicating := found
  628.    END; { DUPLICATING }
  629.  
  630. PROCEDURE LIST_INSERT;
  631.    { inserts an element at the end of the list for the cell fx_row,fx_col;
  632.      'fx' = 'affects' a dependent cell dep_row,dep_col }
  633.    VAR dep            : DepPtr;
  634.        fx_ptr,dep_ptr : CellPtr;
  635.    BEGIN
  636.        fx_ptr := new_cell(fx_row,fx_col);
  637.        dep_ptr := locate_cell(dep_row,dep_col);
  638.        IF (fx_ptr <> NIL) AND (dep_ptr <> NIL) THEN
  639.           IF NOT duplicating(dep_row,dep_col,fx_ptr) THEN
  640.              IF working_memory-dep_size > 0 THEN BEGIN
  641.                 IF fx_ptr^.sub = NIL THEN BEGIN
  642.                    NEW(fx_ptr^.sub);
  643.                    fx_ptr^.sub^.next := NIL
  644.                 END
  645.                 ELSE BEGIN
  646.                    dep := list_end(fx_ptr);
  647.                    NEW(dep^.next);
  648.                    dep^.next^.next := NIL
  649.                 END;
  650.                 dep := list_end(fx_ptr);
  651.                 dep^.r := dep_row;
  652.                 dep^.c := dep_col;
  653.                 working_memory := working_memory-dep_size
  654.              END
  655.              ELSE
  656.                 alert := Do_Alert (
  657.                    '[1][Running out of memory.|Request denied...][  OK  ]',1)
  658.    END; { LIST_INSERT }
  659.  
  660. PROCEDURE LIST_DELETE;
  661.    VAR found    : BOOLEAN;
  662.        dep,temp : DepPtr;
  663.        ptr      : CellPtr;
  664.    BEGIN
  665.        ptr := locate_cell(fx_row,fx_col);
  666.        IF ptr <> NIL THEN BEGIN
  667.           dep := ptr^.sub;
  668.           IF dep <> NIL THEN
  669.              IF (dep^.r <> dep_row) OR (dep^.c <> dep_col) THEN BEGIN
  670.                 found := FALSE;
  671.                 WHILE (NOT found) AND (dep^.next <> NIL) DO
  672.                    IF (dep^.next^.r = dep_row) AND
  673.                       (dep^.next^.c = dep_col) THEN
  674.                       found := TRUE
  675.                    ELSE
  676.                       dep := dep^.next;
  677.                 IF found THEN BEGIN
  678.                    temp := dep^.next^.next;
  679.                    DISPOSE(dep^.next);
  680.                    dep^.next := temp;
  681.                    working_memory := working_memory+dep_size
  682.                 END
  683.              END
  684.              ELSE BEGIN { was first element in list }
  685.                 temp := dep^.next;
  686.                 DISPOSE(dep);
  687.                 ptr^.sub := temp;
  688.                 working_memory := working_memory+dep_size
  689.              END
  690.        END
  691.    END; { LIST_DELETE }
  692.  
  693. PROCEDURE ALL_LISTS;
  694.    { adds/removes all references to this cell to/from the dependency 
  695.      lists of each cell that should/already has an entry for it. Action
  696.      equals: 'add', 'remove' }
  697.    VAR dummy  : INTEGER;
  698.        result : StatusType;
  699.    BEGIN
  700.        IF ptr <> NIL THEN
  701.           IF ptr^.class = Expr THEN
  702.              IF ptr^.str <> NIL THEN 
  703.                 result := adjust_expr(action,ptr,
  704.                                       row,col,
  705.                                       dummy,dummy,dummy,dummy,dummy,dummy)
  706.    END; { ALL_LISTS }
  707.  
  708. (*******************************************)
  709. (* End of Dependent-cell list manipulation *)
  710. (*******************************************)
  711.  
  712.  
  713. (************************)
  714. (* Screen-related stuff *)
  715. (************************)
  716.  
  717. PROCEDURE FIND_SCREEN_POS;
  718.    { takes sheet pos in row,col and returns screen pos;
  719.      called by MOUSE,draw_cell,display_data,reset_window }
  720.    VAR i : INTEGER;
  721.    BEGIN
  722.        l_scr_row := 1;
  723.        l_scr_col := 1;
  724.        i := start_row;
  725.        REPEAT
  726.            IF i < row THEN
  727.               l_scr_row := l_scr_row+1;
  728.            i := i+1
  729.        UNTIL i >= row;
  730.        i := start_col;
  731.        REPEAT
  732.            IF i < col THEN
  733.               l_scr_col := l_scr_col+1;
  734.            i := i+1
  735.        UNTIL i >= col
  736.    END; { FIND_SCREEN_POS }
  737.  
  738. PROCEDURE SAVE_ATTR;
  739.    BEGIN
  740.        w_pos[w_idx,w_hdl] := act_hdl;
  741.        w_pos[w_idx,first_row] := start_row;
  742.        w_pos[w_idx,last_row] := finish_row;
  743.        w_pos[w_idx,first_col] := start_col;
  744.        w_pos[w_idx,last_col] := finish_col;
  745.        w_pos[w_idx,hot_row] := data_row;
  746.        w_pos[w_idx,hot_col] := data_col;
  747.        w_vert_grid[w_idx] := vert_grid
  748.    END; { SAVE_ATTR }
  749.  
  750. PROCEDURE RETURN_ATTR;
  751.    BEGIN
  752.        act_hdl := w_pos[w_idx,w_hdl];
  753.        start_row := w_pos[w_idx,first_row];
  754.        finish_row := w_pos[w_idx,last_row];
  755.        start_col := w_pos[w_idx,first_col];
  756.        finish_col := w_pos[w_idx,last_col];
  757.        data_row := w_pos[w_idx,hot_row];
  758.        data_col := w_pos[w_idx,hot_col];
  759.        vert_grid := w_vert_grid[w_idx];
  760.        h_entry := finish_col-start_col+1;
  761.        v_entry := finish_row-start_row+1;
  762.        IF finish_col < n_cols THEN BEGIN
  763.           virtual_f_col := finish_col+1;
  764.           virtual_h_entry := h_entry+1
  765.        END
  766.        ELSE BEGIN
  767.           virtual_f_col := finish_col;
  768.           virtual_h_entry := h_entry
  769.        END;
  770.        IF finish_row < n_rows THEN BEGIN
  771.           virtual_f_row := finish_row+1;
  772.           virtual_v_entry := v_entry+1
  773.        END
  774.        ELSE BEGIN
  775.           virtual_f_row := finish_row;
  776.           virtual_v_entry := v_entry
  777.        END;
  778.        find_screen_pos(data_row,data_col,scr_row,scr_col)
  779.    END; { RETURN_ATTR }
  780.  
  781. PROCEDURE SWITCH_WINDOW;
  782.    BEGIN
  783.        save_attr;
  784.        w_idx := ABS(w_idx-3);
  785.        return_attr
  786.    END; { SWITCH_WINDOW }    
  787.  
  788. PROCEDURE CELL_ON_SCREEN;
  789.    { update a cell; if two windows are open and cell is visible in both, it
  790.      will be updated in both. }
  791.    VAR a1,a2,b1,b2,c1,c2,d1,d2 : INTEGER;
  792.    BEGIN
  793.        IF n_hdls = 2 THEN BEGIN
  794.           Border_Rect(w_pos[1,w_hdl],a1,b1,c1,d1);
  795.           Border_Rect(w_pos[2,w_hdl],a2,b2,c2,d2);
  796.           IF NOT Rect_Intersect( a1,b1,c1,d1,a2,b2,c2,d2) THEN BEGIN
  797.              switch_window;
  798.              IF (row >= start_row) AND (row <= virtual_f_row) AND
  799.                 (col >= start_col) AND (col <= virtual_f_col) THEN BEGIN
  800.                 Hide_Mouse;
  801.                 IF draw_or_toggle = 1 THEN { completely draw the cell;        }
  802.                    draw_cell(row,col,TRUE) { avoid draw_cell inversing it     }
  803.                 ELSE BEGIN                 { as it would if FALSE was passed. }
  804.                    Work_Rect(act_hdl,x_1,y_1,w_1,h_1); { since toggle does }
  805.                    Set_Clip(x_1,y_1,w_1,h_1);          { NOT affect clip   }
  806.                    toggle_inverse(Black,row,col)
  807.                 END;
  808.                 Show_Mouse
  809.              END;
  810.              switch_window;
  811.              Work_Rect(act_hdl,x_1,y_1,w_1,h_1); { just in case toggle was }
  812.              Set_Clip(x_1,y_1,w_1,h_1)           { used; antibug... }
  813.           END
  814.           ELSE BEGIN    { completely redraw the portion(s) of the inactive }
  815.                         { window using the GEM message queue to make sure  }
  816.              switch_window; { we get the proper clip values        }
  817.              IF (row >= start_row) AND (row <= virtual_f_row) AND
  818.                 (col >= start_col) AND (col <= virtual_f_col) THEN BEGIN
  819.                 First_Rect(act_hdl,a1,b1,c1,d1);
  820.                 WHILE (c1 <> 0) AND (d1 <> 0) DO BEGIN
  821.                    Send_Redraw(FALSE,a1,b1,c1,d1);
  822.                    Next_Rect(act_hdl,a1,b1,c1,d1)
  823.                 END
  824.              END;
  825.              switch_window
  826.           END
  827.        END;
  828.        { now do active window }
  829.        IF (row >= start_row) AND (row <= virtual_f_row) AND
  830.           (col >= start_col) AND (col <= virtual_f_col) THEN BEGIN
  831.           Hide_Mouse;
  832.           IF draw_or_toggle = 1 THEN
  833.              draw_cell(row,col,force)
  834.           ELSE
  835.              toggle_inverse(Black,row,col);
  836.           Show_Mouse
  837.        END
  838.    END; { CELL_ON_SCREEN }
  839.  
  840. PROCEDURE DEFAULT_DRAW_ATTRIBUTES;
  841.    BEGIN
  842.       Paint_Style(Solid);
  843.       Paint_Outline(FALSE);
  844.       Paint_Color(White);
  845.       Text_Color(Black);
  846.       Draw_Mode(Replace_Mode);
  847.       Text_Style(Normal)
  848.    END; { DEFAULT_DRAW_ATTRIBUTES }
  849.    
  850. PROCEDURE REDRAW_MESSAGE;
  851.    VAR
  852.        other_window : BOOLEAN;
  853.    BEGIN
  854.        default_draw_attributes;
  855.        First_Rect(hdl,redraw_x,redraw_y,redraw_w,redraw_h);
  856.        WHILE (redraw_w <> 0) AND (redraw_h <> 0) DO BEGIN
  857.           IF Rect_Intersect(x,y,w,h,
  858.                             redraw_x,redraw_y,
  859.                             redraw_w,redraw_h) THEN BEGIN
  860.              other_window := FALSE;
  861.              IF hdl <> act_hdl THEN BEGIN
  862.                 switch_window;
  863.                 other_window := TRUE
  864.              END;
  865.              redraw_flag := TRUE; { confine sheet_redraw clip rect }
  866.              { draw whole sheet, but within bounds of redraw_x, etc.}
  867.              sheet_redraw(WholeSheet,FALSE,None); { it saves attr }
  868.              IF other_window THEN 
  869.                 switch_window
  870.           END;
  871.           Next_Rect(hdl,redraw_x,redraw_y,redraw_w,redraw_h)
  872.        END
  873.    END; { REDRAW_MESSAGE }
  874.  
  875. PROCEDURE Send_Redraw;
  876.    { write a redraw message to the event queue after displaying fsel so
  877.      that after LOAD, we can redraw the entire screen, instead of first
  878.      redrawing the area covered by the fsel then doing a full redraw of the
  879.      screen. The AES merges the two messages into one. Also, used to send
  880.      the message to redraw the area covered by the 'action indicator'
  881.      which show whether a file is to be loaded, saved, etc. The AES always
  882.      merges this redraw w/ the one generated by fsel. }
  883.    BEGIN
  884.        msg[0] := WM_Redraw;
  885.        msg[1] := ap_id;
  886.        msg[2] := 0;
  887.        msg[3] := act_hdl;
  888.        msg[4] := x;
  889.        msg[5] := y;
  890.        msg[6] := w;
  891.        msg[7] := h;
  892.        Write_Message(ap_id,16,msg);
  893.        IF all_windows THEN
  894.           IF n_hdls = 2 THEN BEGIN
  895.              IF act_hdl = w_pos[1,w_hdl] THEN
  896.                 msg[3] := w_pos[2,w_hdl]
  897.              ELSE
  898.                 msg[3] := w_pos[1,w_hdl];
  899.              Write_Message(ap_id,16,msg)
  900.           END
  901.    END; { Send_Redraw }
  902.  
  903. PROCEDURE HOME_CURSOR;
  904.    { note that home to row,col = 1,1 requires a redraw unless 1,1 is on
  905.      screen- in that case should use both, not origin. Use of both,r,s implies
  906.      that the cell to be moved to already resides on the screen }
  907.    BEGIN
  908.        IF extent = Origin THEN BEGIN
  909.           data_row := logical_row_1;
  910.           data_col := logical_col_1;
  911.           start_row := data_row;
  912.           start_col := data_col
  913.        END;
  914.        IF (extent = R) OR (extent = Both) THEN BEGIN
  915.           data_row := start_row;
  916.           scr_row := 1
  917.        END;
  918.        IF (extent = C) OR (extent = Both) THEN BEGIN
  919.           data_col := start_col;
  920.           scr_col := 1
  921.        END
  922.    END; { HOME_CURSOR }
  923.  
  924. PROCEDURE MY_LINE_STYLE;
  925.    { uses VDI calls for a custom line style; looks better than the 6 default
  926.      patterns provided by VDI; dots are closer together for the vertical
  927.      line }
  928.    BEGIN
  929.        Create_User_Line_Type(style);
  930.        User_Line_Style { like Pasgem Line_Type }
  931.    END; { MY_LINE_STYLE }
  932.  
  933.  
  934. (*******************************)
  935. (* End of Screen-related stuff *)
  936. (*******************************)
  937.  
  938.  
  939. PROCEDURE STRING_A_CELL;
  940.    { take a row and col and convert them to a cell; i.e. 5,2 => B5 }
  941.    BEGIN
  942.        int_to_string(row,temp);
  943.        temp := CONCAT(col_name[col],temp)
  944.    END; { STRING_A_CELL }
  945.  
  946. PROCEDURE OUT_MEM_CELL;
  947.    BEGIN
  948.        string_a_cell(row,col,temp);
  949.        temp := CONCAT('[1][Out of memory in cell ' , temp , ',|' ,
  950.                       'which was NOT ' , specific , '.]' ,
  951.                       '[  OK  ]' );
  952.        alert := Do_Alert(temp,1)
  953.    END; { OUT_MEM_CELL }
  954.  
  955. FUNCTION REL_OVERFLOW;
  956.    { called by perform_2 in adjust_expr }
  957.    BEGIN
  958.        string_a_cell(row,col,temp);
  959.        temp := CONCAT('[1][A relative cell reference|' ,
  960.                           'caused a boundary overflow|' ,
  961.                           'to occur upon incrementing|' ,
  962.                           'a reference in cell ', what ,
  963.                           '.][Cancel|Continue]');
  964.        Set_Mouse(M_Arrow);
  965.        rel_overflow := Do_Alert(temp,2);
  966.        { restore mouse to bee since the caller had it set to bee }
  967.        Set_Mouse(M_Bee);
  968.    END; { REL_OVERFLOW }
  969.  
  970.  
  971. (***********************)
  972. (* Real number parsing *)
  973. (***********************)
  974.  
  975. PROCEDURE STRIP_NUM;
  976.    { strips a REAL from a string; str_pos = position just after last char in
  977.      number, when done. Called by factor in evalexpr, and also by
  978.      scan_for_cells }
  979.    VAR original_pos,e_pos  : INTEGER;
  980.        n_chr               : CHAR;
  981.        e_found,e_sign,done : BOOLEAN;
  982.    BEGIN
  983.        e_found := FALSE;
  984.        e_sign := FALSE;
  985.        done := FALSE;
  986.        original_pos := str_pos;
  987.        num_str := '';
  988.        WHILE (str_pos <= len) AND (NOT done) DO BEGIN
  989.           n_chr := str[str_pos];
  990.           IF n_chr IN float THEN BEGIN
  991.              IF (n_chr = 'E') OR (n_chr = 'e') THEN BEGIN
  992.                 e_pos := str_pos;
  993.                 e_found := TRUE;
  994.              END;
  995.              IF (n_chr = '+') OR (n_chr = '-') THEN
  996.                 IF NOT e_sign THEN
  997.                    IF str_pos > original_pos THEN { either exponent sign or }
  998.                       IF e_found THEN             { delimiter }
  999.                          IF str_pos-1 = e_pos THEN
  1000.                             e_sign := TRUE
  1001.                          ELSE
  1002.                             done := TRUE
  1003.                       ELSE
  1004.                          done := TRUE
  1005.                    ELSE { just the sign of the number }
  1006.                 ELSE { must have been a delimiter }
  1007.                    done := TRUE;
  1008.              IF NOT done THEN BEGIN
  1009.                 num_str := CONCAT(num_str,n_chr);
  1010.                 str_pos := str_pos+1;
  1011.              END;
  1012.           END
  1013.           ELSE
  1014.              done := TRUE
  1015.        END { WHILE }
  1016.    END; { STRIP_NUM }
  1017.  
  1018. FUNCTION VALID_NUMBER;
  1019.    { sees if num_str is a valid number for real_to_string; rules
  1020.      out ALL potential errors, including E3, 1.2.3E4-3, 1.23e3-2, etc.
  1021.      called by window_input, parser. }
  1022.    VAR n_pos,num_sign_pos,exp_sign_pos,
  1023.        dec_pos,e_pos,i,len_num_str       : INTEGER;
  1024.        n_chr                             : CHAR;
  1025.        ok_num                            : StatusType;
  1026.    BEGIN
  1027.        ok_num := OK;
  1028.        n_pos := 1;
  1029.        num_sign_pos := 0;
  1030.        exp_sign_pos := 0;
  1031.        dec_pos := 0;
  1032.        e_pos := 0;
  1033.        len_num_str := LENGTH(num_str);
  1034.        IF len_num_str = 0 THEN
  1035.           ok_num := BadReal
  1036.        ELSE 
  1037.           WHILE (n_pos <= len_num_str) AND (ok_num = OK) DO BEGIN
  1038.              n_chr := num_str[n_pos];
  1039.              IF NOT (n_chr IN float) THEN
  1040.                 ok_num := BadReal
  1041.              ELSE BEGIN
  1042.                  (* good and bad e or E *)
  1043.                  IF ( n_chr='E' ) OR ( n_chr='e' ) THEN
  1044.                     IF e_pos = 0 THEN
  1045.                        IF n_pos > 1 THEN
  1046.                           IF ( { account for -e & -E }
  1047.                                (n_pos = 2) AND (NOT(num_str[1] IN digits))
  1048.                              )  OR
  1049.                              (n_pos = len_num_str) THEN
  1050.                              ok_num := BadReal
  1051.                           ELSE
  1052.                              e_pos := n_pos
  1053.                        { account for e12 & E123 }
  1054.                        ELSE
  1055.                           ok_num := BadReal
  1056.                     { > 1 e's }
  1057.                     ELSE
  1058.                        ok_num := BadReal;
  1059.                  (* good and bad sign, for both number and exponent *)
  1060.                  IF  (n_chr = '+') OR (n_chr = '-') THEN
  1061.                      IF n_pos = 1 THEN { sign of number }
  1062.                         IF num_sign_pos = 0 THEN
  1063.                            IF len_num_str > 1 THEN
  1064.                               { really a pointless assignment, since nothing
  1065.                                 else depends on this; it does clarify and
  1066.                                 keep the routine consistent by documenting
  1067.                                 this, however }
  1068.                               num_sign_pos := n_pos
  1069.                            ELSE
  1070.                               ok_num := BadReal
  1071.                         ELSE { no other possibility }
  1072.                      ELSE IF n_pos = len_num_str THEN
  1073.                         ok_num := BadReal
  1074.                      ELSE IF e_pos = 0 THEN
  1075.                         ok_num := BadReal
  1076.                      ELSE IF exp_sign_pos = 0 THEN
  1077.                         IF (
  1078.                              (POS('E',num_str)=n_pos-1) OR
  1079.                              (POS('e',num_str)=n_pos-1)
  1080.                            )  THEN
  1081.                            exp_sign_pos := n_pos
  1082.                         ELSE
  1083.                            ok_num := BadReal
  1084.                      ELSE
  1085.                         ok_num := BadReal;
  1086.                  (* good & bad decimal *)
  1087.                  IF n_chr = '.' THEN
  1088.                     IF (dec_pos = 0) AND (e_pos = 0) THEN
  1089.                        IF n_pos = len_num_str THEN
  1090.                           ok_num := BadReal
  1091.                        ELSE IF NOT (num_str[n_pos+1] IN digits) THEN
  1092.                                ok_num := BadReal
  1093.                        ELSE
  1094.                           dec_pos := n_pos
  1095.                     ELSE
  1096.                        ok_num := BadReal;
  1097.                  n_pos := n_pos+1;
  1098.              END; { ELSE }
  1099.           END; { WHILE }
  1100.        valid_number := ok_num;
  1101.    END; (* VALID_NUMBER *)
  1102.  
  1103. (******************************)
  1104. (* End of Real number parsing *)
  1105. (******************************)
  1106.  
  1107.  
  1108. (****************)
  1109. (* Cell Parsing *)
  1110. (****************)
  1111.  
  1112. FUNCTION VALID_COL_NAME;
  1113.    { column name = A,B,...,Z,AA,BB,AB,ID, etc. depending on n_cols }
  1114.    VAR first,second,sum : INTEGER;
  1115.    BEGIN
  1116.        valid_col_name := FALSE;
  1117.        sum := 0;
  1118.        IF LENGTH(temp) > 0 THEN BEGIN
  1119.           first := ORD(temp[1])-64;
  1120.           IF LENGTH(temp) > 1 THEN
  1121.              IF temp[2] IN up_case THEN BEGIN
  1122.                 second := ORD(temp[2])-64;
  1123.                 sum := first*26+second
  1124.              END
  1125.              ELSE
  1126.                 sum := first
  1127.           ELSE
  1128.              sum := first
  1129.        END;      
  1130.        IF (sum > 0) AND (sum <= n_cols) THEN
  1131.           IF col_name[sum] = temp THEN
  1132.              valid_col_name := TRUE;
  1133.        col_number := sum { meaningless if valid_col_name set to false }
  1134.    END; { VALID_COL_NAME }
  1135.  
  1136. PROCEDURE GET_COL;
  1137.    VAR column : STR10;
  1138.        at_end : BOOLEAN;
  1139.    BEGIN
  1140.        IF str[str_pos] = '$' THEN BEGIN
  1141.           col_rel := FALSE;
  1142.           str_pos := str_pos+1
  1143.        END
  1144.        ELSE
  1145.           col_rel := TRUE;
  1146.        IF str_pos >= len THEN
  1147.           status := BadRef
  1148.        ELSE BEGIN
  1149.           column := '';
  1150.           at_end := FALSE;
  1151.           WHILE (NOT at_end) AND (status <> BadRef) DO BEGIN
  1152.               IF str[str_pos] IN up_case THEN BEGIN
  1153.                  column := CONCAT(column,str[str_pos]);
  1154.                  str_pos := str_pos+1;
  1155.                  IF LENGTH(column) > 2 THEN
  1156.                     status := BadRef
  1157.               END
  1158.               ELSE
  1159.                  at_end := TRUE;
  1160.               IF str_pos > len THEN
  1161.                  status := BadRef
  1162.           END;
  1163.           IF status = OK THEN
  1164.             IF NOT valid_col_name(column,col) THEN
  1165.                status := BadRef
  1166.        END
  1167.    END; { GET_COL }
  1168.  
  1169. PROCEDURE GET_ROW;
  1170.    VAR i,multiplier : INTEGER;
  1171.        row_str      : STR10;
  1172.        at_end       : BOOLEAN;
  1173.    BEGIN
  1174.        IF str[str_pos] = '$' THEN BEGIN
  1175.           row_rel := FALSE;
  1176.           str_pos := str_pos+1
  1177.        END
  1178.        ELSE
  1179.           row_rel := TRUE;
  1180.        IF str_pos > len THEN
  1181.           status := BadRef
  1182.        ELSE BEGIN
  1183.           row_str := '';
  1184.           at_end := FALSE;
  1185.           WHILE (status <> BadRef) AND (NOT at_end) DO BEGIN
  1186.               IF str[str_pos] IN digits THEN BEGIN
  1187.                  row_str := CONCAT(row_str,str[str_pos]);
  1188.                  str_pos := str_pos+1;
  1189.                  IF LENGTH(row_str) > 3 THEN
  1190.                     status := BadRef
  1191.               END
  1192.               ELSE
  1193.                  at_end := TRUE;
  1194.               IF str_pos > len THEN 
  1195.                  at_end := TRUE
  1196.           END;
  1197.           IF LENGTH(row_str) = 0 THEN
  1198.              status := BadRef;
  1199.           IF status = OK THEN BEGIN
  1200.              multiplier := 1;
  1201.              row := 0;
  1202.              FOR i := LENGTH(row_str) DOWNTO 1 DO BEGIN
  1203.                  row := row+(ORD(row_str[i])-$30)*multiplier;
  1204.                  multiplier := multiplier*10
  1205.              END
  1206.           END
  1207.        END { ELSE }
  1208.    END; { GET_ROW }
  1209.  
  1210. FUNCTION TRANSLATE_CELL;
  1211.    { A1 => 1,1; Expects the starting position of the tentative cell ref and the
  1212.      length of the string it appears in.
  1213.      After the call, if no error was found, str_pos will
  1214.      equal the position immediately following the cell reference, and
  1215.      returns OK; otherwise, returns an error message }
  1216.    VAR status : StatusType;
  1217.    BEGIN
  1218.        IF len < 2 THEN
  1219.           translate_cell := BadRef
  1220.        ELSE BEGIN
  1221.           status := OK;
  1222.           get_col(str,str_pos,len,col,col_rel,status);
  1223.           IF status = OK THEN
  1224.              get_row(str,str_pos,len,row,row_rel,status);
  1225.           IF status = OK THEN
  1226.              IF (col < 1) OR (col > n_cols) OR
  1227.                 (row < 1) OR (row > n_rows) THEN
  1228.                 status := OutOfRange;
  1229.           translate_cell := status
  1230.        END
  1231.    END; { TRANSLATE_CELL }
  1232.  
  1233. (***********************)
  1234. (* End of Cell Parsing *)
  1235. (***********************)
  1236.  
  1237.  
  1238. (********************************)
  1239. (* Expression-specific routines *)
  1240. (********************************)
  1241.  
  1242. FUNCTION SCAN_FOR_CELLS;
  1243.    { scans a string for cells, beginning at str_pos; returns the 
  1244.      position immediately FOLLOWING the ref. Further error checking
  1245.      by translate_cell is performed. Note str_pos is also modified
  1246.      by translate_cell so that it equals the position following the
  1247.      cell ref. To scan for all cell in a string, the caller must call
  1248.      this function until str_pos = len. If an error, str_pos may equal
  1249.      len+1, depending on where the error occurred. Also when no cell
  1250.      is found. The cell's position is returned in cell_pos }
  1251.    VAR found_status : BOOLEAN;
  1252.        dummy        : LorFstr;
  1253.    BEGIN
  1254.        found_status := FALSE;
  1255.        WHILE (str_pos < len) AND (NOT found_status) DO
  1256.           IF str[str_pos] IN up_case THEN
  1257.              IF str[str_pos+1] IN digits+['$'] THEN BEGIN
  1258.                 found_status := TRUE;
  1259.                 cell_pos := str_pos
  1260.              END
  1261.              ELSE IF str_pos+1 < len THEN
  1262.                 IF (str[str_pos+1] IN up_case) AND
  1263.                    (str[str_pos+2] IN digits+['$']) THEN BEGIN
  1264.                    found_status := TRUE;
  1265.                    cell_pos := str_pos
  1266.                 END
  1267.                 ELSE
  1268.                    REPEAT
  1269.                       str_pos := str_pos+1
  1270.                    UNTIL (NOT (str[str_pos] IN up_case)) OR (str_pos = len)
  1271.              ELSE      { must have been a keyword; skip remaining caps so }
  1272.                 REPEAT { that SERR, CORR won't be considered a cell. Note }
  1273.                    str_pos := str_pos+1
  1274.                 UNTIL (NOT (str[str_pos] IN up_case)) OR (str_pos = len)
  1275.           ELSE IF str[str_pos] = '$' THEN BEGIN
  1276.              found_status := TRUE;
  1277.              cell_pos := str_pos
  1278.           END       
  1279.           ELSE IF str[str_pos] IN digits+['.'] THEN { don't care about sign }
  1280.              strip_num(dummy,str,str_pos,len)       { here; what if -E1? }
  1281.           ELSE
  1282.              str_pos := str_pos+1;
  1283.        scan_for_cells := found_status;
  1284.        IF found_status THEN
  1285.           IF translate_cell(str,str_pos,len,row,col,row_rel,col_rel)<>OK THEN
  1286.              scan_for_cells := FALSE { wasn't really a cell }
  1287.    END; { SCAN_FOR_CELLS }  
  1288.  
  1289. FUNCTION ADJUST_EXPR;
  1290.    { called by adjust_cell_refs for which it scans a string for cell
  1291.      refs and if appropriate, modifies the string so that these cell
  1292.      refs reference a different cell ( i.e. a relative adjustment ); 
  1293.      src_row..dest_col are significant as are row_st..col_end. 
  1294.      The latter four represent the scope of the
  1295.      block move; action = adj_cell_refs }
  1296.    { called by all_lists for which it scans a string for cell refs
  1297.      and updates the found cells' dep lists to contain the cell passed
  1298.      in src_row, src_col; action = add or remove }    
  1299.    { called by replicate cell for which it scans a string and modifies
  1300.      the cell refs relatively; src_row..dest_col are meaningful; so we
  1301.      can use the code of perform_2, need to pass row_st..col_end with
  1302.      values of 1,n_rows,1,n_cols since we always wish to adjust and must
  1303.      therefore define a block size equalling the entire sheet; 
  1304.      action = adj_cell_refs } 
  1305.    { the function returns a value of OK unless it was adjusting cell
  1306.      refs, a range error occurred, and the user selected 'Cancel' from
  1307.      the alert box in rel_overflow, in which case it returns OutOfRange }
  1308.  
  1309.    LABEL 1; { label to go to if an adjusted cell ref is invalid and the
  1310.               user wants to abort the action }  
  1311.    VAR i,j,s_r,s_c,dummy,cell_pos,
  1312.        len,str_pos,row,col,adj_r,adj_c          : INTEGER;
  1313.        row_rel,col_rel,abort,do_range           : BOOLEAN;
  1314.        dup_str                                  : LorFstr;
  1315.        status                                   : StatusType;
  1316.        dep                                      : DepPtr;
  1317.    PROCEDURE PERFORM_1 ( row,col : INTEGER );
  1318.       { row,col = the cell which appeared as a reference } 
  1319.       VAR ptr : CellPtr;
  1320.       BEGIN
  1321.           IF action = add THEN { action inherited from adj_expr }
  1322.              list_insert(row,col,src_row,src_col)
  1323.           ELSE BEGIN { action = remove }
  1324.              list_delete(row,col,src_row,src_col);
  1325.              ptr := locate_cell(row,col);
  1326.              IF ptr <> NIL THEN
  1327.                 IF (ptr^.sub = NIL) AND (ptr^.status = Empty) THEN
  1328.                    delete_cell(row,col,FALSE)
  1329.           END             
  1330.       END; { PERFORM_1 }
  1331.  
  1332.    PROCEDURE PERFORM_2 ( row,col : INTEGER );
  1333.       { row,col = the cell which appeared as a reference } 
  1334.       VAR r,c,offset,cell_len : INTEGER;
  1335.           temp1,temp2         : STR10;
  1336.       PROCEDURE ALTER_STR;
  1337.          BEGIN
  1338.              string_a_cell(dest_row,dest_col,temp1);
  1339.              int_to_string(r,temp2);
  1340.              IF NOT row_rel THEN 
  1341.                 temp2 := CONCAT('$',temp2);
  1342.              temp2 := CONCAT(col_name[c],temp2);
  1343.              IF NOT col_rel THEN
  1344.                 temp2 := CONCAT('$',temp2);
  1345.              cell_len := str_pos-cell_pos;
  1346.              IF len-cell_len+LENGTH(temp2) > string_len THEN BEGIN
  1347.                 temp := CONCAT('[1][Adjusting a relative cell|' ,
  1348.                                    'reference in cell ' , temp1 , '|' ,
  1349.                                    'caused the formula length to|' ,
  1350.                                    'exceed the maximum allowed.]' ,
  1351.                                    '[Cancel|Continue]');
  1352.                 ptr^.status := GenError;
  1353.                 IF Do_Alert(temp,2) = 1 THEN BEGIN
  1354.                    adjust_expr := GenError;
  1355.                    ptr^.str^ := dup_str;
  1356.                    cell_on_screen(1,dest_row,dest_col,TRUE);
  1357.                    write_cell_name;
  1358.                    GOTO 1
  1359.                 END
  1360.              END
  1361.              ELSE BEGIN
  1362.                 DELETE(ptr^.str^,cell_pos,cell_len);
  1363.                 IF cell_pos > LENGTH(ptr^.str^) THEN    { can't insert to pos }
  1364.                    ptr^.str^ := CONCAT(ptr^.str^,temp2) { past end of string  }
  1365.                 ELSE
  1366.                    INSERT(temp2,ptr^.str^,cell_pos);
  1367.                 str_pos := cell_pos+LENGTH(temp2) { just in case }
  1368.              END;
  1369.              len := LENGTH(ptr^.str^)
  1370.          END; { ALTER_STR }
  1371.       BEGIN
  1372.           IF (row >= row_st) AND (row <= row_end) AND
  1373.              (col >= col_st) AND (col <= col_end) THEN BEGIN
  1374.  
  1375.              { so it's within the realm of the block passed; errors due to
  1376.                adjusted rel. refs exceeding the sheet bounds need NOT be
  1377.                checked; they are IMPOSSIBLE***, since:
  1378.                1. only valid cell refs may appear in formulas;
  1379.                2. only cell refs falling within the bounds of the block
  1380.                   are adjusted;
  1381.                3. block moves must have a destination block falling within
  1382.                   the bounds of the sheet.
  1383.                So, the above really says the following things, using rows as
  1384.                an example, for a cell ref that will be adjusted:
  1385.                     src_block_start <= row_ref <= src_block_end;
  1386.                     dest_block_start <= dest_row_ref <= dest_block_end
  1387.                                                                   <= n_rows.
  1388.                ***All the above applies to sheet inserts/deletes.
  1389.  
  1390.                BUT cell REPLICATION
  1391.                can still generate out-of-bounds cell refs, so must still check
  1392.                for these errors; easiest just to do for all cases, and
  1393.                hardly takes any time at all. }
  1394.  
  1395.              string_a_cell(dest_row,dest_col,temp1); { in case OutOfRange }
  1396.              IF row_rel THEN BEGIN
  1397.                 offset := src_row-row;
  1398.                 IF (dest_row-offset<1) OR (dest_row-offset>n_rows) THEN BEGIN
  1399.                    ptr^.status := GenError;
  1400.                    IF rel_overflow(dest_row,dest_col,temp1) = 1 THEN BEGIN
  1401.                       adjust_expr := OutOfRange;
  1402.                       ptr^.str^ := dup_str;
  1403.                       cell_on_screen(1,dest_row,dest_col,TRUE);
  1404.                       write_cell_name;
  1405.                       GOTO 1 { hasty exit }
  1406.                    END
  1407.                    ELSE
  1408.                       r := row { no change, continue anyway }
  1409.                 END
  1410.                 ELSE
  1411.                    r := dest_row-offset
  1412.              END
  1413.              ELSE 
  1414.                 r := row;
  1415.              IF col_rel THEN BEGIN
  1416.                 offset := src_col-col;
  1417.                 IF (dest_col-offset<1) OR (dest_col-offset>n_cols) THEN BEGIN
  1418.                    ptr^.status := GenError;
  1419.                    IF rel_overflow(dest_row,dest_col,temp1) = 1 THEN BEGIN
  1420.                       adjust_expr := OutOfRange;
  1421.                       ptr^.str^ := dup_str;
  1422.                       cell_on_screen(1,dest_row,dest_col,TRUE);
  1423.                       write_cell_name;
  1424.                       GOTO 1
  1425.                    END 
  1426.                    ELSE
  1427.                       c := col
  1428.                 END      
  1429.                 ELSE
  1430.                    c := dest_col-offset
  1431.              END
  1432.              ELSE
  1433.                 c := col;
  1434.              IF NOT do_range THEN BEGIN
  1435.                 adj_r := r;
  1436.                 adj_c := c
  1437.              END
  1438.              ELSE IF (adj_r > r) OR (adj_c > c) THEN BEGIN
  1439.                 ptr^.status := GenError;
  1440.                 temp := CONCAT('[1][A range reference in cell|' ,
  1441.                                    temp1, ' will be altered to|' ,
  1442.                                    'prevent an invalid range from|' ,
  1443.                                    'being created.][Cancel|Continue]');
  1444.                 IF Do_Alert(temp,2) = 1 THEN BEGIN
  1445.                    adjust_expr := BadRef;
  1446.                    ptr^.str^ := dup_str;
  1447.                    cell_on_screen(1,dest_row,dest_col,TRUE);
  1448.                    write_cell_name;
  1449.                    GOTO 1
  1450.                 END
  1451.                 ELSE BEGIN
  1452.                    r := adj_r;
  1453.                    c := adj_c
  1454.                 END
  1455.              END;
  1456.              alter_str
  1457.           END
  1458.           ELSE IF NOT do_range THEN BEGIN
  1459.              adj_r := row;
  1460.              adj_c := col
  1461.           END
  1462.           ELSE IF (adj_r > row) OR (adj_c > col) THEN BEGIN
  1463.              ptr^.status := GenError;
  1464.              temp := CONCAT('[1][A range reference in cell|' ,
  1465.                                 temp1, ' will be altered to|' ,
  1466.                                 'prevent an invalid range from|' ,
  1467.                                 'being created.][Cancel|Continue]');
  1468.              IF Do_Alert(temp,2) = 1 THEN BEGIN
  1469.                 adjust_expr := BadRef;
  1470.                 ptr^.str^ := dup_str;
  1471.                 cell_on_screen(1,dest_row,dest_col,TRUE);
  1472.                 write_cell_name;
  1473.                 GOTO 1
  1474.              END
  1475.              ELSE BEGIN
  1476.                 r := adj_r;
  1477.                 c := adj_c;
  1478.                 alter_str
  1479.              END   
  1480.           END
  1481.       END; { PERFORM_2 }
  1482.  
  1483.    PROCEDURE PERFORM ( loc_action,s_row,s_col,e_row,e_col : INTEGER );
  1484.       VAR i,j : INTEGER;
  1485.       BEGIN
  1486.           CASE loc_action OF
  1487.              add,remove { 1,2 } : perform_1(s_row,s_col);
  1488.              adj_refs   { 3 }   : perform_2(s_row,s_col);
  1489.              4 : IF action = adj_refs THEN { action was inherited }
  1490.                     perform_2(s_row,s_col) { from parent          }
  1491.                  ELSE                      
  1492.                     FOR i := s_row TO e_row DO
  1493.                         FOR j := s_col TO e_col DO
  1494.                             perform_1(i,j)
  1495.           END
  1496.       END; { PERFORM }                         
  1497.  
  1498.    BEGIN { ADJUST_EXPR }
  1499.        adjust_expr := OK;
  1500.        IF ptr <> NIL THEN
  1501.           IF (ptr^.class = Expr) AND (ptr^.str <> NIL) THEN BEGIN
  1502.              abort := FALSE;
  1503.              len := LENGTH(ptr^.str^);
  1504.              dup_str := ptr^.str^;
  1505.              str_pos := 1;
  1506.              WHILE (str_pos < len) AND (NOT abort) DO BEGIN
  1507.                  do_range := FALSE;
  1508.                  IF scan_for_cells(ptr^.str^,str_pos,len,cell_pos,row,col,
  1509.                                    row_rel,col_rel) THEN
  1510.                     IF str_pos < len THEN
  1511.                        IF ptr^.str^[str_pos] <> ':' THEN
  1512.                           perform(action,row,col,dummy,dummy)
  1513.                        ELSE BEGIN { a range was referenced }
  1514.                           s_r := row;
  1515.                           s_c := col;
  1516.                           IF action = adj_refs THEN
  1517.                              perform(action,s_r,s_c,dummy,dummy);
  1518.                           str_pos := str_pos+1;
  1519.                           do_range := TRUE;
  1520.                           IF scan_for_cells(ptr^.str^,str_pos,len,cell_pos,
  1521.                                             row,col,row_rel,col_rel) THEN
  1522.                              IF action = adj_refs THEN
  1523.                                 perform(action,row,col,dummy,dummy)
  1524.                              ELSE
  1525.                                 perform(4,s_r,s_c,row,col)
  1526.                           ELSE
  1527.                              abort := TRUE;
  1528.                        END
  1529.                     ELSE
  1530.                        perform(action,row,col,dummy,dummy)
  1531.                  ELSE
  1532.                     abort := TRUE
  1533.              END { WHILE }       
  1534.           END;
  1535. 1: END; { ADJUST_EXPR }
  1536.  
  1537. (***************************************)
  1538. (* End of Expression-specific routines *)
  1539. (***************************************)
  1540.  
  1541.  
  1542. (***********************)
  1543. (* Miscellaneous stuff *)
  1544. (***********************)
  1545.  
  1546. PROCEDURE ADJUST_MENU;
  1547.    { block set or not set }
  1548.    BEGIN
  1549.        IF enable THEN BEGIN
  1550.           Menu_Enable(main_menu,mcopy);
  1551.           Menu_Enable(main_menu,mmove);
  1552.           Menu_Enable(main_menu,mdelete);
  1553.           Menu_Text(main_menu,mfirstc,'  Show Block Start  cF');
  1554.           Menu_Text(main_menu,mlastc, '  Show Block End    cL')
  1555.        END
  1556.        ELSE BEGIN
  1557.           Menu_Disable(main_menu,mcopy);
  1558.           Menu_Disable(main_menu,mmove);
  1559.           Menu_Disable(main_menu,mdelete);
  1560.           Menu_Text(main_menu,mfirstc,'  Show First Cell   cF');
  1561.           Menu_Text(main_menu,mlastc, '  Show Last Cell    cL')
  1562.        END
  1563.    END; { ADJUST_MENU }
  1564.  
  1565. FUNCTION FIND_PREC;
  1566.    BEGIN
  1567.        IF ptr <> NIL THEN
  1568.           find_prec := ptr^.format & prec_mask
  1569.        ELSE
  1570.           find_prec := default_format & prec_mask
  1571.    END; { FIND_PREC }
  1572.  
  1573. FUNCTION FIND_JUST;
  1574.    VAR just : INTEGER;
  1575.    BEGIN
  1576.        just := 0;
  1577.        IF ptr <> NIL THEN
  1578.           just := ptr^.format & just_mask
  1579.        ELSE
  1580.           just := default_format & just_mask;
  1581.        IF just = 0 THEN
  1582.           find_just := VDI_Right
  1583.        ELSE IF just = $0030 THEN
  1584.           find_just := VDI_Center
  1585.        ELSE
  1586.           find_just := VDI_Left
  1587.    END; { FIND_JUST }
  1588.  
  1589. PROCEDURE PREPARE_NUM;
  1590.    { converts a number within a cell to a string,
  1591.      taking into account col_width, precision, etc.
  1592.      called by draw_cell, display_data, print }
  1593.    VAR
  1594.        prec                : INTEGER;
  1595.        number              : REAL;
  1596.        perc_set,dollar_set : BOOLEAN;
  1597.    BEGIN
  1598.        IF ptr <> NIL THEN
  1599.           WITH ptr^ DO
  1600.              IF ((class = Val) OR (class = Expr)) AND { faster than calling  }
  1601.                 (status = Full) THEN BEGIN            { assigned, which must }
  1602.                 number := num;                        { do a locate_cell }
  1603.                 dollar_set := format & dollar_mask <> 0;
  1604.                 perc_set := format & perc_mask <> 0;
  1605.                 IF perc_set THEN
  1606.                    number := number*100;
  1607.                 prec := find_prec(ptr);
  1608.                 IF number <> 0 THEN
  1609.                    IF format & $0008 <> 0 THEN
  1610.                       real_to_string(number,temp,prec,TRUE)
  1611.                    ELSE
  1612.                       real_to_string(number,temp,prec,FALSE)
  1613.                 ELSE
  1614.                    temp := '0';
  1615.                 IF temp[1] = ' ' THEN
  1616.                    DELETE(temp,1,1);
  1617.                 IF dollar_set THEN
  1618.                    IF temp[1] = '-' THEN
  1619.                       INSERT('$',temp,2)
  1620.                    ELSE
  1621.                       temp := CONCAT('$',temp);
  1622.                 IF perc_set THEN
  1623.                    temp := CONCAT(temp,'%')
  1624.              END
  1625.              ELSE IF status < OK THEN
  1626.                 temp := error_msg[status]
  1627.    END; { PREPARE_NUM }
  1628.  
  1629. PROCEDURE BLOCK_TOO_BIG;
  1630.    { called by load_file when "load_block at cursor position" and by 
  1631.      transport_block }
  1632.    VAR temp : STR255;
  1633.    BEGIN
  1634.        Set_Mouse(M_Arrow);
  1635.        temp := CONCAT('[3][The block is too large to|' ,
  1636.                           'insert at that position.|' ,
  1637.                           'Required row & col values:|' ,
  1638.                           'Col <= ' , col ,
  1639.                           '|Row <= ' , row , '][ Cancel ]' );
  1640.        alert := Do_Alert(temp,1)
  1641.    END; { BLOCK_TOO_BIG }
  1642.  
  1643. FUNCTION FIND_FIRST_AND_LAST;
  1644.    VAR i,
  1645.        pert_row,
  1646.        pert_col   : INTEGER;
  1647.        ptr        : CellPtr;
  1648.    BEGIN
  1649.       marks[5].row := n_rows;
  1650.       marks[5].col := n_cols;
  1651.       IF virtual_or_actual THEN BEGIN
  1652.          pert_row := logical_row_1;
  1653.          pert_col := logical_col_1;
  1654.          marks[6].row := logical_row_1;
  1655.          marks[6].col := logical_col_1
  1656.       END
  1657.       ELSE BEGIN
  1658.          pert_row := 1;
  1659.          pert_col := 1;
  1660.          marks[6].row := 1;
  1661.          marks[6].col := 1
  1662.       END;   
  1663.       FOR i := pert_row TO n_rows DO BEGIN
  1664.           ptr := data[i];
  1665.           WHILE ptr <> NIL DO BEGIN
  1666.              IF ptr^.c >= pert_col THEN BEGIN
  1667.                 IF i < marks[5].row THEN
  1668.                    marks[5].row := i;
  1669.                 IF ptr^.c < marks[5].col THEN
  1670.                    marks[5].col := ptr^.c;
  1671.                 marks[6].row := i;
  1672.                 IF ptr^.c > marks[6].col THEN
  1673.                    marks[6].col := ptr^.c
  1674.              END;      
  1675.              ptr := ptr^.next
  1676.           END
  1677.       END;
  1678.       IF (marks[5].row <= marks[6].row) AND 
  1679.          (marks[5].col <= marks[6].col) THEN
  1680.          find_first_and_last := TRUE
  1681.       ELSE
  1682.          find_first_and_last := FALSE
  1683.    END; { FIND_FIRST_AND_LAST }
  1684.    
  1685. PROCEDURE CLEAR_BUFFER;
  1686.    { clears out row 0, which is not used for data but rather as a buffer 
  1687.      for block moves and file i/o when "insert block at cursor" was chosen }
  1688.    VAR ptr : CellPtr;
  1689.    BEGIN
  1690.        ptr := data[0];      
  1691.        WHILE ptr <> NIL DO BEGIN 
  1692.           delete_cell(0,ptr^.c,FALSE);
  1693.           ptr := data[0]
  1694.        END
  1695.    END; { CLEAR_BUFFER }    
  1696.  
  1697. PROCEDURE DELETE_RANGE;
  1698.    VAR i,col : INTEGER;
  1699.        ptr   : CellPtr;
  1700.    BEGIN
  1701.        { Want to leave the dep list alone if a cell outside the range 
  1702.          accesses a cell inside the range and will exist after the deletes;
  1703.          that is the in range cell must be alive to have a dep list }
  1704.        i := s_row;
  1705.        WHILE i <= f_row DO BEGIN
  1706.           ptr := data[i];
  1707.           WHILE ptr <> NIL DO 
  1708.              IF (ptr^.c >= s_col) AND (ptr^.c <= f_col) THEN BEGIN
  1709.                 col := ptr^.c;
  1710.                 delete_cell(i,col,FALSE);
  1711.                 ptr := locate_cell(i,col); { may still be alive }
  1712.                 IF ptr = NIL THEN
  1713.                    ptr := data[i]
  1714.                 ELSE
  1715.                    ptr := ptr^.next;   
  1716.                 IF draw THEN
  1717.                    cell_on_screen(1,i,col,TRUE);
  1718.              END
  1719.              ELSE
  1720.                 ptr := ptr^.next;
  1721.           i := i+1
  1722.        END   
  1723.    END; { DELETE_RANGE }
  1724.  
  1725. PROCEDURE CLEAR_WORKSHEET;
  1726.    VAR i   : INTEGER;
  1727.    BEGIN
  1728.        { can NOT use Mark..Release here because apparently these commands 
  1729.          are buggy; may have to use them within same scope. When load_block
  1730.          at cursor followed by load_file, got > 1 cell being set to the same
  1731.          address, leading to crashes. Presumably, the 'free pointer space'
  1732.          list was not properly reinited by the Release, leading to 
  1733.          new_cell returning the same ptr for > 1 cell, in cells in different
  1734.          rows, in the first col! The following, however, DISPOSEs of each
  1735.          cell individually, and although slow for large sheets, it *works* }
  1736.        FOR i := 0 TO n_rows DO
  1737.            WHILE data[i] <> NIL DO
  1738.               delete_cell(i,data[i]^.c,TRUE);
  1739.        working_memory := original_memory; { should not have changed }
  1740.        block_set := FALSE;
  1741.        adjust_menu(FALSE);
  1742.        Send_Redraw(TRUE,0,0,screen_width,screen_height)
  1743.    END; { CLEAR_WORKSHEET }
  1744.  
  1745. PROCEDURE SIMULATE_MESSAGE;
  1746.    { fills message_buffer and inits inp_code so the caller may then call
  1747.      handle_message; valid for MN_Selected, WM_Arrowed, etc. Does NOT write to
  1748.      GEM's queue }
  1749.    BEGIN
  1750.        IF msg_type = MN_Selected THEN
  1751.           Menu_Hilight(main_menu,three);
  1752.        msg_area[0] := msg_type;
  1753.        msg_area[3] := three;
  1754.        msg_area[4] := four;
  1755.        handle_message;
  1756.        redraw_flag := FALSE
  1757.    END; { SIMULATE_MESSAGE }
  1758.    
  1759. PROCEDURE HIDE;
  1760.    BEGIN
  1761.       Obj_SetFlags(new_desk_ptr,mathmenu,
  1762.                    Obj_Flags(new_desk_ptr,mathmenu) | Hide_Tree);
  1763.       Obj_SetFlags(new_desk_ptr,trigmenu,
  1764.                    Obj_Flags(new_desk_ptr,trigmenu) | Hide_Tree);
  1765.       Obj_SetFlags(new_desk_ptr,statmenu,
  1766.                    Obj_Flags(new_desk_ptr,statmenu) | Hide_Tree);
  1767.       Obj_SetFlags(new_desk_ptr,finmenu,
  1768.                    Obj_Flags(new_desk_ptr,finmenu) | Hide_Tree);
  1769.       Obj_SetFlags(new_desk_ptr,boolmenu,
  1770.                    Obj_Flags(new_desk_ptr,boolmenu) | Hide_Tree);
  1771.       Obj_SetFlags(new_desk_ptr,tabmenu,
  1772.                    Obj_Flags(new_desk_ptr,tabmenu) | Hide_Tree)
  1773.    END; { HIDE }
  1774.    
  1775. PROCEDURE UNHIDE;
  1776.    BEGIN
  1777.       Obj_SetFlags(new_desk_ptr,menu,
  1778.                    Obj_Flags(new_desk_ptr,menu) & ~Hide_Tree)
  1779.    END; { UNHIDE }
  1780.  
  1781.  
  1782. (******************************)
  1783. (* End of Miscellaneous stuff *)
  1784. (******************************)
  1785.  
  1786. BEGIN
  1787. END.
  1788.  
  1789.  
  1790.  
  1791.  
  1792.