home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0000 - 0009 / ibm0000-0009 / ibm0003.tar / ibm0003 / TPOWER54.ZIP / DEMOSRC.ARC / PTRPT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-07-10  |  32.0 KB  |  1,203 lines

  1. {$S-,R-,V-,I-,B-,F-}
  2. {$M 10000,5000,65500}
  3.  
  4. {$I TPDEFINE.INC}
  5.  
  6. {*********************************************************}
  7. {*                    PTRPT.PAS 5.07                     *}
  8. {*         Report Generator for PTIME Data Files         *}
  9. {*     An example program for Turbo Professional 5.0     *}
  10. {*        Copyright (c) TurboPower Software 1987.        *}
  11. {* Portions copyright (c) Sunny Hill Software 1985, 1986 *}
  12. {*     and used under license to TurboPower Software     *}
  13. {*                 All rights reserved.                  *}
  14. {*********************************************************}
  15.  
  16. program PTime_Report;
  17.   {-Report generator for PTIME data files}
  18.  
  19. uses
  20.   Dos,                       {standard DOS/BIOS routines}
  21.   Printer,                   {standard printer unit}
  22.   TpInt24,                   {Turbo Professional critical error handler}
  23.   TpString,                  {Turbo Professional string handling routines}
  24.   TpDos,                     {Turbo Professional disk/file management, etc.}
  25.   TpCrt,                     {Turbo Professional CRT unit}
  26.   {$IFDEF UseMouse}
  27.   TpMouse,                   {Turbo Professional mouse routines}
  28.   {$ENDIF}
  29.   TpDate,                    {Turbo Professional date/time routines}
  30.   TpEntry,                   {Turbo Professional data entry routines}
  31.   TpPick,                    {Turbo Professional pick list manager}
  32.   TpDir,                     {Turbo Professional directory picker}
  33.   TPMenu;                    {Turbo Professional menu system}
  34.  
  35.   {general declarations}
  36. type
  37.   String8 = string[8];
  38.   String64 = string[64];
  39.   String80 = string[80];
  40.  
  41.   {menu system}
  42. const
  43.   Color1 : MenuColorArray = ($09, $1F, $09, $1F, $0B, $09, $07, $07);
  44.   Mono1 : MenuColorArray = ($0F, $70, $07, $70, $0F, $07, $07, $07);
  45.   Frame1 : FrameArray = '╒╘╕╛═│';
  46. var
  47.   RootMenu : Menu;           {our menu}
  48.   Key : MenuKey;             {key returned by menu system}
  49.   MenuCh : Char;             {char pressed to exit menu system}
  50.   AllDone : Boolean;         {true to exit main menu loop}
  51.   ColorsPtr : ^MenuColorArray; {points to Mono1 or Color1}
  52.  
  53.   {database}
  54. type
  55.   BillingRec =               {this *must* agree with declaration in PTIME}
  56.     record
  57.       Starting : DateTimeRec;
  58.       Stopping : DateTimeRec;
  59.       Elapsed : Time;
  60.       AllowUpdate : Boolean;
  61.       Comment : string[76];
  62.     end;
  63.   SignatureType = string[7];
  64. const
  65.   Signature : SignatureType = 'PTime50';
  66.   SecsPerHour = 3600;        {60 minutes * 60 seconds}
  67.   DateFormat : string[08] = 'MM/dd/yy';
  68.   AmPmFormat : string[11] = 'HH:mm:ss te';
  69.   TimeFormat : string[08] = 'HH:mm:ss';
  70. var
  71.   BillingName : String64;    {name of current data file}
  72.   BillingFile : file of BillingRec; {current data file}
  73.   BlankBill : BillingRec;    {blank billing record}
  74.   ESR : ESrecord;            {edit screen record}
  75.   Rate : Float;              {hourly billing rate}
  76.   TotalRecs,                 {total # of records in data file}
  77.   CurrentRec : Word;         {record pointer for browsing}
  78.   DataFileIsOpen : Boolean;  {true if a data file is open}
  79.  
  80.   {screen}
  81. var
  82.   InitSL : Word;             {initial scan lines for cursor}
  83.   Dim,                       {video attributes}
  84.   Bright,
  85.   Reverse : Byte;
  86.   PickColors : PickColorArray;
  87.  
  88.   {messages}
  89. const
  90.   ZeroEntries : string[24] = 'Data file has no entries';
  91.   ChoosePrompt : string[64] =
  92.   'Use <PgUp> and <PgDn> to move, <^Enter> to select, <Esc> to quit';
  93.   BrowsePrompt : string[44] =
  94.   'Use <PgUp> and <PgDn> to move, <Esc> to quit';
  95.   ReportError : string[28] = 'Error writing to report file';
  96.   PrintError : string[24] = 'Error writing to printer';
  97.  
  98.   {miscellaneous}
  99. const
  100.   Esc = #27;
  101.   Enter = ^M;
  102.   NullString : string[1] = '';
  103. var
  104.   Blank : String80;
  105.   BlankLen : Byte absolute Blank;
  106.  
  107.   {$IFDEF UseMouse}
  108. const
  109.   {used to translate mouse buttons to keys}
  110.   ButtonCodes : array[$E9..$EF] of Word = (
  111.     $011B,                   {all three buttons         = ESC}
  112.     $011B,                   {right and center buttons  = ESC}
  113.     $011B,                   {left and center buttons   = ESC}
  114.     $011B,                   {center button             = ESC}
  115.     $011B,                   {both buttons              = ESC}
  116.     $011B,                   {right button              = ESC}
  117.     $1C0D);                  {left button               = Enter}
  118.   {$ENDIF}
  119.  
  120.   {$IFDEF UseMouse}
  121.   function ReadKeyWord : Word;
  122.     {-Get a key from the keyboard or mouse}
  123.   var
  124.     I : Word;
  125.   begin
  126.     I := ReadKeyOrButton;
  127.     case Hi(I) of
  128.       $E9..$EF :
  129.         ReadKeyWord := ButtonCodes[Hi(I)];
  130.     else
  131.       ReadKeyWord := I
  132.     end;
  133.   end;
  134.   {$ENDIF}
  135.  
  136.   function Decimal(I, Width : Word; DoZero : Boolean) : String8;
  137.     {-Return a string representing a decimal number}
  138.   var
  139.     S : String8;
  140.     SLen : Byte absolute S;
  141.   begin
  142.     Str(I:Width, S);
  143.     if DoZero then
  144.       for I := 1 to SLen do
  145.         if S[I] = ' ' then
  146.           S[I] := '0';
  147.     Decimal := S;
  148.   end;
  149.  
  150.   function MoneyString(R : Float) : string;
  151.     {-Return a string in $999,999,999.99 format}
  152.   var
  153.     S : string;
  154.   begin
  155.     S := Real2Str(R, 0, 2);
  156.     MergePicture('$999,999,999.99', S, S, 0);
  157.     MoneyString := Trim(S);
  158.   end;
  159.  
  160.   function HoursMinsSecs(Days : Word; T : Time) : string;
  161.     {-Return a string showing the number of hours, minutes, and seconds}
  162.   var
  163.     Hours, Minutes, Seconds : Byte;
  164.   begin
  165.     TimeToHMS(T, Hours, Minutes, Seconds);
  166.     HoursMinsSecs := Decimal((Days*24)+Hours, 0, False)+' hrs, '+
  167.     Decimal(Minutes, 0, False)+' mins, '+Decimal(Seconds, 0, False)+' secs';
  168.   end;
  169.  
  170.   procedure ClearPromptLine;
  171.     {-Clear prompt line at bottom of screen}
  172.   begin
  173.     {$IFDEF UseMouse}
  174.     HideMouse;
  175.     {$ENDIF}
  176.  
  177.     BlankLen := 80;
  178.     FastWrite(Blank, 25, 1, Dim);
  179.  
  180.     {$IFDEF UseMouse}
  181.     ShowMouse;
  182.     {$ENDIF}
  183.   end;
  184.  
  185.   procedure ClearDisplayWindow;
  186.     {-Clear the window where records are displayed}
  187.   begin
  188.     {$IFDEF UseMouse}
  189.     HideMouse;
  190.     {$ENDIF}
  191.  
  192.     Window(1, 4, 80, 24);
  193.     ClrScr;
  194.  
  195.     {$IFDEF UseMouse}
  196.     ShowMouse;
  197.     {$ENDIF}
  198.   end;
  199.  
  200.   procedure Prompt(Msg : String80);
  201.     {-Display a prompt}
  202.   begin
  203.     {$IFDEF UseMouse}
  204.     HideMouse;
  205.     {$ENDIF}
  206.  
  207.     FastWrite(Pad(Msg, 80), 25, 1, Bright);
  208.  
  209.     {$IFDEF UseMouse}
  210.     ShowMouse;
  211.     {$ENDIF}
  212.   end;
  213.  
  214.   procedure ErrorMessage(Msg : string; Beep : Boolean);
  215.     {-Display an error message at the prompt line}
  216.   var
  217.     ChWord : Word;
  218.   begin
  219.     if Msg[Length(Msg)] <> '.' then begin
  220.       Inc(Msg[0]);
  221.       Msg[Length(Msg)] := '.';
  222.     end;
  223.     Prompt(Msg+' Press any key...');
  224.     if Beep then
  225.       RingBell;
  226.     ChWord := ReadKeyWord;
  227.     ClearPromptLine;
  228.   end;
  229.  
  230.   {$F+}
  231.   procedure ErrorHandler(var ESR : ESrecord; Code : Byte; Msg : string);
  232.     {-Display error messages}
  233.   var
  234.     CursorSL, CursorXY : Word;
  235.   begin
  236.     {Store cursor position and shape, then hide it}
  237.     GetCursorState(CursorXY, CursorSL);
  238.     HiddenCursor;
  239.  
  240.     {display error message}
  241.     ErrorMessage(Msg, True);
  242.  
  243.     case Code of
  244.       InitError, OverflowError, MemoryError, ParamError :
  245.         begin
  246.           {a fatal error: unhide cursor and clear the screen}
  247.           {$IFDEF UseMouse}
  248.           HideMouse;
  249.           {$ENDIF}
  250.           NormalCursor;
  251.           Window(1, 1, 80, 25);
  252.           ClrScr;
  253.         end;
  254.     else
  255.       {Restore cursor position and shape}
  256.       RestoreCursorState(CursorXY, CursorSL);
  257.     end;
  258.   end;
  259.  
  260.   procedure DisplayHelpPrompt(var ESR : ESrecord);
  261.     {-Display a help prompt for the current field}
  262.   var
  263.     S : string[80];
  264.   begin
  265.     {no prompts in read-only mode}
  266.     if ESR.ReadOnlyFlag then
  267.       Exit;
  268.  
  269.     case ESR.CurrentID of
  270.       {--Field 0 is the name of the data file (protected)--}
  271.       {--Field 1 is the record number (protected)--}
  272.       02 : S := 'Enter the date when timing started';
  273.       03 : S := 'Enter the time of day when timing started';
  274.       04 : S := 'Enter the date when timing stopped';
  275.       05 : S := 'Enter the time of day when timing stopped';
  276.       06 : S := 'Enter the cumulative elapsed time, adjusted for pauses';
  277.       07 : S := 'Allow PTIME to update this record?';
  278.       08 : S := 'Enter comment indicating what work was done during this period';
  279.     else S := '';
  280.     end;
  281.     Prompt(S);
  282.   end;
  283.   {$F-}
  284.  
  285.   function YesOrNo(Msg : string; Default : Boolean) : Boolean;
  286.     {-Prompt for answer to yes/no question}
  287.   const
  288.     YorN : array[Boolean] of Char = ('N', 'Y');
  289.   var
  290.     ChWord : Word;
  291.     Ch : Char absolute ChWord;
  292.   begin
  293.     ClearPromptLine;
  294.     Msg := Msg+' ['+YorN[Default]+']';
  295.     Prompt(Msg);
  296.     repeat
  297.       ChWord := ReadKeyWord;
  298.       Ch := Upcase(Ch);
  299.       if Ch = ^M then
  300.         Ch := YorN[Default];
  301.     until (Ch = 'Y') or (Ch = 'N');
  302.     YesOrNo := Ch = 'Y';
  303.     ClearPromptLine;
  304.   end;
  305.  
  306.   function GetFileNameShell(var FName : String64) : Boolean;
  307.     {-Shell for TpDir.GetFileName}
  308.   var
  309.     I : Word;
  310.   begin
  311.     {use TPDIR to put up a directory list}
  312.     ShowSizeDateTime := True;
  313.     PickSrch := CharPickSrch;
  314.     I := GetFileName(FName, AnyFile, 20, 5, 23, 1, PickColors, FName);
  315.     GetFileNameShell := (I = 0) and (Length(FName) <> 0);
  316.   end;
  317.  
  318.   function GetFName(Prompt : string; var FName : String64;
  319.                     CheckForFile : Boolean) : Boolean;
  320.     {-Get a filename from the user}
  321.   const
  322.     S : String64 = '';
  323.   var
  324.     Escaped : Boolean;
  325.   begin
  326.     ClearPromptLine;
  327.  
  328.     if Length(FName) <> 0 then
  329.       S := FName
  330.     else if CheckForFile and (Length(S) = 0) then
  331.       S := '*.*';
  332.  
  333.     {edit FName}
  334.     ESfieldAttr := ESstringAttr;
  335.     ESctrlAttr := ESstringAttr;
  336.     EditString(Prompt, 25, 1, 64, '', 0, Escaped, S);
  337.     FName := S;
  338.  
  339.     if Escaped then
  340.       GetFName := False
  341.     else if CheckForFile then
  342.       GetFName := GetFileNameShell(FName)
  343.     else
  344.       GetFName := Length(FName) <> 0;
  345.  
  346.     {clean up}
  347.     ClearPromptLine;
  348.   end;
  349.  
  350.   function ValidateBill : Boolean;
  351.     {-Validate the contents of BlankBill}
  352.   var
  353.     Days : Word;
  354.     Secs : LongInt;
  355.   begin
  356.     ValidateBill := False;
  357.  
  358.     with BlankBill do begin
  359.       {compare starting and stopping times}
  360.       if (Stopping.D < Starting.D) or
  361.       ((Stopping.D = Starting.D) and (Stopping.T < Starting.T)) then begin
  362.         ErrorMessage('Start time must be later than stop time', True);
  363.         Exit;
  364.       end;
  365.  
  366.       {check for excessive elapsed time}
  367.       DateTimeDiff(Starting, Stopping, Days, Secs);
  368.       if (Days > 0) then begin
  369.         ErrorMessage('Stop time must be within 24 hours of start time', True);
  370.         Exit;
  371.       end;
  372.       if Elapsed > Secs then begin
  373.         ErrorMessage('Elapsed time exceeds actual difference in time', True);
  374.         Exit;
  375.       end;
  376.     end;
  377.  
  378.     ValidateBill := True;
  379.   end;
  380.  
  381.   function ReadRecord(RecNum : Word; var BR : BillingRec) : Boolean;
  382.     {-Read the RecNum'th record into BR. Returns false for I/O error}
  383.   begin
  384.     {assume failure}
  385.     ReadRecord := False;
  386.  
  387.     {seek to the RecNum'th record}
  388.     Seek(BillingFile, RecNum);
  389.     if (Int24Result <> 0) then
  390.       ErrorMessage('Seek error reading record #'+Decimal(RecNum, 0, False), True)
  391.     else begin
  392.       Read(BillingFile, BR);
  393.       if Int24Result <> 0 then
  394.         ErrorMessage('Unable to read record #'+Decimal(RecNum, 0, False), True)
  395.       else
  396.         ReadRecord := True;
  397.     end;
  398.   end;
  399.  
  400.   function WriteRecord(RecNum : Word; var BR : BillingRec) : Boolean;
  401.     {-Write BR as the RecNum'th record. Returns false for I/O error}
  402.   begin
  403.     {assume failure}
  404.     WriteRecord := False;
  405.  
  406.     {seek to the RecNum'th record}
  407.     Seek(BillingFile, RecNum);
  408.     if (Int24Result <> 0) then
  409.       ErrorMessage('Error seeking to record #'+Decimal(RecNum, 0, False), True)
  410.     else begin
  411.       Write(BillingFile, BR);
  412.       if Int24Result <> 0 then
  413.         ErrorMessage('Unable to write record #'+Decimal(RecNum, 0, False), True)
  414.       else begin
  415.         WriteRecord := True;
  416.         if RecNum > TotalRecs then
  417.           TotalRecs := RecNum;
  418.       end;
  419.     end;
  420.   end;
  421.  
  422.   function DeleteRecord(RecNum : Word) : Boolean;
  423.     {-Delete the RecNum'th record. Returns false for I/O error}
  424.   var
  425.     BR : BillingRec;
  426.     I : Word;
  427.   begin
  428.     {assume failure}
  429.     DeleteRecord := False;
  430.  
  431.     {shuffle the records after RecNum back}
  432.     for I := Succ(RecNum) to TotalRecs do begin
  433.       if not ReadRecord(I, BR) then
  434.         Exit;
  435.       if not WriteRecord(Pred(I), BR) then
  436.         Exit;
  437.     end;
  438.  
  439.     {position file pointer and truncate file}
  440.     Seek(BillingFile, TotalRecs);
  441.     if Int24Result <> 0 then
  442.       Exit;
  443.     Truncate(BillingFile);
  444.     if Int24Result <> 0 then
  445.       Exit;
  446.  
  447.     {decrement count of total records}
  448.     Dec(TotalRecs);
  449.     DeleteRecord := True;
  450.   end;
  451.  
  452.   function OpenBilling : Boolean;
  453.     {-Open the billing file, returning a success flag}
  454.   var
  455.     BR : BillingRec;
  456.     BSig : string[5] absolute BR;
  457.     I : Word;
  458.   begin
  459.     {assume failure}
  460.     OpenBilling := False;
  461.     DataFileIsOpen := False;
  462.  
  463.     {expand the name}
  464.     BillingName := FullPathName(BillingName);
  465.  
  466.     {try to open the file}
  467.     Assign(BillingFile, BillingName);
  468.     Reset(BillingFile);
  469.     if Int24Result <> 0 then begin
  470.       ErrorMessage('Unable to open data file', True);
  471.       Exit;
  472.     end;
  473.  
  474.     {read the first entry}
  475.     Read(BillingFile, BR);
  476.  
  477.     {check for read error}
  478.     if Int24Result <> 0 then begin
  479.       ErrorMessage('Error reading from data file', True);
  480.       Close(BillingFile);
  481.       I := Int24Result;
  482.     end
  483.     else if (BSig <> Signature) then begin
  484.       {signature invalid}
  485.       ErrorMessage('Not a valid data file', True);
  486.       Close(BillingFile);
  487.       I := Int24Result;
  488.     end
  489.     else begin
  490.       {success}
  491.       DataFileIsOpen := True;
  492.       TotalRecs := Pred(FileSize(BillingFile));
  493.       CurrentRec := 1;
  494.       OpenBilling := True;
  495.     end;
  496.   end;
  497.  
  498.   function GetRate : Boolean;
  499.     {-Get the user's billing rate. Returns true if Rate was specified.}
  500.   var
  501.     Escaped : Boolean;
  502.     S : string[7];
  503.   begin
  504.     ClearPromptLine;
  505.  
  506.     {edit the rate}
  507.     ESfieldAttr := ESstringAttr;
  508.     ESctrlAttr := ESstringAttr;
  509.     S := Real2Str(Rate, 0, 2);
  510.     EditString('Enter rate per hour: ', 25, 1, 7, '$999.99', 0, Escaped, S);
  511.     GetRate := not Escaped;
  512.     if not Escaped then begin
  513.       {get rid of blanks and floating dollar sign}
  514.       S := Trim(S);
  515.       Delete(S, 1, 1);
  516.  
  517.       if not Str2Real(S, Rate) then begin
  518.         Rate := 0;
  519.         GetRate := False;
  520.       end;
  521.     end;
  522.  
  523.     {clean up}
  524.     ClearPromptLine;
  525.   end;
  526.  
  527.   function ChooseRecord(var Escaped : Boolean; Browsing : Boolean) : Boolean;
  528.     {-Choose a record. Returns false for I/O error}
  529.   var
  530.     LastRec : Word;
  531.     Done : Boolean;
  532.   begin
  533.     ChooseRecord := False;
  534.     Escaped := False;
  535.  
  536.     if CurrentRec > TotalRecs then
  537.       CurrentRec := TotalRecs;
  538.  
  539.     {display help prompt}
  540.     if Browsing then
  541.       Prompt(Center(BrowsePrompt, 80))
  542.     else
  543.       Prompt(Center(ChoosePrompt, 80));
  544.  
  545.     Done := False;
  546.     LastRec := 0;
  547.     repeat
  548.       {read the next record}
  549.       if CurrentRec <> LastRec then
  550.         if not ReadRecord(CurrentRec, BlankBill) then
  551.           Exit;
  552.       LastRec := CurrentRec;
  553.  
  554.       {display the record}
  555.       case EditScreen(ESR, ESR.CurrentID, True) of
  556.         ESquit :             {Esc}
  557.           begin
  558.             Escaped := True;
  559.             Done := True;
  560.           end;
  561.         ESdone :             {^Enter, ^KD, ^KQ}
  562.           Done := not Browsing;
  563.         ESprevRec :          {PgUp}
  564.           if CurrentRec > 1 then
  565.             Dec(CurrentRec);
  566.         ESnextRec :          {PgDn}
  567.           if CurrentRec < TotalRecs then
  568.             Inc(CurrentRec);
  569.       end;
  570.  
  571.     until Done;
  572.  
  573.     ClearPromptLine;
  574.     ChooseRecord := True;
  575.   end;
  576.  
  577.   function DataFileOpen : Boolean;
  578.     {-Return true if a file is already open, else prompt for file name
  579.       and try to open the file}
  580.   begin
  581.     if DataFileIsOpen then
  582.       {file already open}
  583.       DataFileOpen := True
  584.     else begin
  585.       {no file open, get filename}
  586.       ClearPromptLine;
  587.  
  588.       {if a name is specified, try to open the file}
  589.       if GetFName('Data file: ', BillingName, True) then
  590.         DataFileOpen := OpenBilling
  591.       else
  592.         DataFileOpen := False;
  593.     end;
  594.   end;
  595.  
  596.   procedure Append;
  597.     {-Append a new database entry}
  598.   var
  599.     Result : EStype;
  600.   begin
  601.     {exit if we have no data}
  602.     if not DataFileOpen then
  603.       Exit;
  604.  
  605.     {create an empty record}
  606.     CurrentRec := Succ(TotalRecs);
  607.     FillChar(BlankBill, SizeOf(BlankBill), $FF);
  608.     BlankBill.AllowUpdate := True;
  609.     BlankBill.Comment[0] := #0;
  610.  
  611.     {edit the blank record}
  612.     repeat
  613.       Result := EditScreen(ESR, ESR.CurrentID, False);
  614.     until (Result = ESquit) or ((Result = ESdone) and ValidateBill);
  615.  
  616.     {write it}
  617.     if Result = ESquit then
  618.       Dec(CurrentRec)
  619.     else if not WriteRecord(CurrentRec, BlankBill) then begin
  620.       AllDone := True;
  621.       Exit;
  622.     end;
  623.  
  624.     {clear the display window}
  625.     ClearDisplayWindow;
  626.   end;
  627.  
  628.   procedure Browse;
  629.     {-Browse through database entries}
  630.   var
  631.     Escaped : Boolean;
  632.   begin
  633.     {exit if we have no data}
  634.     if not DataFileOpen then
  635.       Exit;
  636.  
  637.     {make sure there's something to browse through}
  638.     if TotalRecs = 0 then begin
  639.       ErrorMessage(ZeroEntries, True);
  640.       Exit;
  641.     end;
  642.  
  643.     {clear the display window}
  644.     ClearDisplayWindow;
  645.  
  646.     {call ChooseRecord routine}
  647.     if not ChooseRecord(Escaped, True) then begin
  648.       AllDone := True;
  649.       Exit;
  650.     end;
  651.  
  652.     {clear the display window}
  653.     ClearDisplayWindow;
  654.   end;
  655.  
  656.   procedure Calculate;
  657.     {-Calculate total hours, earnings}
  658.   var
  659.     I : Word;
  660.     TotalSecs : LongInt;
  661.     Earnings : Float;
  662.     BR : BillingRec;
  663.     St : String80;
  664.     Days : Word;
  665.   begin
  666.     if not DataFileOpen then
  667.       Exit;
  668.  
  669.     {make sure there's something to calculate}
  670.     if TotalRecs = 0 then begin
  671.       ErrorMessage(ZeroEntries, True);
  672.       Exit;
  673.     end;
  674.  
  675.     {get rate per hour}
  676.     if not GetRate then
  677.       Exit;
  678.  
  679.     {calculate total time}
  680.     TotalSecs := 0;
  681.     Days := 0;
  682.     for I := 1 to TotalRecs do
  683.       if ReadRecord(I, BR) then begin
  684.         Inc(TotalSecs, BR.Elapsed);
  685.         if TotalSecs > MaxTime then begin
  686.           Inc(Days);
  687.           Dec(TotalSecs, SecondsInDay);
  688.         end;
  689.       end
  690.       else begin
  691.         AllDone := True;
  692.         Exit;
  693.       end;
  694.  
  695.     {display time, earnings}
  696.     Earnings := Rate*(((Days*1.0*SecondsInDay)+TotalSecs)/SecsPerHour);
  697.     St := HoursMinsSecs(Days, TotalSecs)+' at '+MoneyString(Rate)+'/hr = '+
  698.     MoneyString(Earnings);
  699.     ErrorMessage(St, False);
  700.   end;
  701.  
  702.   procedure DeleteEntries;
  703.     {-Delete database entries}
  704.   var
  705.     Escaped : Boolean;
  706.   begin
  707.     if not DataFileOpen then
  708.       Exit;
  709.  
  710.     {make sure there's something to delete}
  711.     if TotalRecs = 0 then begin
  712.       ErrorMessage(ZeroEntries, True);
  713.       Exit;
  714.     end;
  715.  
  716.     {clear the display window}
  717.     ClearDisplayWindow;
  718.  
  719.     repeat
  720.       {choose a record}
  721.       if not ChooseRecord(Escaped, False) then begin
  722.         AllDone := True;
  723.         Exit;
  724.       end;
  725.  
  726.       {confirm that it is to be deleted}
  727.       if not Escaped then
  728.         if YesOrNo('Are you sure you want to delete this record?', False) then
  729.           if not DeleteRecord(CurrentRec) then begin
  730.             AllDone := True;
  731.             Exit;
  732.           end;
  733.     until Escaped;
  734.  
  735.     {clear the display window}
  736.     ClearDisplayWindow;
  737.   end;
  738.  
  739.   procedure EditEntries;
  740.     {-Edit database entries}
  741.   var
  742.     Result : EStype;
  743.   begin
  744.     if not DataFileOpen then
  745.       Exit;
  746.  
  747.     {make sure there's something to edit}
  748.     if TotalRecs = 0 then begin
  749.       ErrorMessage(ZeroEntries, True);
  750.       Exit;
  751.     end;
  752.  
  753.     {clear the display window}
  754.     ClearDisplayWindow;
  755.  
  756.     repeat
  757.       {read in the record}
  758.       if not ReadRecord(CurrentRec, BlankBill) then begin
  759.         AllDone := True;
  760.         Exit;
  761.       end;
  762.  
  763.       {edit the record}
  764.       repeat
  765.         Result := EditScreen(ESR, ESR.CurrentID, False);
  766.       until (Result = ESquit) or ValidateBill;
  767.  
  768.       {write the record}
  769.       if Result <> ESquit then
  770.         if not WriteRecord(CurrentRec, BlankBill) then begin
  771.           AllDone := True;
  772.           Exit;
  773.         end;
  774.  
  775.       {next/previous record?}
  776.       case Result of
  777.         ESnextRec :          {next record}
  778.           if CurrentRec < TotalRecs then
  779.             Inc(CurrentRec);
  780.         ESprevRec :          {previous record}
  781.           if CurrentRec > 1 then
  782.             Dec(CurrentRec);
  783.       end;
  784.     until (Result = ESquit) or (Result = ESdone);
  785.  
  786.     {clear the display window}
  787.     ClearDisplayWindow;
  788.   end;
  789.  
  790.   procedure SelectFile;
  791.     {-Select a new data file}
  792.   var
  793.     SaveBillingName : String64;
  794.     SaveOpen : Boolean;
  795.   begin
  796.     {save the current file name, open status, etc.}
  797.     SaveBillingName := BillingName;
  798.     SaveOpen := DataFileIsOpen;
  799.     if SaveOpen then begin
  800.       Close(BillingFile);
  801.       if Int24Result <> 0 then begin
  802.         ErrorMessage('Error while closing current data file', True);
  803.         AllDone := True;
  804.         Exit;
  805.       end;
  806.     end;
  807.     DataFileIsOpen := False;
  808.  
  809.     {if new name not selected, restore old one}
  810.     if not DataFileOpen then begin
  811.       BillingName := SaveBillingName;
  812.       if SaveOpen then begin
  813.         BillingName := SaveBillingName;
  814.         if not OpenBilling then
  815.           AllDone := True;
  816.       end;
  817.     end;
  818.   end;
  819.  
  820.   function QuitProgram : Boolean;
  821.     {-Returns true if user wants to quit or no data file is open}
  822.   begin
  823.     if not DataFileIsOpen then
  824.       QuitProgram := True
  825.     else
  826.       QuitProgram := YesOrNo('Are you sure you wish to quit?', False);
  827.   end;
  828.  
  829.   procedure Report(ToPrinter : Boolean);
  830.     {-Write a report to disk}
  831.   const
  832.     FName : String64 = '';
  833.   var
  834.     F : Text;
  835.     FP : ^Text;
  836.     St : string;
  837.     BR : BillingRec;
  838.     TotalSecs : LongInt;
  839.     Earnings : Float;
  840.     DoRate : Boolean;
  841.     Days, I, IoStat : Word;
  842.   begin
  843.     if not DataFileOpen then
  844.       Exit;
  845.  
  846.     {make sure there's something to report}
  847.     if TotalRecs = 0 then begin
  848.       ErrorMessage(ZeroEntries, True);
  849.       Exit;
  850.     end;
  851.  
  852.     if ToPrinter then
  853.       FP := @Lst
  854.     else begin
  855.       {get filename}
  856.       if not GetFName('Report file: ', FName, False) then
  857.         Exit;
  858.  
  859.       {check for overwrite}
  860.       if ExistFile(FName) then
  861.         if not YesOrNo('File exists. Overwrite it?', False) then
  862.           Exit;
  863.  
  864.       {open file}
  865.       Assign(F, FName);
  866.       Rewrite(F);
  867.       IoStat := Int24Result;
  868.       if IoStat <> 0 then begin
  869.         ErrorMessage('Error opening report file', True);
  870.         AllDone := True;
  871.         Exit;
  872.       end;
  873.       FP := @F;
  874.     end;
  875.  
  876.     {get rate}
  877.     DoRate := GetRate;
  878.  
  879.     {write header}
  880.     WriteLn(FP^, 'Data from: ', BillingName, ^M^J);
  881.     IoStat := Int24Result;
  882.     if IoStat <> 0 then begin
  883.       if ToPrinter then
  884.         ErrorMessage(PrintError, True)
  885.       else
  886.         ErrorMessage(ReportError, True);
  887.       AllDone := True;
  888.       Exit;
  889.     end;
  890.  
  891.     {write the report}
  892.     TotalSecs := 0;
  893.     Days := 0;
  894.     for I := 1 to TotalRecs do begin
  895.       if not ReadRecord(I, BR) then begin
  896.         AllDone := True;
  897.         Exit;
  898.       end;
  899.  
  900.       {add to total}
  901.       Inc(TotalSecs, BR.Elapsed);
  902.       if TotalSecs > MaxTime then begin
  903.         Inc(Days);
  904.         Dec(TotalSecs, SecondsInDay);
  905.       end;
  906.  
  907.       with BR do begin
  908.         if DoRate then
  909.           St := '  Earnings: '+MoneyString(Rate*(Elapsed/SecsPerHour))
  910.         else
  911.           St := NullString;
  912.         if Length(Comment) <> 0 then
  913.           St := St+^M^J+Comment;
  914.         WriteLn(FP^, 'Record #', I, ^M^J,
  915.           'Start: ', DateToDateString(DateFormat, Starting.D), ' at ',
  916.           TimeToTimeString(AmPmFormat, Starting.T), ^M^J,
  917.           'Stop:  ', DateToDateString(DateFormat, Stopping.D), ' at ',
  918.           TimeToTimeString(AmPmFormat, Stopping.T), ^M^J,
  919.           'Elapsed: ', TimeToTimeString(TimeFormat, Elapsed), St, ^M^J);
  920.         IoStat := Int24Result;
  921.         if IoStat <> 0 then begin
  922.           if ToPrinter then
  923.             ErrorMessage(PrintError, True)
  924.           else
  925.             ErrorMessage(ReportError, True);
  926.           AllDone := True;
  927.           Exit;
  928.         end;
  929.  
  930.       end;
  931.     end;
  932.  
  933.     {write totals}
  934.     St := HoursMinsSecs(Days, TotalSecs);
  935.     if DoRate then begin
  936.       Earnings := Rate*(((Days*1.0*SecondsInDay)+TotalSecs)/SecsPerHour);
  937.       St := St+' at '+MoneyString(Rate)+'/hr = '+MoneyString(Earnings);
  938.     end
  939.     else
  940.       St := St+'.';
  941.     WriteLn(FP^, 'Totals:'^M^J'-------'^M^J, St);
  942.     IoStat := Int24Result;
  943.     if IoStat <> 0 then begin
  944.       if ToPrinter then
  945.         ErrorMessage(PrintError, True)
  946.       else
  947.         ErrorMessage(ReportError, True);
  948.       AllDone := True;
  949.       Exit;
  950.     end;
  951.  
  952.     if ToPrinter then begin
  953.       {write a form feed}
  954.       Write(FP^, ^L);
  955.       IoStat := Int24Result;
  956.     end
  957.     else begin
  958.       {close the file}
  959.       Close(FP^);
  960.       IoStat := Int24Result;
  961.     end;
  962.  
  963.     {check for IO error}
  964.     if IoStat <> 0 then begin
  965.       ErrorMessage('Error closing report file', True);
  966.       AllDone := True;
  967.     end
  968.     else begin
  969.       {show that we're finished}
  970.       Prompt('Report finished...');
  971.       Delay(1500);
  972.       ClearPromptLine;
  973.     end;
  974.   end;
  975.  
  976.   procedure Totals;
  977.     {-Display number of records, total time}
  978.   var
  979.     Days, I : Word;
  980.     TotalSecs : LongInt;
  981.     BR : BillingRec;
  982.   begin
  983.     if not DataFileOpen then
  984.       Exit;
  985.  
  986.     {make sure there's something to total}
  987.     if TotalRecs = 0 then begin
  988.       ErrorMessage(ZeroEntries, True);
  989.       Exit;
  990.     end;
  991.  
  992.     {calculate total time}
  993.     TotalSecs := 0;
  994.     Days := 0;
  995.     for I := 1 to TotalRecs do
  996.       if ReadRecord(I, BR) then begin
  997.         Inc(TotalSecs, BR.Elapsed);
  998.         if TotalSecs > MaxTime then begin
  999.           Inc(Days);
  1000.           Dec(TotalSecs, SecondsInDay);
  1001.         end;
  1002.       end
  1003.       else begin
  1004.         AllDone := True;
  1005.         Exit;
  1006.       end;
  1007.  
  1008.     {display # of records, total time}
  1009.     ErrorMessage(Decimal(TotalRecs, 0, False)+' records. '+
  1010.       HoursMinsSecs(Days, TotalSecs), False);
  1011.   end;
  1012.  
  1013.   procedure InitMenu(var M : Menu);
  1014.     {-Initialize main menu}
  1015.   begin
  1016.     {Customize this call for special exit characters and custom item displays}
  1017.     M := NewMenu([], nil);
  1018.  
  1019.     {select color table}
  1020.     if WhichHerc = HercInColor then
  1021.       ColorsPtr := @Color1
  1022.     else case CurrentMode of
  1023.       2, 7 : ColorsPtr := @Mono1;
  1024.       else ColorsPtr := @Color1;
  1025.     end;
  1026.  
  1027.     SubMenu(1, 1, 25, Horizontal, Frame1, ColorsPtr^,
  1028.       ' PTRPT: Report Generator for PTIME Data Files 5.07 ');
  1029.     MenuItem(' Append ', 02, 2, 01, 'Append a database entry');
  1030.     MenuItem(' Browse ', 10, 2, 02, 'Browse through database entries');
  1031.     MenuItem(' Calculate ', 18, 2, 03, 'Calculate total hours, earnings');
  1032.     MenuItem(' Delete ', 29, 2, 04, 'Delete a database entry');
  1033.     MenuItem(' Edit ', 37, 2, 05, 'Edit a database entry');
  1034.     MenuItem(' File ', 43, 2, 06, 'Select a data file');
  1035.     MenuItem(' Print ', 49, 2, 07, 'Generate a printed report');
  1036.     MenuItem(' Quit ', 56, 2, 08, 'Quit the program');
  1037.     MenuItem(' Report ', 62, 2, 09, 'Generate a report on disk');
  1038.     MenuItem(' Totals ', 70, 2, 10, 'Display number of entries in database, total hours');
  1039.     PopSublevel;
  1040.  
  1041.     ResetMenu(M);
  1042.   end;
  1043.  
  1044.   procedure CleanUpScreen;
  1045.     {-Clean up screen at end of program}
  1046.   begin
  1047.     {$IFDEF UseMouse}
  1048.     HideMouse;
  1049.     {$ENDIF}
  1050.  
  1051.     EraseMenu(RootMenu, False);
  1052.     NormVideo;
  1053.     ClrScr;
  1054.     RestoreCursorState($0101, InitSL);
  1055.   end;
  1056.  
  1057.   procedure Initialize;
  1058.     {-Initialize global variables, etc.}
  1059.   var
  1060.     Dummy : Word;
  1061.   begin
  1062.     {check screen mode, size}
  1063.     if not(InTextMode and (ScreenWidth = 80)) then begin
  1064.       WriteLn('This program runs only in 80-column text modes.');
  1065.       Halt(1);
  1066.     end;
  1067.  
  1068.     {initialize screen}
  1069.     if ScreenHeight > 25 then
  1070.       SelectFont8x8(False);
  1071.     ClrScr;
  1072.     CheckBreak := False;
  1073.     GetCursorState(Dummy, InitSL);
  1074.     HiddenCursor;
  1075.  
  1076.     {build the menu system}
  1077.     InitMenu(RootMenu);
  1078.  
  1079.     {$IFDEF EnablePickOrientations}
  1080.     {set the pick orientation}
  1081.     SetVerticalPick;
  1082.     {$ENDIF}
  1083.  
  1084.     {video attributes}
  1085.     Dim := ColorsPtr^[HelpColor];
  1086.     Bright := ColorsPtr^[HiliteColor];
  1087.     Reverse := ColorsPtr^[SelectColor];
  1088.     PickColors[WindowAttr] := Dim;
  1089.     PickColors[FrameAttr] := Bright;
  1090.     PickColors[HeaderAttr] := Reverse;
  1091.     PickColors[SelectAttr] := Reverse;
  1092.     PickColors[AltNormal] := Bright;
  1093.     PickColors[AltHigh] := Reverse;
  1094.     {$IFDEF PickItemDisable}
  1095.     PickColors[WindowAttr] := Dim;
  1096.     {$ENDIF}
  1097.  
  1098.     {$IFDEF UseMouse}
  1099.     if MouseInstalled then begin
  1100.       {use a diamond for our mouse cursor}
  1101.       SoftMouseCursor($0000, (Bright shl 8)+$04);
  1102.       ShowMouse;
  1103.  
  1104.       {enable mouse support}
  1105.       EnableEntryMouse;
  1106.       EnablePickMouse;
  1107.       EnableMenuMouse;
  1108.     end;
  1109.     {$ENDIF}
  1110.  
  1111.     {initialize global variables}
  1112.     TotalRecs := 0;
  1113.     CurrentRec := 0;
  1114.     Rate := 0.0;
  1115.     DataFileIsOpen := False;
  1116.     FillChar(Blank[1], 80, ' ');
  1117.  
  1118.     {initialize the edit screen record}
  1119.     InitESrecord(ESR);
  1120.  
  1121.     {install user-written event handlers}
  1122.     SetPreEditPtr(ESR, @DisplayHelpPrompt);
  1123.     SetErrorPtr(ESR, @ErrorHandler);
  1124.  
  1125.     {set edit screen options}
  1126.     SetWrapMode(ESR, WrapAtEdges);
  1127.  
  1128.     {set field editing options}
  1129.     SetClearFirstChar(On);
  1130.     SetPromptAttr(Bright);
  1131.     SetFieldAttr(Dim);
  1132.     SetStringAttr(Reverse);
  1133.     SetCtrlAttr(Reverse);
  1134.  
  1135.     {add each of the edit fields in order: left to right, top to bottom}
  1136.     SetProtection(On);
  1137.     AddStringField(ESR, 'Data file:', 7, 4, '', 7, 15, 64, 0, nil, BillingName);
  1138.     AddWordField(ESR, 'Record #', 9, 4, '99999', 9, 18, 1, 0, 0, CurrentRec);
  1139.     SetProtection(Off);
  1140.  
  1141.     AddDateField(ESR, 'Start date:', 10, 4, DateFormat, 10, 18, 2,
  1142.       MinDate, Today+1, BlankBill.Starting.D);
  1143.     AddTimeField(ESR, 'Start time:', 11, 4, AmPmFormat, 11, 18, 3,
  1144.       MinTime, MaxTime, BlankBill.Starting.T);
  1145.     AddDateField(ESR, 'Stop date:', 12, 4, DateFormat, 12, 18, 4,
  1146.       MinDate, Today+1, BlankBill.Stopping.D);
  1147.     AddTimeField(ESR, 'Stop time:', 13, 4, AmPmFormat, 13, 18, 5,
  1148.       MinTime, MaxTime, BlankBill.Stopping.T);
  1149.     AddTimeField(ESR, 'Elapsed time:', 14, 4, TimeFormat, 14, 18, 6,
  1150.       MinTime, MaxTime, BlankBill.Elapsed);
  1151.     AddYesNoField(ESR, 'Allow update:', 16, 4, '', 16, 18, 7,
  1152.       BlankBill.AllowUpdate);
  1153.  
  1154.     SetAutoAdvance(Off);
  1155.     AddStringField(ESR, 'Comment:', 18, 4, CharStr('X', 76), 18, 18, 58, 8, nil,
  1156.       BlankBill.Comment);
  1157.  
  1158.     {get name of data file, if any}
  1159.     BillingName := StUpcase(ParamStr(1));
  1160.     if Length(BillingName) <> 0 then
  1161.       if GetFileNameShell(BillingName) then
  1162.         if not OpenBilling then {} ;
  1163.   end;
  1164.  
  1165. begin
  1166.   {make sure we can run under a multitasking environment}
  1167.   DetectMultitasking := True;
  1168.   ReinitCrt;
  1169.  
  1170.   {initialize globals, menu system, etc}
  1171.   Initialize;
  1172.  
  1173.   AllDone := False;
  1174.   repeat
  1175.     {reset}
  1176.     CurrentRec := 1;
  1177.     ESR.CurrentID := 0;
  1178.  
  1179.     {Put up the menu and get a menu action}
  1180.     Key := MenuChoice(RootMenu, MenuCh);
  1181.  
  1182.     {Handle the command}
  1183.     if MenuCmdNum = MKSExit then
  1184.       AllDone := QuitProgram
  1185.     else if MenuCmdNum = MKSSelect then
  1186.       case Key of
  1187.         01 : Append;         {Append database entry}
  1188.         02 : Browse;         {Browse through database entries}
  1189.         03 : Calculate;      {Calculate total hours, earnings}
  1190.         04 : DeleteEntries;  {Delete database entries}
  1191.         05 : EditEntries;    {Edit database entries}
  1192.         06 : SelectFile;     {select data File}
  1193.         07 : Report(True);   {report to Printer}
  1194.         08 : AllDone := QuitProgram; {Quit}
  1195.         09 : Report(False);  {Report to disk}
  1196.         10 : Totals;         {display Totals}
  1197.       end;
  1198.   until AllDone;
  1199.  
  1200.   {clean up the screen}
  1201.   CleanUpScreen;
  1202. end.
  1203.