About WinProc
Types
Variables
Procedures
The docs aren't that great. The problem is that the procedures are very general purpose ones, and any effort to describe them tends to get vague. Anyway, I did my best, I'll improve them gradually, for the rest I suggest you look at the example that describes almost all procedures, QuoSetup
The entire system is also usable for debugging, I used specially the array/record editting for that. That procedure pops up a window (and leaves the original screen untouched) in which you can edit/view an array/record.
The WinProc-EWindow combination is also quite small. You can create a quite nice working setup program, 30 kb (uncompressed) in size. Compile QuoSetup.pp to get the idea. Quosetup edits two arrays and one general configuration record
According to me, and most people would looked at it, the main missing feature is the fact that you can't change options on the fly. (e.g. you have on boolean field. If it has a certain value, you have to fill in several other fields. The problem is that the fields are always visible, you can't hide them depending on a boolean)
You can add some support for hot keys though, through the "otherkeys". This was mainly meant for a (context-sensitive) helpfunction, but that's not ready for FPC yet (next on the list though)
Editting/Selection procedures
Big String system
The problem I designed it for was printing all the fieldnames for EditRec, which were simply a standard sequence over and over again :
GotoXY(X,Y); TextColor(Col1); TextBackGround(Col2); Writeln('text');I wanted to simply this, what I did was simply putting the data in one 0 terminated string :
CHR(x)+CHR(y)+CHR(length of entry field)+CHR(color of text) + text +CHR(0)This is, if you indent it properly, a bit more readable and more maintainable than 50 times the above statements. As you can see I added an extra parameter, the length of the input-field AFTER the query. That value isn't used for printing the actual field names, but it is used (together with the length of the fieldname) to write the value of the fields of the record.
This way, all data I can move around dynamically is in this textobject. (I can't add things like the type and the name of the variable or field to be editted, since these are compiled in the code)
The BigString type is no more than several of the above array of char strings after eachother, plus an extra terminating #0 at the end.
This is also handy, this way you can use PCHAR routines from unit String to manipulate them.
The only problem is that textual constants in FPC can only have maximal size 256 ( Pascal STRING maximal size). I circumvented this by patching Msg2Inc supplied with the compiler RTL, which made it a kind of preprocessor.
Also, the type is purely binary. You could dump it in a file and load it dynamically
The only better system is patching the TheDraw loader to put a screen to a certain X1,Y1,X2,Y2 region on the screen. I'm not sure if TheDraw exports pascal code for non 80x25 screens.
See devel/quo1.txt and devel/quomake.bat
VAR NormalColor, { Some colors required by the Edit } HilightColor, { procedures } EditColor : BYTE; FieldSpace : BYTE; { Character used as background in InputStr. Default=177. }The above variables are global to all edit procedures (EditArray,Selector, EditRecord and InputStr(only FieldSpace)
Normalcolor is, as the name already says, the color for normal writes, HighlightColor for hilighting, normally the inverse of NormalColor.
FieldSpace is the background character which is substituted for space in InputStr, to be able to see the editablearea. This character isn't substituted for space in the editted string, only on the screen
EditColor isn't used at the moment, but might be (re)implemented in the future.
Editting records
Big String
ProcDisplayField = PROCEDURE( Rec : LONGINT; Line : LONGINT; Color : BYTE );This procedure is passed to several editting procedures to display a record on the screen.
EditArray uses a procedure of this type to display the line with information per record in the window where you select which record you edit. The first parameter is the record number, the second the line in the window the information should be written to. The third parameter is the color (16*background+foreground) the data should have. (used for highlighting)
EditRecord uses a procedure of this type to display each field. The first parameter is the number of the field (first=1), the second the record number. The third is again the color, also for highlighting
ProcManipRec = PROCEDURE( VAR CurRec:LONGINT;VAR TotalRec:LONGINT);Procedure of this type are used by EditArray to add or remove records. The first is the record to be removed (if deleting), the second is the total number of records. The reason Inserting a record also receives the number of the current record, is to allow insertion at a certain point in the array.
ProcLeftOverKeys = PROCEDURE( Key:WORD;VAR CRec,NRec: LONGINT);This procedure handles all non standard keys (which are Enter,Escape ins and del), the parameters are the pressed key, the current record, and the number of records respectively
ProcBackGr = PROCEDURE;This parameter type pops up the "background" in a window. (all text you see in quosetup when editing a record which is not editable, and the top bar in the array-editting procedures (in yellow)). Currently it is only called once, right after opening the window. (Since editting takes place in a separate window).
ProcEditField = PROCEDURE( Field:LONGINT;Rec:LONGINT );This procedure should edit field number "Field" in record number Rec.
EditRec = RECORD EditField : ProcEditField { Invoked when editing a field of the record} InitEdit : ProcBackGr { Display background before record is displayed} DisplEdit : ProcDisplayField { Display the contents of the fields} X1,Y1,X2,Y2 : LONGINT; { dimensions of the selector window} Frame : BOOLEAN; { if true then Frame= On? and the following fields get meaning} FrameType : String[10]; { Frame : type} ForeFrame, { colors of Frame} BackFrame, BackCol, { Color of edit background} ForeCol : BYTE ; { Color of edit (Default)} END;This very important structure contains all information necessary to edit a record. Most data is about the window which should be opened for editting (by EWindow, see WinDef for more information), the other 3 procedure-variables are the actual editing procedures. EditField is called to edit a field, InitEdit to display the non editable text in the window to be created, and DisplEdit to display the values of a field.
SelectEditRec= RECORD X1,Y1,X2,Y2 : LONGINT; { Dimensions of selector window} Frame : BOOLEAN; { Frame On? If yes then } FrameType : String[10]; { Type of Frame } ForeFrame, { Colors of Frame } BackFrame, BackCol, { Default colors of window} ForeCol : BYTE; BackGrProc : ProcBackGr; { Display background text in window} DisplLine : ProcDisplayField; { Display one record } DelRecProc, { Delete one record } InsRecProc : ProcManipRec { Insert one record } ElseProc : ProcLeftOverKeys { All other keys are handed to this proc } Fields : LONGINT; { # Fields in record} VisLines : LONGINT; { # Lines visible in window. } MaxRec : LONGINT; { # Maximum records in array } END; This very important structure contains all information necessary to edit an array with EditArray (or something similar, a linked list, any collection of more than one element) except the information already avaiable in an EditRecord.
BackGrRec = RECORD Data : PChar; Pointer to the data. Items : LONGINT; Number of strings in the data Indices : ARRAY[0..NrItemMax] OF LONGINT; Start of each string Bytes : LONGINT; Number of bytes in data END;This simple recordtype speeds up the Big string handling. The data is only parsed once, by InitBigStr, and the other procedures use the indexes from this record
FUNCTION InputStr(VAR S:String;X,Y,Len:LONGINT;TextIn:BOOLEAN;CharAllow:CHARSET):BOOLEAN;
Description
Inputs a string, in an input field on positions X,Y on the screen, Len characters long. If TextIn is TRUE, S contains already a valid, default value. The CHARSET CharAllow contains the allowed characters for input.
Available editting keys:
See Also FieldSpace
Example:
VAR S : String; BEGIN S:='Edit me'; InputStr(S,10,10,20,TRUE,['A'..'Z','a'..'z',' ']); Write(S); END;
PROCEDURE SetAttrs(Col : BYTE);
Description
Set attributes from one byte, Col=TextColor+16*TextBackground. Faster under Linux because only one character is written.
Example:
BEGIN SetAttrs(15); {White on black; 15+16*0} Write(S); END;
FUNCTION Selector( Display : ProcDisplayField; MaxLines, Lines : LONGINT; VAR First,Curr : LONGINT ) : LONGINT;
Description
(Vague,complete description:) A very generic procedure which manages a selection. The number of selectable items(Lines) can be bigger than the number of at-once-visible selectable items (1..MaxLines slots)
The "Display" procedure does the most work, (see also ProcDisplayField), it outputs a certain item to a certain line in a certain color. The item is identified by a number (1..Lines), the line is the number of the visible slot on the screen (1..maxlines).
First is the number of the item (1..Lines-Maxlines) which is in the first visible slot. Cur is the currently highlighted item, in slot Cur-First.
(Simple, but not complete description:) The procedure is very vague, as you can see in the above text. Rougly, it manages a selection window with possibly more items than can be showed at once. The exact displaying of the items is done by a procedure which you define, which has to put a certain record on a line (or column) on the screen in a certain color. Selector takes care of the cursorkeys, and checking bounderies (if last item, then pressing (down) isn't possible anymore etc), which item to highlight etc.
Allowed keys are arrow up and down, page up and down, home and end, which are clear I think
Every other key pressed terminates the selection. The pressed key is returned, together with the itemnumber which was selected when
that key was pressed (in Curr), and the number of the first line visible on screen (in First).
If Escape was pressed, Curr is reset to 0.
See QuoSetup's mainmenu for a simple example. Also EditArray uses this procedure, so if you select the second or third option in Quosetups mainmenu, you get into another Selector menu(you select which record in an array to edit)
Example: (Too complex, see QuoSetup)
PROCEDURE Toggler(VAR Value : BYTE;InString : String;Increment:BOOLEAN);
PROCEDURE Toggler(VAR Value : Boolean;InString : String;Increment:BOOLEAN); Description
This is a procedure which I use a lot in the procedures that I pass to EditRecord (DisplayEdit and EditField procedures) to easy create "toggle" fields.
Toggle fields are a kind of ordinal fields, you can select a certain option by pressing enter. There are two versions, a byte-typed and a boolean version.
Since overloading didn't exist in the M2 version, the boolean version is new, and therefore not yet used in QuoSetup
Value indicates the status of the togglefield, InString is a string with all options separated by @ characters, and prefixed by the number of options (e.g. YesNo=#2+'YES@NO'). If Increment is true, Value is updated, otherwise only it's value is written. Caller has to issue a gotoXY to make sure the text gets on the screen on the right spot.
Example: (Too complex, see demo QuoSetup.pp)
PROCEDURE EditRecord( VAR Edit:EditRec; MaxField : LONGINT;Rec:LONGINT );
Description
Edit a group of variables with a dialogue screen. See the description EditRec for more details about the EditRec parameter. Often used for records, but any group of changable variables can be used, since only own code (passed via the procedure variables) accesses the variables.
The procedure
Toggle fields are a kind of ordinal fields, you can select a certain option by pressing enter. There are two versions, a byte-typed and a boolean version.
Since overloading didn't exist in the M2 version, the boolean version is new, and therefore not yet used in QuoSetup
Value indicates the status of the togglefield, InString is a string with all options separated by @ characters, and prefixed by the number of options (e.g. YesNo=#2+'YES@NO'). If Increment is true, Value is updated, otherwise only it's value is written. Caller has to issue a gotoXY to make sure the text gets on the screen on the right spot.
Example: (Too complex, see QuoSetup)
PROCEDURE EditArray(VAR R : SelectEditRec;VAR Edit :EditRec;VAR NrRec:LONGINT);
Description
This procedure uses the browsing capability of Selector to select a specific record, and then EditRec to edit the record. After editting control is returned to selector.
Edit holds all data for the editting with EditRec, see EditRec for more details.
The record R contains all other data, for the selection proces. This explained in detail in the SelectEditRec section
Example: (Too complex, see QuoSetup)
FUNCTION InitBigStr(TS : PChar;Bytes:LONGINT):PBackGrType;
Description
See also the Big String section
InitBigStr creates indexes to the indivual strings in TS. Bytes is the number of bytes in TS. A pointer to the indexes on the heap is returned.
The indexes (pointer type) are used by all other Big String procedures, and therefore, InitBigStr should always be called to intialise the type.
VAR TSInt : PBackGrType; {Assuming that BackGrndInterTxt contains the array of char type data created with Msg2Inc} BEGIN TSINT:= InitBigStr(@BackGrndInterTxt, SIZEOF(BackGrndInterTxt)); {Init Big String system} ClrScr; {Crt procedure, clear screen} BuildScreenFromPtr(TSInt); {Draw screen} KillBigStr(TSInt); {Remove indexes from memory} END;
PROCEDURE KillBigStr(VAR TS : PBackGrType); Description
Opposite of InitBigStr, remove the indexes from the heap.
Note: If TS^.Data is your own reference to the data, and you allocated the data dynamically, then you should dispose TS^.Data first
Example:
VAR TSInt : PBackGrType; {Assuming that BackGrndInterTxt contains the array of char type data created with Msg2Inc} BEGIN TSINT:= InitBigStr(@BackGrndInterTxt, SIZEOF(BackGrndInterTxt)); {Init Big String system} ClrScr; {Crt procedure, clear screen} BuildScreenFromPtr(TSInt); {Draw screen} KillBigStr(TSInt); {Remove indexes from memory} END;
PROCEDURE BuildScreenFromPtr(TS:PBackGrType);
Description
Opposite of InitBigStr, remove the indexes from the heap.
Note: If TS^.Data is your own reference to the data, and you allocated the data dynamically, then you should dispose TS^.Data first
Example:
VAR TSInt : PBackGrType; {Assuming that BackGrndInterTxt contains the array of char type data created with Msg2Inc} BEGIN TSINT:= InitBigStr(@BackGrndInterTxt, SIZEOF(BackGrndInterTxt)); {Init Big String system} ClrScr; {Crt procedure, clear screen} BuildScreenFromPtr(TSInt); {Draw screen} KillBigStr(TSInt); {Remove indexes from memory} END;
FUNCTION GetStrPtr(TS: PBackGrType;NrFoc : LONGINT):PChar;
Description
Returns a PCHAR pointer to NrFoc'ed string in the BigString.