home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / GET10.ZIP / GETREAL.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-11-09  |  18.1 KB  |  674 lines

  1. procedure getreal
  2.  
  3.    (num_prompt:       screen_text;
  4.     atr,atc:          byte;        {screen co-ords}
  5.     low,high:         real;        {not checked if equal}
  6.     decimal_places:   integer;     {UserDefinedFormat no. if negative}
  7.     pattr,nattr:      byte;        {attributes}
  8. var number:           real;
  9.     default:          numstring);
  10.  
  11. {
  12.   The field size required for low or high, whichever is larger, controls
  13.   how big the field for entry of number is, unless low = high then nominal
  14.   field size of 13 digits is used (11 significant digits, plus sign and
  15.   decimal point). The value of decimal_places constrains the maximum range
  16.   of the number.
  17.  
  18.   implied_lowest  implied_highest  decimal_places
  19.  
  20.   -2147483648     217483647               0
  21.   -2147483648.0   217483647.0             1
  22.   -999999999.99   99999999.99             2
  23.   -99999999.999   9999999.999             3
  24.  
  25.   -9.9999999999   9.999999999            10
  26.  
  27.   The range may be constrained further by the parameters low and high.
  28.  
  29.   Dynamic validation is not attempted unless the range includes the number 0.
  30. }
  31. var
  32.     UserFormat:     UserFormatRec;
  33.     UnitSymbol:     SymbolStr;
  34.     UseSymbolFirst,
  35.     UseCommas,
  36.     UseParentheses,
  37.     UseSIdisplay:   boolean;
  38.  
  39.  
  40.     UserDefinedFormat,
  41.     finished,
  42.     negative,
  43.     numberok:       boolean;
  44.  
  45.     ThousandsDelimiter,
  46.     DecimalPointChar,
  47.     dchar:              char;
  48.  
  49.     lostr,histr,
  50.     instr:          numstring;
  51.  
  52.     implied_lowest,
  53.     implied_highest,
  54.     default_number,
  55.     last_number,
  56.     save_number:    real;
  57.  
  58.     negintsize,
  59.     posintsize,
  60.     sign,
  61.     FormatNo,
  62.     FormattingOverhead,
  63.     shift,
  64.     mantissa_size,
  65.     dot,
  66.     output_size,
  67.     nominal_field_size,
  68.     dotpos,
  69.     field_size:          byte;
  70.     code:                integer;
  71.  
  72. function RealToUserFormat (amount: real): numstring;
  73.  
  74. var
  75.     outstr:  numstring;
  76.     threes,
  77.     I:       integer;
  78.  
  79. begin
  80.   if (pos('-',UnitSymbol) > 0) or
  81.      (pos('+',UnitSymbol) > 0) then
  82.     UnitSymbol := ''; {null}
  83.  
  84.   outstr := RealToString(amount,decimal_places);
  85.   if amount < 0 then
  86.     delete(outstr,1,1); {delete minus sign}
  87.  
  88.   threes := pos('.',outstr);
  89.  
  90.   if UseSIdisplay and (threes > 0) then
  91.     outstr[threes] := DecimalPointChar;
  92.  
  93.   if UseCommas then
  94.     begin
  95.       if threes = 0 then
  96.         threes := length(outstr);
  97.  
  98.       dec(threes,3);
  99.  
  100.       while threes > 1 do
  101.         begin
  102.           insert(ThousandsDelimiter,outstr,threes);
  103.           dec(threes,3);
  104.         end;
  105.     end;
  106.  
  107.   if (amount < 0) then
  108.     if UseParentheses then
  109.     else outstr := '-' + outstr; {restore minus sign}
  110.  
  111.   if UseSymbolFirst then
  112.     outstr := UnitSymbol + outstr
  113.   else outstr := outstr + UnitSymbol;
  114.  
  115.   if (amount < 0) and UseParentheses then
  116.     outstr := '(' + outstr + ')';
  117.  
  118.   RealToUserFormat := outstr;
  119. end;
  120.  
  121.  
  122. function validation_on: boolean;
  123. {
  124.   Dynamic validation not attempted unless: low <= 0 <= high (and low <> high)
  125. }
  126. begin
  127.   validation_on := (low <> high) and
  128.                    (finished or ((low <= 0) and (high >= 0)));
  129.  
  130. end;
  131.  
  132.  
  133. function RealOutOfRange: boolean;
  134. begin
  135.   RealOutOfRange :=
  136.     (validation_on and ((number < low) or (number > high)));
  137. end;
  138.  
  139.  
  140. function field_complete: boolean;
  141. {
  142.   Field is complete when it's full but not with asterisks or when
  143.   min or maxlongint has been entered.
  144. }
  145. begin
  146.   field_complete :=
  147.     ((length(instr) = field_size) and (instr[1] <> '*')) or
  148.     (int(number) = maxlongint) or (int(number) = - maxlongint - 1);
  149. end;
  150.  
  151.  
  152. function mantissa_complete: boolean;
  153. {
  154.   checks for decimal_places characters entered after '.' in instr
  155. }
  156. begin
  157.   mantissa_complete := length(instr) - pos('.',instr) = decimal_places;
  158. end;
  159.  
  160.  
  161. procedure reset_number (reset_value: real);
  162. {
  163.   Resets number to value passed and converts it to a string for display
  164.   removing trailing 0's and decimal pt if necessary. Note: this procedure
  165.   needs to be executed even if number and reset_value are the same in case
  166.   instr is set to asterisks -- otherwise it will not be reset.
  167. }
  168. begin
  169.   number := reset_value;
  170.   last_number := number;
  171.   instr := RealToString(number,decimal_places);
  172.   if number = 0 then
  173.     instr := '';
  174.   negative := number < 0;
  175.   finished := false;
  176.  
  177.   if pos('.',instr) > 0 then {better than decimal_places > 0}
  178.  
  179.     begin
  180.       while (instr[length(instr)] = '0') do
  181.         dec(ord(instr[0])); {trailing 0s}
  182.  
  183.       if abs(int(number)) < billion then {chop the decimal pt}
  184.         if instr[length(instr)] = '.' then
  185.           dec(ord(instr[0]));
  186.     end;
  187.  
  188. end;
  189.  
  190.  
  191. procedure display_real_number (signed_number: real; SetFinished: boolean);
  192. {
  193.   Until the number is finished we shouldn't display any more decimal
  194.   places than are already in the string -- instr.
  195.  
  196. }
  197. var oldsize:     integer;
  198.     dotpos,
  199.     fattr:       byte;
  200.     display_str: numstring;
  201.  
  202.  
  203. procedure format_instr_for_display;
  204. {
  205.   This procedure is out of line for readability only
  206. }
  207. begin
  208.   if (signed_number = 0) and ZeroAsBlank then
  209.     instr := '';
  210.  
  211.   if UserDefinedFormat and not(instr = '') then
  212.     begin
  213.       instr := RealToUserFormat(signed_number);
  214.       if signed_number < 0 then
  215.         fattr := RedAttr;
  216.     end
  217.   else
  218.     begin
  219.       if ZeroFillNumbers then {takes precedence over ZeroAsBlank}
  220.         begin
  221.           if signed_number < 0 then
  222.             delete(instr,1,1);
  223.           while length(instr) < field_size do
  224.             instr := '0' + instr;
  225.           if signed_number < 0 then
  226.             instr[1] := '-';
  227.         end
  228.      end;
  229.  
  230.    if RightJustifyNumbers then
  231.      while length(instr) < output_size do
  232.        instr := ' ' + instr;
  233.  
  234.    display_str := instr;
  235. end;
  236.  
  237.  
  238. begin
  239.   finished := SetFinished;
  240.  
  241.   if EditingField and not finished and (fieldcursor <> 0) then
  242.     fattr := fieldcursor
  243.   else fattr := nattr;
  244.  
  245.   oldsize := length(instr); {must precede next line}
  246.   instr := RealToString(signed_number,decimal_places);
  247.  
  248.   if RealOutOfRange then
  249.     begin
  250.       if not escaped then
  251.         if EditingField then {no msg if field painting}
  252.           error2(NumberWord + brighten(instr) + outside_validation_range
  253.                 + brighten(lostr) + ToWord + brighten(histr));
  254.  
  255.       fillchar (instr[1],output_size,'*');      {* fill field + length byte}
  256.       instr[0] := chr(output_size);             {set length byte}
  257.       oldsize := output_size;
  258.     end;
  259.  
  260.   if finished then
  261.     if instr[1] <> '*' then
  262.       format_instr_for_display
  263.     else {do nothing}
  264.   else {decimal point will be a period, display as a comma if necessary}
  265.     begin
  266.       if negative and not (instr[1] in ['-','*']) then
  267.         instr := '-' + instr;
  268.       instr := copy(instr,1,oldsize);
  269.       dotpos := pos('.',instr);
  270.       display_str := instr;
  271.       if UseSIdisplay and (dotpos > 0) then
  272.         display_str[dotpos] := ',';
  273.     end;
  274.  
  275.   Qfill(atr,atc,1,output_size,fattr,' ');
  276.  
  277.   if RightJustifyNumberEntry then
  278.     Qwrite(atr,atc + output_size - length(instr),fattr,display_str)
  279.   else Qwrite(atr,atc,fattr,display_str);
  280. end;
  281.  
  282.  
  283. procedure enter_default_value;
  284. {
  285.   Enter default if default is a valid number (illegal default values are
  286.   ignored) and either ValidationOveride is true or the number falls within
  287.   the validation range.
  288.  
  289.   This procedure may be amended to prompt for confirmation of default values.
  290. }
  291. begin
  292.   val(default,default_number,code);
  293.  
  294.   if code = 0 then
  295.     begin
  296.       {use ConfirmDefault here}
  297.  
  298.       if ValidationOverride then
  299.         begin
  300.           low := 0;
  301.           high := 0;
  302.         end;
  303.  
  304.       if (low <> high) and
  305.         ((default_number < low) or
  306.         (default_number > high)) then
  307.         error2(No_privilege)
  308.       else reset_number(default_number);
  309.     end
  310.   else error2(default_is_invalid);
  311. end;
  312.  
  313.  
  314. procedure get_input;
  315. begin
  316.  
  317.     display_real_number(number,false);
  318.     if RightJustifyNumberEntry then
  319.       getdigit(atr,atc + output_size,0)
  320.     else getdigit(atr,atc + length(instr),0);
  321.     clearerror2;
  322.  
  323.     if scancode = 0 then {amend string}
  324.       begin
  325.  
  326.         dchar := chr(asciicode);
  327.  
  328.         if (length(instr) = field_size) and (dchar in ['0'..'9']) then
  329.           if instr[1] = '*' then
  330.             instr := ''
  331.           else dchar := ^I; {don't allow it to get longer}
  332.  
  333.         case dchar of
  334.  
  335.           ^H       : if instr[1] = '*' then
  336.                        reset_number(0)
  337.                      else if length(instr) > 0 then
  338.                        begin
  339.  
  340. (*                         if instr[length(instr)] = '.' then
  341.                            dec(ord(instr[0])); {remove decimal pt}
  342. *)
  343.                         dec(ord(instr[0]));
  344.                          negative := negative and (length(instr) > 0)
  345.                        end;
  346.  
  347.            '-'     : if instr[1] <> '*' then
  348.                        if (low < 0) or not validation_on then
  349.                          negative := not negative;
  350.  
  351.            '+'     : if instr[1] <> '*' then
  352.                        if (high > 0) or not validation_on then
  353.                          negative := false;
  354.  
  355.            ',','.' : if instr[1] <> '*' then
  356.                        if pos('.',instr) = 0 then
  357.                          begin
  358.                            if instr = '' then
  359.                              instr := '0'
  360.                            else if instr = '-' then
  361.                                   instr := '-0';
  362.  
  363.                            finished := decimal_places = 0;
  364.                            if not finished then
  365.                              instr := instr + '.';
  366.                          end;
  367.  
  368.            ^I      : finished := instr[1] <> '*';
  369.  
  370.            ^J,^M   : if instr[1] = '*' then
  371.                        reset_number(save_number)
  372.                      else
  373.  
  374.                        begin
  375.                          if (default <> '') and
  376.                            ((dchar = ^J) or (instr = '')) then
  377.                            enter_default_value;
  378.                          finished := true;
  379.                        end;
  380.  
  381.            ^[      : begin
  382.                        reset_number(save_number);
  383.                        escaped := true;
  384.                      end;
  385.  
  386.            ^Y      : reset_number(0);
  387.  
  388.            ^U      : reset_number(save_number);
  389.  
  390.            '0'..'9': { allow digits to be added except 0's to 0 or
  391.                        non zero digits to maxlongint or minlongint }
  392.  
  393.                      begin
  394.                        if (dchar = '0') then
  395.                          begin
  396.                            if ((instr = '0') or (instr = '-0')) then
  397.                              {do nothing}
  398.                            else instr := instr + dchar;
  399.                          end
  400.                        else
  401.                          begin
  402.                            if (copy(instr,1,10)  = strmaxlongint) or
  403.                               (copy(instr,1,11) = strminlongint) then
  404.                              {do nothing}
  405.                            else
  406.                              instr := instr + dchar;
  407.                          end;
  408.  
  409.                        { Now add decimal point if integer part of number
  410.                          must be complete. If the number just entered is
  411.                          outside any validation constraints the last digit
  412.                          and the decimal point added will be discarded.
  413.                        }
  414.  
  415.                        dot := pos('.',instr);
  416.  
  417.                        if (decimal_places > 0) and (dot = 0) then
  418.                          if ((instr[1] = '-')  and
  419.                             (length(instr) = negintsize)) or
  420.                             ((instr[1] <> '-') and
  421.                             (length(instr) = posintsize)) then
  422.                             instr := instr + '.';
  423.  
  424.                        {see if we are finished before the end of the field}
  425.  
  426.                        finished := (dot <> 0) and
  427.                                    (mantissa_complete and AutoTab);
  428.                      end;
  429.  
  430.         end; {case}
  431.  
  432.  
  433.         if not escaped and (instr[1] <> '*') then {turn string into number}
  434.           begin
  435.  
  436.             if negative then
  437.               begin
  438.                 if (instr[1] <> '-') then
  439.                   instr := '-' + instr;
  440.               end
  441.             else if instr[1] = '-' then
  442.                    delete(instr,1,1);
  443.  
  444.             last_number := number;
  445.  
  446.             real_value(instr,number,code); {string -> number}
  447.  
  448.             if code <> 0 then
  449.               begin
  450.                 error2(Number_must_be_in_range + brighten(strminlongint) +
  451.                        ToWord + brighten(strmaxlongint));
  452.                 reset_number(last_number);
  453.               end;
  454.  
  455.             finished := finished or (field_complete and AutoTab);
  456.         end; {not escaped}
  457.     end
  458.  
  459.   else {extended key}
  460.  
  461.     begin
  462.       action := get_edit(command);
  463.  
  464.       {amend here to control what extended keys are acceptable}
  465.  
  466.       case action of
  467.  
  468.         tabback: finished := instr[1] <> '*';
  469.  
  470.         reset: if ValidationOverride then {user has privilege}
  471.                  begin
  472.                    if low <> high then
  473.                      info2(Range_checking_suspended);
  474.  
  475.                    low := 0;
  476.                    high := 0;
  477.  
  478.                    reset_number(save_number);
  479.                  end
  480.                else error2(No_privilege);
  481.  
  482.         help,
  483.         upchar,
  484.         downchar,
  485.         leftchar,
  486.         rightchar,
  487.         pageup,
  488.         pagedown,
  489.         scrollup,
  490.         scrolldown,
  491.         goto_top,
  492.         goto_bottom,
  493.         abort,
  494.         exit_screen,
  495.         quit:
  496.  
  497.                  begin
  498.                    reset_number(save_number);
  499.                    finished := true;
  500.                  end;
  501.  
  502.       else error2(Invalid_key);
  503.       end; {case}
  504.     end;
  505. end;
  506.  
  507.  
  508. procedure warn_user_number_is_invalid;
  509. begin {reset number to last validated value}
  510.   error2(Enter_a_number_between + brighten(lostr) + AndWord +
  511.          brighten(histr));
  512.   reset_number(last_number);
  513. end;
  514.  
  515.  
  516. begin {getreal}
  517.   escaped := false;
  518.   finished := false;     {used in setting validation_on & therefore field_size}
  519.  
  520.   save_number := number;
  521.   last_number := number;
  522.  
  523.   if low > high then
  524.     begin
  525.       low := 0;
  526.       high := 0;
  527.     end;
  528.  
  529.   UserDefinedFormat :=
  530.     (decimal_places < 0) and (abs(decimal_places) <= TotalUserFormats);
  531.  
  532.   if UserDefinedFormat then
  533.     begin
  534.       FormatNo       := abs(decimal_places);
  535.       UserFormat     := UserFormatArray[FormatNo];
  536.       UnitSymbol     := UserFormat.UnitSymbol;
  537.       decimal_places := UserFormat.places;
  538.  
  539.       UseSymbolFirst := (UserFormat.UnitFormat AND SymbolFirst) > 0;
  540.       UseCommas      := (UserFormat.UnitFormat AND Commas)      > 0;
  541.       UseParentheses := (UserFormat.UnitFormat AND Parentheses) > 0;
  542.       UseSIdisplay   := (UserFormat.UnitFormat AND SIdisplay)   > 0;
  543.  
  544.       FormattingOverhead := length(UnitSymbol);
  545.     end
  546.   else
  547.     begin
  548.       FormattingOverhead := 0;
  549.       UseSIdisplay       := false;
  550.     end;
  551.  
  552.   if UseSIdisplay then
  553.     begin
  554.       ThousandsDelimiter := SI_ThousandsDelimiter; {' ' or (default) '.'}
  555.       DecimalPointChar   := ',';
  556.     end
  557.   else
  558.     begin
  559.       ThousandsDelimiter := ',';
  560.       DecimalPointChar   := '.';
  561.     end;
  562.  
  563.  
  564.   if not decimal_places in [0..10] then
  565.     decimal_places := DecimalDefault;
  566. {
  567.   No of decimal places implies max and min possible values for low and high.
  568.   These will be used if low = high (user specified range checking off) or if
  569.   the value of either low or high specified is not appropriate for the given
  570.   number of decimal places.
  571. }
  572.   if decimal_places < 2 then
  573.     implied_highest := maxlongint
  574.   else
  575.     begin
  576.       implied_highest := 999999999.99; {2 decimal places}
  577.       shift := decimal_places - 2;
  578.       if shift > 0 then
  579.         implied_highest := implied_highest / powerof(10,shift);
  580.     end;
  581.  
  582.   if decimal_places < 2 then
  583.     implied_lowest := pred(-maxlongint)
  584.   else implied_lowest := -implied_highest;
  585.  
  586.   if (high > implied_highest) or (low  < implied_lowest) or (low = high) then
  587.     begin
  588.       high := implied_highest;
  589.       low  := implied_lowest;
  590.     end;
  591.  
  592. {
  593.   ensure equivalence of string input and real limits
  594. }
  595.  
  596.   strval(high,decimal_places);
  597.   strval(low,decimal_places);
  598.  
  599. {
  600.   Find out how many digits to allow for input. Lower number may be longer,
  601.   e.g., with a range of -999.9 to 99.9.
  602. }
  603.  
  604.   if decimal_places = 0 then
  605.     nominal_field_size := 11
  606.   else nominal_field_size := 13;
  607.  
  608.   lostr := RealToString(low,decimal_places);
  609.   histr := RealToString(high,decimal_places);
  610.  
  611.   If UseSIdisplay then
  612.     begin {ensure error message strings use correct character for decimal pt}
  613.       dotpos := pos('.',lostr);
  614.       if dotpos > 0 then
  615.         lostr[dotpos] := ',';
  616.       dotpos := pos('.',histr);
  617.       if dotpos > 0 then
  618.       histr[dotpos] := ',';
  619.     end;
  620.  
  621.  
  622.   negintsize := digits(low);
  623.   posintsize := digits(high);
  624.  
  625.   if ValidationOverride or not validation_on then
  626.     field_size := nominal_field_size
  627.   else field_size := maxW(ord(lostr[0]),ord(histr[0]));
  628.  
  629.   mantissa_size := decimal_places;
  630.   if decimal_places > 0 then
  631.     inc(mantissa_size); {allow for decimal pt}
  632.  
  633.   if UserDefinedFormat then
  634.     begin
  635.       if lostr[1] = '-' then
  636.          begin
  637.            sign := 1;
  638.            if UseParentheses then
  639.              inc(FormattingOverhead);  {'-' turns to '('; allow for ')' }
  640.          end
  641.       else sign := 0;
  642.  
  643.       if UseCommas then {find out how many commas to allow for}
  644.         inc(FormattingOverhead,pred(field_size - sign - mantissa_size) div 3);
  645.     end;
  646.  
  647.   output_size := field_size + FormattingOverhead;
  648.  
  649.   SetCursor(CursorOn or CursorUnderline);
  650.   clearerror2;
  651.   numberok := false;
  652.  
  653.   display_prompt(num_prompt,atr,atc,pattr,output_size);
  654.  
  655.   if not PaintingFields then
  656.     repeat
  657.       reset_number(number);
  658.       repeat
  659.         get_input
  660.       until finished or escaped or RealOutOfRange;
  661.  
  662.       numberok := (not RealOutOfRange) or Escaped;
  663.       if not numberok then
  664.         warn_user_number_is_invalid;
  665.  
  666.     until numberok
  667.   else
  668.     reset_number(number);
  669.  
  670.   display_real_number(number,true);
  671.   SetCursor(CursorOff);
  672. end; {getreal}
  673.  
  674.