home *** CD-ROM | disk | FTP | other *** search
- _STRUCTURED PROGRAMMING COLUMN_
- by Jeff Duntemann
-
-
-
- [LISTING ONE]
-
- PROGRAM HCalc; { By Jeff Duntemann; Update of 10/31/91 }
- { Requires Turbo Pascal 6.0! }
-
- USES App,Dialogs,Objects,Views,Menus,Drivers,
- FInput, { By Allen Bauer; on CompuServe BPROGA }
- Mortgage; { By Jeff Duntemann; from DDJ 10/91 }
-
- CONST
- cmNewMortgage = 199;
- cmExtraPrin = 198;
- cmCloseAll = 197;
- cmCloseBC = 196;
- cmPrintSummary = 195;
- WindowCount : Integer = 0;
-
- TYPE
- MortgageDialogData =
- RECORD
- PrincipalData : Real;
- InterestData : Real;
- PeriodsData : Integer;
- END;
-
- ExtraPrincipalDialogData =
- RECORD
- PaymentNumber : Integer;
- ExtraDollars : Real;
- END;
-
- THouseCalcApp =
- OBJECT(TApplication)
- InitDialog : PDialog; { Dialog for initializing a mortgage }
- ExtraDialog : PDialog; { Dialog for entering extra principal }
- CONSTRUCTOR Init;
- PROCEDURE InitMenuBar; VIRTUAL;
- PROCEDURE CloseAll;
- PROCEDURE HandleEvent(VAR Event : TEvent); VIRTUAL;
- PROCEDURE NewMortgage;
- END;
-
- PMortgageTopInterior = ^TMortgageTopInterior;
- TMortgageTopInterior =
- OBJECT(TView)
- Mortgage : PMortgage;
- CONSTRUCTOR Init(VAR Bounds : TRect);
- PROCEDURE Draw; VIRTUAL;
- END;
-
-
- PMortgageBottomInterior = ^TMortgageBottomInterior;
- TMortgageBottomInterior =
- OBJECT(TScroller)
- { Points to Mortgage object owned by TMortgageView }
- Mortgage : PMortgage;
- CONSTRUCTOR Init(VAR Bounds : TRect;
- AHScrollBar, AVScrollbar : PScrollBar);
- PROCEDURE Draw; VIRTUAL;
- END;
-
- PMortgageView = ^TMortgageView;
- TMortgageView =
- OBJECT(TWindow)
- Mortgage : TMortgage;
- CONSTRUCTOR Init(VAR Bounds : TRect;
- ATitle : TTitleStr;
- ANumber : Integer;
- InitMortgageData :
- MortgageDialogData);
- PROCEDURE HandleEvent(Var Event : TEvent); VIRTUAL;
- PROCEDURE ExtraPrincipal;
- PROCEDURE PrintSummary;
- DESTRUCTOR Done; VIRTUAL;
- END;
-
-
- CONST
- DefaultMortgageData : MortgageDialogData =
- (PrincipalData : 100000;
- InterestData : 10.0;
- PeriodsData : 360);
-
-
- VAR
- HouseCalc : THouseCalcApp; { This is the application object itself }
-
-
-
- {------------------------------}
- { METHODS: THouseCalcApp }
- {------------------------------}
-
-
- CONSTRUCTOR THouseCalcApp.Init;
-
- VAR
- R : TRect;
- aView : PView;
-
- BEGIN
- TApplication.Init; { Always call the parent's constructor first! }
-
- { Create the dialog for initializing a mortgage: }
- R.Assign(20,5,60,16);
- InitDialog := New(PDialog,Init(R,'Define Mortgage Parameters'));
- WITH InitDialog^ DO
- BEGIN
- { First item in the dialog box is input line for principal: }
- R.Assign(3,3,13,4);
- aView := New(PFInputLine,Init(R,8,DRealSet,DReal,0));
- Insert(aView);
- R.Assign(2,2,12,3);
- Insert(New(PLabel,Init(R,'Principal',aView)));
-
- { Next is the input line for interest rate: }
- R.Assign(17,3,26,4);
- aView := New(PFInputLine,Init(R,6,DRealSet,DReal,3));
- Insert(aView);
- R.Assign(16,2,25,3);
- Insert(New(PLabel,Init(R,'Interest',aView)));
- R.Assign(26,3,27,4); { Add a static text "%" sign }
- Insert(New(PStaticText,Init(R,'%')));
-
- { Up next is the input line for number of periods: }
- R.Assign(31,3,36,4);
- aView := New(PFInputLine,Init(R,3,DUnsignedSet,DInteger,0));
- Insert(aView);
- R.Assign(29,2,37,3);
- Insert(New(PLabel,Init(R,'Periods',aView)));
-
- { These are standard buttons for the OK and Cancel commands: }
- R.Assign(8,8,16,10);
- Insert(New(PButton,Init(R,'~O~K',cmOK,bfDefault)));
- R.Assign(22,8,32,10);
- Insert(New(PButton,Init(R,'Cancel',cmCancel,bfNormal)));
- END;
-
- { Create the dialog for adding additional principal to a payment: }
- R.Assign(20,5,60,16);
- ExtraDialog := New(PDialog,Init(R,'Apply Extra Principal to Mortgage'));
- WITH ExtraDialog^ DO
- BEGIN
- { First item in the dialog is the payment number to which }
- { we're going to apply the extra principal: }
- R.Assign(9,3,18,4);
- aView := New(PFInputLine,Init(R,6,DUnsignedSet,DInteger,0));
- Insert(aView);
- R.Assign(3,2,12,3);
- Insert(New(PLabel,Init(R,'Payment #',aView)));
-
- { Next item in the dialog box is input line for extra principal: }
- R.Assign(23,3,33,4);
- aView := New(PFInputLine,Init(R,8,DRealSet,DReal,2));
- Insert(aView);
- R.Assign(20,2,35,3);
- Insert(New(PLabel,Init(R,'Extra Principal',aView)));
-
- { These are standard buttons for the OK and Cancel commands: }
- R.Assign(8,8,16,10);
- Insert(New(PButton,Init(R,'~O~K',cmOK,bfDefault)));
- R.Assign(22,8,32,10);
- Insert(New(PButton,Init(R,'Cancel',cmCancel,bfNormal)));
- END;
-
- END;
-
-
- { This method sends out a broadcast message to all views. Only the
- { mortgage windows know how to respond to it, so when cmCloseBC is
- { issued, only the mortgage windows react--by closing. }
-
- PROCEDURE THouseCalcApp.CloseAll;
-
- VAR
- Who : Pointer;
-
- BEGIN
- Who := Message(Desktop,evBroadcast,cmCloseBC,@Self);
- END;
-
-
- PROCEDURE THouseCalcApp.HandleEvent(VAR Event : TEvent);
-
- BEGIN
- TApplication.HandleEvent(Event);
- IF Event.What = evCommand THEN
- BEGIN
- CASE Event.Command OF
- cmNewMortgage : NewMortgage;
- cmCloseAll : CloseAll;
- ELSE
- Exit;
- END; { CASE }
- ClearEvent(Event);
- END;
- END;
-
-
- PROCEDURE THouseCalcApp.NewMortgage;
-
- VAR
- Code : Integer;
- R : TRect;
- Control : Word;
- ThisMortgage : PMortgageView;
- InitMortgageData : MortgageDialogData;
-
- BEGIN
- { First we need a dialog to get the intial mortgage values from }
- { the user. The dialog appears *before* the mortgage window! }
- WITH InitMortgageData DO
- BEGIN
- PrincipalData := 100000;
- InterestData := 10.0;
- PeriodsData := 360;
- END;
- InitDialog^.SetData(InitMortgageData);
- Control := Desktop^.ExecView(InitDialog);
- IF Control <> cmCancel THEN { Create a new mortgage object: }
- BEGIN
- R.Assign(5,5,45,20);
- Inc(WindowCount);
- { Get data from the initial mortgage dialog: }
- InitDialog^.GetData(InitMortgageData);
- { Call the constructor for the mortgage window: }
- ThisMortgage :=
- New(PMortgageView,Init(R,'Mortgage',WindowCount,
- InitMortgageData));
-
- { Insert the mortgage window into the desktop: }
- Desktop^.Insert(ThisMortgage);
- END;
- END;
-
-
- PROCEDURE THouseCalcApp.InitMenuBar;
-
- VAR
- R : TRect;
-
- BEGIN
- GetExtent(R);
- R.B.Y := R.A.Y + 1; { Define 1-line menu bar }
-
- MenuBar := New(PMenuBar,Init(R,NewMenu(
- NewSubMenu('~M~ortgage',hcNoContext,NewMenu(
- NewItem('~N~ew','F6',kbF6,cmNewMortgage,hcNoContext,
- NewItem('~E~xtra Principal ','',0,cmExtraPrin,hcNoContext,
- NewItem('~C~lose all','F7',kbF7,cmCloseAll,hcNoContext,
- NewItem('E~x~it','Alt-X',kbAltX,cmQuit,hcNoContext,
- NIL))))),
- NIL)
- )));
- END;
-
-
- {---------------------------------}
- { METHODS: TMortgageTopInterior }
- {---------------------------------}
-
- CONSTRUCTOR TMortgageTopInterior.Init(VAR Bounds : TRect);
-
- BEGIN
- TView.Init(Bounds); { Call ancestor's constructor }
- GrowMode := gfGrowHiX; { Permits pane to grow in X but not Y }
- END;
-
-
- PROCEDURE TMortgageTopInterior.Draw;
-
- VAR
- YRun : Integer;
- Color : Byte;
- B : TDrawBuffer;
- STemp : String[20];
-
- BEGIN
- Color := GetColor(1);
- MoveChar(B,' ',Color,Size.X); { Clear the buffer to spaces }
- MoveStr(B,' Principal Interest Periods',Color);
- WriteLine(0,0,Size.X,1,B);
-
- MoveChar(B,' ',Color,Size.X); { Clear the buffer to spaces }
- { Here we convert payment data to strings for display: }
- Str(Mortgage^.Principal:7:2,STemp);
- MoveStr(B[2],STemp,Color); { At beginning of buffer B }
- Str(Mortgage^.Interest*100:7:2,STemp);
- MoveStr(B[14],STemp,Color); { At position 14 of buffer B }
- Str(Mortgage^.Periods:4,STemp);
- MoveStr(B[27],STemp,Color); { At position 27 of buffer B }
- WriteLine(0,1,Size.X,1,B);
-
- MoveChar(B,' ',Color,Size.X); { Clear the buffer to spaces }
- MoveStr(B,
- ' Extra Principal Interest',
- Color);
- WriteLine(0,2,Size.X,1,B);
-
- MoveChar(B,' ',Color,Size.X); { Clear the buffer to spaces }
- MoveStr(B,
- 'Paymt # Prin. Int. Balance Principal So far So far ',
- Color);
- WriteLine(0,3,Size.X,1,B);
-
- END;
-
-
- {------------------------------------}
- { METHODS: TMortgageBottomInterior }
- {------------------------------------}
-
- CONSTRUCTOR TMortgageBottomInterior.Init(VAR Bounds : TRect;
- AHScrollBar, AVScrollBar :
- PScrollBar);
-
- BEGIN
- { Call ancestor's constructor: }
- TScroller.Init(Bounds,AHScrollBar,AVScrollBar);
- GrowMode := gfGrowHiX + gfGrowHiY;
- Options := Options OR ofFramed;
- END;
-
-
- PROCEDURE TMortgageBottomInterior.Draw;
-
- VAR
- Color : Byte;
- B : TDrawBuffer;
- YRun : Integer;
- STemp : String[20];
-
- BEGIN
- Color := GetColor(1);
- FOR YRun := 0 TO Size.Y-1 DO
- BEGIN
- MoveChar(B,' ',Color,80); { Clear the buffer to spaces }
- Str(Delta.Y+YRun+1:4,STemp);
- MoveStr(B,STemp+':',Color); { At beginning of buffer B }
- { Here we convert payment data to strings for display: }
- Str(Mortgage^.Payments^[Delta.Y+YRun+1].PayPrincipal:7:2,STemp);
- MoveStr(B[6],STemp,Color); { At beginning of buffer B }
- Str(Mortgage^.Payments^[Delta.Y+YRun+1].PayInterest:7:2,STemp);
- MoveStr(B[15],STemp,Color); { At position 15 of buffer B }
- Str(Mortgage^.Payments^[Delta.Y+YRun+1].Balance:10:2,STemp);
- MoveStr(B[24],STemp,Color); { At position 24 of buffer B }
- { There isn't an extra principal value for every payment, so }
- { display the value only if it is nonzero: }
- STemp := '';
- IF Mortgage^.Payments^[Delta.Y+YRun+1].ExtraPrincipal > 0
- THEN
- Str(Mortgage^.Payments^[Delta.Y+YRun+1].ExtraPrincipal:10:2,STemp);
- MoveStr(B[37],STemp,Color); { At position 37 of buffer B }
- Str(Mortgage^.Payments^[Delta.Y+YRun+1].PrincipalSoFar:10:2,STemp);
- MoveStr(B[50],STemp,Color); { At position 50 of buffer B }
- Str(Mortgage^.Payments^[Delta.Y+YRun+1].InterestSoFar:10:2,STemp);
- MoveStr(B[64],STemp,Color); { At position 64 of buffer B }
- { Here we write the line to the window, taking into account the }
- { state of the X scroll bar: }
- WriteLine(0,YRun,Size.X,1,B[Delta.X]);
- END;
- END;
-
-
- {------------------------------}
- { METHODS: TMortgageView }
- {------------------------------}
-
- CONSTRUCTOR TMortgageView.Init(VAR Bounds : TRect;
- ATitle : TTitleStr;
- ANumber : Integer;
- InitMortgageData :
- MortgageDialogData);
- VAR
- TopInterior : PMortgageTopInterior;
- BottomInterior : PMortgageBottomInterior;
- HScrollBar,VScrollBar : PScrollBar;
- R,S : TRect;
-
- BEGIN
- TWindow.Init(Bounds,ATitle,ANumber); { Call ancestor's constructor }
- { Call the Mortgage object's constructor using dialog data: }
- WITH InitMortgageData DO
- Mortgage.Init(PrincipalData,
- InterestData / 100,
- PeriodsData,
- 12);
-
- { Here we set up a window with *two* interiors, one scrollable, one }
- { static. It's all in the way that you define the bounds, mostly: }
- GetClipRect(Bounds); { Get bounds for interior of view }
- Bounds.Grow(-1,-1); { Shrink those bounds by 1 for both X & Y }
-
- { Define a rectangle to embrace the upper of the two interiors: }
- R.Assign(Bounds.A.X,Bounds.A.Y,Bounds.B.X,Bounds.A.Y+4);
- TopInterior := New(PMortgageTopInterior,Init(R));
- TopInterior^.Mortgage := @Mortgage;
- Insert(TopInterior);
-
- { Define a rectangle to embrace the lower of two interiors: }
- R.Assign(Bounds.A.X,Bounds.A.Y+5,Bounds.B.X,Bounds.B.Y);
-
- { Create scroll bars for both mouse & keyboard input: }
- VScrollBar := StandardScrollBar(sbVertical + sbHandleKeyboard);
- { We have to adjust vertical bar to fit bottom interior: }
- VScrollBar^.Origin.Y := R.A.Y; { Adjust top Y value }
- VScrollBar^.Size.Y := R.B.Y - R.A.Y; { Adjust size }
- { The horizontal scroll bar, on the other hand, is standard: }
- HScrollBar := StandardScrollBar(sbHorizontal + sbHandleKeyboard);
-
- { Create bottom interior object with scroll bars: }
- BottomInterior :=
- New(PMortgageBottomInterior,Init(R,HScrollBar,VScrollBar));
- { Make copy of pointer to mortgage object: }
- BottomInterior^.Mortgage := @Mortgage;
- { Set the limits for the scroll bars: }
- BottomInterior^.SetLimit(80,InitMortgageData.PeriodsData);
- { Insert the interior into the window: }
- Insert(BottomInterior);
- END;
-
-
- PROCEDURE TMortgageView.HandleEvent(Var Event : TEvent);
-
- BEGIN
- TWindow.HandleEvent(Event);
- IF Event.What = evCommand THEN
- BEGIN
- CASE Event.Command OF
- cmExtraPrin : ExtraPrincipal;
- cmPrintSummary : PrintSummary;
- ELSE
- Exit;
- END; { CASE }
- ClearEvent(Event);
- END
- ELSE
- IF Event.What = evBroadcast THEN
- CASE Event.Command OF
- cmCloseBC : Done
- END; { CASE }
- END;
-
-
- PROCEDURE TMortgageView.ExtraPrincipal;
-
- VAR
- Control : Word;
- ExtraPrincipalData : ExtraPrincipalDialogData;
-
- BEGIN
- { Execute the "extra principal" dialog box: }
- Control := Desktop^.ExecView(HouseCalc.ExtraDialog);
- IF Control <> cmCancel THEN { Update the active mortgage window: }
- BEGIN
- { Get data from the extra principal dialog: }
- HouseCalc.ExtraDialog^.GetData(ExtraPrincipalData);
- Mortgage.Payments^[ExtraPrincipalData.PaymentNumber].ExtraPrincipal :=
- ExtraPrincipalData.ExtraDollars;
- Mortgage.Recalc; { Recalculate the amortization table... }
- Redraw; { ...and redraw the mortgage window }
- END;
- END;
-
-
- PROCEDURE TMortgageView.PrintSummary;
-
- BEGIN
- END;
-
-
- DESTRUCTOR TMortgageView.Done;
-
- BEGIN
- Mortgage.Done; { Dispose of the mortgage object's memory }
- TWindow.Done; { Call parent's destructor to dispose of window }
- END;
-
-
-
- BEGIN
- HouseCalc.Init;
- HouseCalc.Run;
- HouseCalc.Done;
- END.
-
-
- [THE FOLLOWING IS SOURCE FOR FINPUT.PAS]
-
- unit FInput;
- {$X+}
- {
- This unit implements a derivative of TInputLine that supports several
- data types dynamically. It also provides formatted input for all the
- numerical types, keystroke filtering and uppercase conversion, field
- justification, and range checking.
-
- When the field is initialized, many filtering and uppercase converions
- are implemented pertinent to the particular data type.
-
- The CheckRange and ErrorHandler methods should be overridden if the
- user wants to implement then.
-
- This is just an initial implementation and comments are welcome. You
- can contact me via Compuserve. (76066,3202)
-
- I am releasing this into the public domain and anyone can use or modify
- it for their own personal use.
-
- Copyright (c) 1990 by Allen Bauer (76066,3202)
-
- 1.1 - fixed input validation functions
-
- This is version 1.2 - fixed DataSize method to include reals.
- fixed Draw method to not format the data
- while the view is selected.
- }
-
- interface
- uses Objects, Drivers, Dialogs;
-
- type
- VKeys = set of char;
-
- PFInputLine = ^TFInputLine;
- TFInputLine = object(TInputLine)
- ValidKeys : VKeys;
- DataType,Decimals : byte;
- imMode : word;
- Validated, ValidSent : boolean;
- constructor Init(var Bounds: TRect; AMaxLen: integer;
- ChrSet: VKeys;DType, Dec: byte);
- constructor Load(var S: TStream);
- procedure Store(var S: TStream);
- procedure HandleEvent(var Event: TEvent); virtual;
- procedure GetData(var Rec); virtual;
- procedure SetData(var Rec); virtual;
- function DataSize: word; virtual;
- procedure Draw; virtual;
- function CheckRange: boolean; virtual;
- procedure ErrorHandler; virtual;
- end;
-
- const
- imLeftJustify = $0001;
- imRightJustify = $0002;
- imConvertUpper = $0004;
-
- DString = 0;
- DChar = 1;
- DReal = 2;
- DByte = 3;
- DShortInt = 4;
- DInteger = 5;
- DLongInt = 6;
- DWord = 7;
- DDate = 8;
- DTime = 9;
-
- DRealSet : VKeys = [#1..#31,'+','-','0'..'9','.','E','e'];
- DSignedSet : VKeys = [#1..#31,'+','-','0'..'9'];
- DUnSignedSet : VKeys = [#1..#31,'0'..'9'];
- DCharSet : VKeys = [#1..#31,' '..'~'];
- DUpperSet : VKeys = [#1..#31,' '..'`','{'..'~'];
- DAlphaSet : VKeys = [#1..#31,'A'..'Z','a'..'z'];
- DFileNameSet : VKeys = [#1..#31,'!','#'..')','-'..'.','0'..'9','@'..'Z','^'..'{','}'..'~'];
- DPathSet : VKeys = [#1..#31,'!','#'..')','-'..'.','0'..':','@'..'Z','^'..'{','}'..'~','\'];
- DFileMaskSet : VKeys = [#1..#31,'!','#'..'*','-'..'.','0'..':','?'..'Z','^'..'{','}'..'~','\'];
- DDateSet : VKeys = [#1..#31,'0'..'9','/'];
- DTimeSet : VKeys = [#1..#31,'0'..'9',':'];
-
- cmValidateYourself = 5000;
- cmValidatedOK = 5001;
-
- procedure RegisterFInputLine;
-
- const
- RFInputLine : TStreamRec = (
- ObjType: 20000;
- VmtLink: Ofs(typeof(TFInputLine)^);
- Load: @TFInputLine.Load;
- Store: @TFinputLine.Store
- );
-
- implementation
-
- uses Views, MsgBox, StrFmt, Dos;
-
- function CurrentDate : string;
- var
- Year,Month,Day,DOW : word;
- DateStr : string[10];
- begin
- GetDate(Year,Month,Day,DOW);
- DateStr := SFLongint(Month,2)+'/'
- +SFLongInt(Day,2)+'/'
- +SFLongInt(Year mod 100,2);
- for DOW := 1 to length(DateStr) do
- if DateStr[DOW] = ' ' then
- DateStr[DOW] := '0';
- CurrentDate := DateStr;
- end;
-
- function CurrentTime : string;
- var
- Hour,Minute,Second,Sec100 : word;
- TimeStr : string[10];
- begin
- GetTime(Hour,Minute,Second,Sec100);
- TimeStr := SFLongInt(Hour,2)+':'
- +SFLongInt(Minute,2)+':'
- +SFLongInt(Second,2);
- for Sec100 := 1 to length(TimeStr) do
- if TimeStr[Sec100] = ' ' then
- TimeStr[Sec100] := '0';
- CurrentTime := TimeStr;
- end;
-
- procedure RegisterFInputLine;
- begin
- RegisterType(RFInputLine);
- end;
-
- constructor TFInputLine.Init(var Bounds: TRect; AMaxLen: integer;
- ChrSet: VKeys; DType, Dec: byte);
- begin
- if (DType in [DDate,DTime]) and (AMaxLen < 8) then
- AMaxLen := 8;
-
- TInputLine.Init(Bounds,AMaxLen);
-
- ValidKeys:= ChrSet;
- DataType := DType;
- Decimals := Dec;
- Validated := true;
- ValidSent := false;
- case DataType of
- DReal,DByte,DLongInt,
- DShortInt,DWord : imMode := imRightJustify;
-
- DChar,DString,
- DDate,DTime : imMode := imLeftJustify;
- end;
- if ValidKeys = DUpperSet then
- imMode := imMode or imConvertUpper;
- EventMask := EventMask or evMessage;
- end;
-
- constructor TFInputLine.Load(var S: TStream);
- begin
- TInputLine.Load(S);
- S.Read(ValidKeys, sizeof(VKeys));
- S.Read(DataType, sizeof(byte));
- S.Read(Decimals, sizeof(byte));
- S.Read(imMode, sizeof(word));
- S.Read(Validated, sizeof(boolean));
- S.Read(ValidSent, sizeof(boolean));
- end;
-
- procedure TFInputLine.Store(var S: TStream);
- begin
- TInputLine.Store(S);
- S.Write(ValidKeys, sizeof(VKeys));
- S.Write(DataType, sizeof(byte));
- S.Write(Decimals, sizeof(byte));
- S.Write(imMode, sizeof(word));
- S.Write(Validated, sizeof(boolean));
- S.Write(ValidSent, sizeof(boolean));
- end;
-
- procedure TFInputLine.HandleEvent(var Event: TEvent);
- var
- NewEvent: TEvent;
- begin
- case Event.What of
- evKeyDown : begin
- if (imMode and imConvertUpper) <> 0 then
- Event.CharCode := upcase(Event.CharCode);
- if not(Event.CharCode in [#0..#31]) then
- begin
- Validated := false;
- ValidSent := false;
- end;
- if (Event.CharCode <> #0) and not(Event.CharCode in ValidKeys) then
- ClearEvent(Event);
- end;
- evBroadcast: begin
- if (Event.Command = cmReceivedFocus) and
- (Event.InfoPtr <> @Self) and
- ((Owner^.State and sfSelected) <> 0) and
- not(Validated) and not(ValidSent) then
- begin
- NewEvent.What := evBroadcast;
- NewEvent.InfoPtr := @Self;
- NewEvent.Command := cmValidateYourself;
- PutEvent(NewEvent);
- ValidSent := true;
- end;
- if (Event.Command = cmValidateYourself) and
- (Event.InfoPtr = @Self) then
- begin
- if not CheckRange then
- begin
- ErrorHandler;
- Select;
- end
- else
- begin
- NewEvent.What := evBroadCast;
- NewEvent.InfoPtr := @Self;
- NewEvent.Command := cmValidatedOK;
- PutEvent(NewEvent);
- Validated := true;
- end;
- ValidSent := false;
- ClearEvent(Event);
- end;
- end;
- end;
- TInputLine.HandleEvent(Event);
- end;
-
- procedure TFInputLine.GetData(var Rec);
- var
- Code : integer;
- begin
- case DataType of
- Dstring,
- DDate,
- DTime : TInputLine.GetData(Rec);
- DChar : char(Rec) := Data^[1];
- DReal : val(Data^, real(Rec) , Code);
- DByte : val(Data^, byte(Rec) , Code);
- DShortInt : val(Data^, shortint(Rec) , Code);
- DInteger : val(Data^, integer(Rec) , Code);
- DLongInt : val(Data^, longint(Rec) , Code);
- DWord : val(Data^, word(Rec) , Code);
- end;
- end;
-
- procedure TFInputLine.SetData(var Rec);
- begin
- case DataType of
- DString,
- DDate,
- DTime : TInputLine.SetData(Rec);
- DChar : Data^ := char(Rec);
- DReal : Data^ := SFDReal(real(Rec),MaxLen,Decimals);
- DByte : Data^ := SFLongInt(byte(Rec),MaxLen);
- DShortInt : Data^ := SFLongInt(shortint(Rec),MaxLen);
- DInteger : Data^ := SFLongInt(integer(Rec),MaxLen);
- DLongInt : Data^ := SFLongInt(longint(Rec),MaxLen);
- DWord : Data^ := SFLongInt(word(Rec),MaxLen);
- end;
- SelectAll(true);
- end;
-
- function TFInputLine.DataSize: word;
- begin
- case DataType of
- DString,
- DDate,
- DTime : DataSize := TInputLine.DataSize;
- DChar : DataSize := sizeof(char);
- DReal : DataSize := sizeof(real);
- DByte : DataSize := sizeof(byte);
- DShortInt : DataSize := sizeof(shortint);
- DInteger : DataSize := sizeof(integer);
- DLongInt : DataSize := sizeof(longint);
- DWord : DataSize := sizeof(word);
- else
- DataSize := TInputLine.DataSize;
- end;
- end;
-
- procedure TFInputLine.Draw;
- var
- RD : real;
- Code : integer;
- begin
- if not((State and sfSelected) <> 0) then
- case DataType of
- DReal : begin
- if Data^ = '' then
- Data^ := SFDReal(0.0,MaxLen,Decimals)
- else
- begin
- val(Data^, RD, Code);
- Data^ := SFDReal(RD,MaxLen,Decimals);
- end;
- end;
-
- DByte,
- DShortInt,
- DInteger,
- DLongInt,
- DWord : if Data^ = '' then Data^ := SFLongInt(0,MaxLen);
-
- DDate : if Data^ = '' then Data^ := CurrentDate;
- DTime : if Data^ = '' then Data^ := CurrentTime;
-
- end;
-
- if State and (sfFocused+sfSelected) <> 0 then
- begin
- if (imMode and imRightJustify) <> 0 then
- while (length(Data^) > 0) and (Data^[1] = ' ') do
- delete(Data^,1,1);
- end
- else
- begin
- if ((imMode and imRightJustify) <> 0) and (Data^ <> '') then
- while (length(Data^) < MaxLen) do
- insert(' ',Data^,1);
- if (imMode and imLeftJustify) <> 0 then
- while (length(Data^) > 0) and (Data^[1] = ' ') do
- delete(Data^,1,1);
-
- end;
- TInputLine.Draw;
- end;
-
- function TFInputLine.CheckRange: boolean;
- var
- MH,DM,YS : longint;
- Code : integer;
- MHs,DMs,YSs : string[2];
- Delim : char;
- Ok : boolean;
- begin
- Ok := true;
- case DataType of
- DDate,
- DTime : begin
- if DataType = DDate then Delim := '/' else Delim := ':';
- if pos(Delim,Data^) > 0 then
- begin
- MHs := copy(Data^,1,pos(Delim,Data^));
- DMs := copy(Data^,pos(Delim,Data^)+1,2);
- delete(Data^,pos(Delim,Data^),1);
- YSs := copy(Data^,pos(Delim,Data^)+1,2);
- if length(MHs) < 2 then MHs := '0' + MHs;
- if length(DMs) < 2 then DMs := '0' + DMs;
- if length(YSs) < 2 then YSs := '0' + YSs;
- Data^ := MHs + DMs + YSs;
- end;
- if (length(Data^) >= 6) and (pos(Delim,Data^) = 0) then
- begin
- val(copy(Data^,1,2), MH, Code);
- if Code <> 0 then MH := 0;
- val(copy(Data^,3,2), DM, Code);
- if Code <> 0 then DM := 0;
- val(copy(Data^,5,2), YS, Code);
- if Code <> 0 then YS := 0;
- if DataType = DDate then
- begin
- if (MH > 12) or (MH < 1) or
- (DM > 31) or (DM < 1) then Ok := false;
- end
- else
- begin
- if (MH > 23) or (MH < 0) or
- (DM > 59) or (DM < 0) or
- (YS > 59) or (YS < 0) then Ok := false;
- end;
- insert(Delim,Data^,5);
- insert(Delim,Data^,3);
- end
- else
- Ok := false;
- end;
-
- DByte : begin
- val(Data^, MH, Code);
- if (Code <> 0) or (MH > 255) or (MH < 0) then Ok := false;
- end;
-
- DShortint :
- begin
- val(Data^, MH, Code);
- if (Code <> 0) or (MH < -127) or (MH > 127) then Ok := false;
- end;
-
- DInteger :
- begin
- val(Data^, MH, Code);
- if (Code <> 0) or (MH < -32768) or (MH > 32767) then Ok := false;
- end;
-
- DWord : begin
- val(Data^, MH, Code);
- if (Code <> 0) or (MH < 0) or (MH > 65535) then Ok := false;
- end;
- end;
- CheckRange := Ok;
- end;
-
- procedure TFInputLine.ErrorHandler;
- var
- MsgString : string[80];
- Params : array[0..1] of longint;
- Event: TEvent;
- begin
- fillchar(Params,sizeof(params),#0);
- MsgString := '';
- case DataType of
- DDate : MsgString := ' Invalid Date Format! Enter Date as MM/DD/YY ';
- DTime : MsgString := ' Invalid Time Format! Enter Time as HH:MM:SS ';
- DByte,
- DShortInt,
- DInteger,
- DWord : begin
- MsgString := ' Number must be between %d and %d ';
- case DataType of
- DByte : Params[1] := 255;
- DShortInt : begin Params[0] := -128; Params[1] := 127; end;
- DInteger : begin Params[0] := -32768; Params[1] := 32768; end;
- DWord : Params[1] := 65535;
- end;
- end;
- end;
- MessageBox(MsgString, @Params, mfError + mfOkButton);
- end;
-
- end.
-