home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 21
/
CD_ASCQ_21_040595.iso
/
dos
/
prg
/
pas
/
tvgr70
/
editors.doc
< prev
next >
Wrap
Text File
|
1994-11-15
|
28KB
|
667 lines
file EDITORS.DOC
8/10/94
{11/14/94 bug fix THeapFileEditor.InitBuffer}
DOCUMENTATION FOR THE GEDITORS UNIT
This unit is based on BP7.0 maintenance release of Editors.PAS.
The Editors unit contains the basic text editor view (TEditor). It
also contains a text file editor and a window for the text file
editor with Scrollbars and cursor position Indicator. These are for
ASCII text files. TMemo is a descendant of TEditor that is designed
for insertion as a control in Dialogs. See Turbo Vision 2.0 doc
Chapter 15 for general discussion plus description of several vital
programming issues necessary to using TEditor in your program. (If
you don't read about memory usage, your program will bomb.)
The TVGraphic GEditors unit offers the choice between the Turbo
Vision (TV) Editor behavior and several behavior changes that most
users will see as improvements. These improvements are controlled by
setting the efImprovedBehavior flag in the EditorFlags variable. It
is set by default in TVGraphic.
Note that TV's Editors unit does NOT duplicate the editing behavior
of the Pascal IDE (Integrated Development Environment). In
particular, TV and TVGraphic's selected (highlighted) blocks are
released whenever the cursor is moved. You cannot Undo more than once
or after you have moved the cursor. The max file size is 64K. EMS is
not supported though protected mode works.
Several minor features from the IDE have been added to TVGraphic's
Editors. These include place markers, adjustable tab widths, and
selecting a word in response to Ctrl-KT. The behavior when moving the
cursor word by word or deleting the word at the end of a line is like
the IDE.
Two major features have been added. One is Wordwraping. The other is
the option of adding a pull down menu at the Close Icon of an editing
window.
REGISTRATION NOTE: non-Registered versions of TVGraphic come with a
demo version of the GEditor unit. Edited files and memos can not
be saved with the demo unit.
---------------------------------
Features
Indicator - displays "I" for AutoIndent, "W" for WordWrap enabled,
diamond shape for modified.
Place Markers
Place markers are numbered 0 to 9 and are not visible. Set by
Ctrl-K-number. Jump to by Ctrl-Q-number. Will not jump to a marker
unless set. Deleting a block of text does not delete an imbedded
marker - it remains where the block was.
SelectWord called by Ctrl-K-T
selects entire word under the cursor, leaves cursor at end of word
Tabs
Display
With wordwrap it is useful to see the imbedded tabs in a
document. Tabs appear as as right arrow. Control the display with
flag efShowTabs in EditorFlags.
Width
TabWidth field added to TEditor
Tab width may be set to a power of two (2,4,8,16..). The editors
unit has a default tab width constant. StdEditorDialog has an
added dialog to set the tab width.
Right Margin
Field added to TEditor.
Used when reformating or wordwrapping.
StdEditorDialog has added dialog to set right margin.
Wordwrapping - Reformating(Re-Aligning) text
(NOTE: These operations are designed to work with TVGraphic's
Improved Behavior option for Editors.)
Wordwrap and Reformating work by looking at the last character in
a line. (The character that precedes the carriage return in the
buffer.) If the character is a space or a Tab, then the line's
carriage return is treated as a "soft" or movable carriage return.
Otherwise a line's carriage return is "hard" - it will not be
moved/deleted. No special characters are added to the file.
Wordwrapping and Reformating work with respect to the Right Margin.
They also handle Tabs. You may change the margin or Tabwidth at any time.
They will work with AutoIndent enabled but see comments under
Wordwrap below.
Reformating
A block reformating command (cmBlockReformat, type Ctrl-B) has
been added which will reformat text based on the Right Margin.
The reformating command may be disabled clearing a
flag in the VOptions field. Reformating begins at the start of
the line the text cursor is on and continues until it reaches a
line with a "hard" carriage return.
Calling Reformat repeatedly until the end of the document will
re-align all the text.
while LineStart(CurPtr) <> LineStart(NextLine(CurPtr)) do
Reformat(CurPtr);
WordWrap
Wordwrapping is enabled if the TEditor.Wordwrap field is true.
This field is set during construction to the value of global
DefaultWordWrap.
Wordwrap strips any leading spaces from lines following the one
you are typing on. Wordwrap will also work (but slowly and with
considerable redrawing flicker) with AutoIndent mode. It is a
little strange to use this combination but it is nice for
document files like this one.
The Undo capability is limited to 255 chars when Wordwrap is
enabled. A dialog will ask you if you want to procede with an
Edit operation that will exceed this limit.
Drop down window menu
Optionally every window can have a drop down menu in the upper left
corner. These menus are of type TWindowPopup. Their constructor has
no TRect parameter, just a PMenu. The menu appears if the window's
Close Icon is pressed or if Alt-spacebar is pressed. Create a
TWindowPopup and assign it to the window's Menu field. Easy. See
example in Editors demo program.
Behavior Options
Which chars are considered parts of words
TV provides the set (chars not in this set act to separate words)
WordChars set of Char = ['0'..'9', 'A'..'Z', '_', 'a'..'z',''''];
TVGraphic adds the ' char to the set. Modify as required.
Capitalization of non-English characters
This can be accomplished globally in TVGraphic by overriding the
UpCase function. See file European.Doc
EditorDialog / Default Editor Dialog
In TV's Editors unit,
the default EditorDialog := DefEditorDialog;
DefEditorDialog does nothing except return cmCancel.
You would normally assign the real Editor dialog
EditorDialog := StdEditorDialog
in your code. This is the default assignment in GEditors.
Improved Behavior summary
Deleting Text with TVGraphic's Improved behavior
To cause a selected block to be deleted during a call to
InsertText or InsertBuffer, you must set TEditor field
NeedToDelete to true before the call. The call will reset it
false. NeedToDelete has no effect if there is no selected block.
efImprovedBehavior flag for EditorFlags variable.
Fixes TV Quirks. set by default
With TV, if you enter any alpha key while a block of text is
selected (highlighted), then the selected text is deleted and the
letter is entered. If you then move the cursor at all, you cannot
undelete the deleted text. Improved behavior will deselect the
block and enter the letter you typed.
The Delete key will delete a character unless there is a
highlighted block - then deletes the block but not the character.
Improved behavior will cause the Delete key to deselect the block
and delete the character.
Use the usual Ctrl-Delete to delete a block.
Or if you have a ClipBoard, then Shift-Delete will "Cut" the
block to the Clipboard.
edvEnableDeleteKeyForBlockDelete flag for TEditor.VOptions
When using efImprovedBehavior, pressing the Delete key will NOT
delete a selected block. This is like the IDE Editor window.
However you may want the Delete key to delete the selected block,
perhaps in a TMemo. If so, then set this flag after construction.
Cursor positioning
When moving the cursor from start of one line to the end of the
previous line, TV leaves cursor at start of last word of previous
line. Improved behavior leaves cursor at end of line.
Moving left (but not right) a word at a time skips over blank
lines. Improved behavior does skip over blank lines.
InsertText, SearchReplace do not highlight the new text. Improved
behavior will highlight new text.
Editors BUG Status
Editors unit has a number of (non-Borland) published bugs.
Most were fixed in the maintanence release of Pascal 7.0 that this
unit is based on.
1. ^T in the last word of line deletes the carriage return as well as
the word. Fixed in TVGraphic.
2. Search
^QF, then ^L and then typing letter deletes text.
This is the way TV's Editor's code must work by its conceptual
design. Fixed in TVGraphic.
3. Bug (non-published)
When text cursor is in Overwrite mode, a block of text is selected
and the user enters a letter key, then the letter is inserted as
if cursor is in Insert mode. Fixed in TVGraphic.
4. Editors uses variable ShiftState which is not reliable in
protected mode. TVGraphic uses function GetShiftState.
---------------------------
A FileEditor NOT using TV's special memory buffers
(The following discussion only applies in real mode. In protected
mode, TV uses the protected mode memory manager which does not
restrict the size of the heap when using FileEditors.)
TV provides a special resizable buffer memory system for its
FileEditor objects. This system avoids memory fragmentation.
This is a big advantage when the purpose of the program is to have
numerous file editors open. The disadvantage is that you have to
specify at the beginning of your program how much heap space
to reserve for the non-FileEditor portion of your program.
The FileEditors get the rest of the memory. You can't change this
later.
It is simple to override three methods to create a descendant of
FileEditor that uses the regular heap and respects the safety pool.
The code below allows the Buffer to shrink and grow. Changing the
buffer size is done by creating a second buffer and then copying
the contents from the first buffer. So there must be enough memory for
both buffers to exist at once.
If your file/Edit buffer is/becomes over 32K bytes, the code will
set the Buffer size to the max $FFF0 (almost 64K).
Note: Repeated changing of the Buffer size mixed with other memory
allocations from the heap may/will fragment the heap.
NOTE: The ClipBoard must use the same memory scheme (type of
FileEditor) as your other FileEditors.
THeapFileEditor = object(TFileEditor)
procedure InitBuffer; virtual;
procedure DoneBuffer; virtual;
function SetBufSize(NewSize: Word): Boolean; virtual;
end;
procedure THeapFileEditor.InitBuffer;
begin
{make an initial allocation > 0 - here 4K bytes}
Buffer := MemAlloc($1000);
{next line added 11/14/94, match allocation above}
if Buffer <> nil then BufSize := $1000;
end;
procedure THeapFileEditor.DoneBuffer; virtual;
{code from TEditor}
begin
if Buffer <> nil then
begin
FreeMem(Buffer, BufSize);
Buffer := nil;
end;
end;
function THeapFileEditor.SetBufSize(NewSize: Word): Boolean;
var
N: Word;
BPtr : PEDitBuffer;
begin
SetBufSize := False;
if NewSize = 0 then NewSize := $1000 else
if NewSize > $F000 then NewSize := $FFF0 else
NewSize := (NewSize + $0FFF) and $F000;
if NewSize > $8000 then NewSize := $FFF0; {assign max mem if > 32K}
if NewSize <> BufSize then
begin
BPtr := MemAllocSeg(NewSize); {allocate new buffer}
if BPtr = nil then Exit; {Exit if not successful}
N := BufLen - CurPtr + DelCount;
Move(Buffer^[0], BPtr^[0], CurPtr);
Move(Buffer^[BufSize - N], BPtr^[NewSize - N], N);
FreeMem(Buffer, BufSize); {free old buffer}
Buffer := BPtr; {point to new buffer}
BufSize := NewSize;
GapLen := BufSize - BufLen;
end;
SetBufSize := True;
end;
Now we need to create a descendant of TEDitWindow.Init that uses
the new HeapFileEditor.
PHeapEditWindow = ^THeapEditWindow;
THeapEditWindow = object(TEditWindow)
constructor THeapEditWindow.Init(var Bounds: TRect;
FileName: FNameStr; ANumber: Integer);
end;
constructor THeapEditWindow.Init(var Bounds: TRect;
FileName: FNameStr; ANumber: Integer);
const
Inset = FrameInset; {distance from edge of window in pixels}
var
HScrollBar, VScrollBar: PScrollBar;
Indicator: PIndicator;
R: TRect;
begin
inherited Init(Bounds, '', ANumber);
Options := Options or ofTileable;
R.Assign( 18*Charlen, Size.Y-Inset-HScrollBarWidth,
Size.X-2*Charlen, Size.Y-Inset);
HScrollBar := New(PScrollBar, Init(R));
Insert(HScrollBar);
R.Assign(Size.X-Inset-VScrollBarWidth, Boxheight,
Size.X-Inset, Size.Y-Boxheight);
VScrollBar := New(PScrollBar, Init(R));
Insert(VScrollBar);
if Frame <> nil then with Frame^ do
VOptions := VOptions or tfVScrollBar or tfHScrollBar;
R.Assign( 2*Charlen, Size.Y-Inset-HScrollBarWidth,
16*Charlen -1, Size.Y-Inset-1);
Indicator := New(PIndicator, Init(R));
Insert(Indicator);
GetMaxSubViewSize(R);
{use THeapFileEditor type}
Editor := New(PHeapFileEditor, Init(
R, HScrollBar, VScrollBar, Indicator, FileName));
Insert(Editor);
end;
-----------------------
REFERENCE
Note: TVGraphic's TEditor.ClipCopy
sets ClipBoard^.CanUndo false automatically. Borland recommends always
doing this (manaully) for the clipboard on page 267 of TV2.0 guide.
Note: TMemo palette has been changed.
* = unique to TVGraphic
+ = modified in TVGraphic
const
used in TEditor.VOptions field
* edvEnableDeleteKeyForBlockDelete = $40;
When using efImprovedBehavior, the Delete key no longer deletes
a selected block - it just deletes the character the cursor is on.
This is like the IDE. To delete a selected block, use Ctrl-Delete.
If you want the Delete key to delete the selcted block, set
this flag in the view's VOptions field.
* edvReformat = $40; {cmBlockReformat enabled when this bit set}
global
values used to set TabWidth, Wordwrap in TEditor.Init
* DefaultTabWidth : byte = 4; {width of tabs, Value MUST be power of two!}
* DefaultWordWrap : boolean = true; {Wordwrap is disabled if false}
* MinRm = 20; {right margin limits for wordwrap/reformating}
* MaxRm = 128;
* cmBlockReformat = 526; keycode Ctrl-B
* cmSelectWord = 527; keycode Ctrl-K-T
* cmJumpToMark = 528; keycode Ctrl-Q-number
* cmSetMark = 529; keycode Ctrl-K-number
* cmSetRightMargin = 530;
* cmSetTabWidth = 531;
* cmToggleWordwrap = 532;
MaxLineLength = 256; used by TEditor
* MaxMemoLineLength : byte = 128; used by TMemo
used with StdEditorDialog
* edWordWrapNoUndo = 11;
* edSetRightMargin = 12;
* edSetTabWidth = 13;
used in EditorFlags to control all Editor views
* efShowTabs = $0080;
* efImprovedBehavior = $1000;
TMemo Palette
+ CMemo = #19#20; {was CMemo = #26#27; TV2.0}
{#19#20 is InputLine Normal,Selected}
global functions
+ function StdEditorDialog(Dialog: Integer; Info: Pointer): Word;
add dialogs for
setting right margin
setting tab width
notifying user that current operation is too big to Undo with
Wordwrap enabled.
const
+ WordChars: set of Char = ['0'..'9', 'A'..'Z', '_', 'a'..'z',''''];
added apostrophe ' to WordChars
+ EditorDialog: TEditorDialog = StdEditorDialog {DefEditorDialog};
assign the StdEditorDialog, rather than DefaultEditorDialog
which does nothing.
EditorFlags: Word = efBackupFiles + efPromptOnReplace
* {added} + efImprovedBehavior + efShowTabs;
type
PIndicator = ^TIndicator; only new or changed methods/fields listed
TIndicator = object(TView)
* AutoIndent : boolean;
* WordWrap : boolean;
+ constructor Init(var Bounds: TRect);
sets VFont := font8x8;
+ procedure SetState(AState: Word; Enable: Boolean); virtual;
calls inherited SetState, then
if AState and (sfActive) <> 0 then DrawView;
no code for sfDragging
+ procedure SetValue(ALocation: TPoint; AModified,AIndent,AWrap : boolean);
Also sets new fields AutoIndent and Wordwrap based on AIndent,AWrap.
Redraws if necessary.
end;
type
PEditor = ^TEditor; only new or changed methods/fields listed
TEditor = object(TView)
* WordWrap : boolean;
* Spacing : TPoint; {READ ONLY - set with SetSpacing
number of pixels that view scrolls by -
Spacing.x must be 8,
match Spacing.y to your font}
* TabWidth : byte; {2,4,8,...}
* RightMargin : byte; {for reformating and wordwrap}
* NeedToDelete : boolean; {temp,
used when efImprovedBehavior is set in EditorFlags.
Or with wordwrap.
Must set true before calling InsertText or InsertBuffer if
you want the existing selected block to be deleted.
No effect if no selected block.}
+ constructor Init(var Bounds: TRect;
AHScrollBar, AVScrollBar: PScrollBar;
AIndicator: PIndicator; ABufSize: Word);
usual code plus
SetSpacing(Charlen, Boxheight); {default settings,
Must precede SetBufLen to prevent division by 0 in DoUpDate}
SetBufLen(0); {existing code}
EventMask := EventMask or evTimerTick; need to enable TimerTick
events so can flash cursor
TabWidth := DefaultTabWidth;
if DefaultRightMargin = 0 then
RightMargin := ((Size.x+1) div Spacing.x)-1 {autosize}
else RightMargin := DefaultRightMargin; {default setting}
VOptions := edvReformat;
Wordwrap := DefaultWordWrap;
VFont := font8x14; text font
+ constructor Load(var S: TStream);
load Wordwrap,Spacing,TabWidth,RightMargin
+ procedure ChangeBounds(var Bounds: TRect); virtual;
use Spacing field
+ procedure ConvertEvent(var Event: TEvent); virtual;
Event.InfoChar := UserChar; {return chars "0"-"9" for keys 0..9}
+ function CursorVisible: Boolean;
use Spacing field
+ procedure DeleteSelect;
sets NeedToDelete := true; before calling InsertText
+ procedure Draw; virtual;
use Spacing.y
+ procedure HandleEvent(var Event: TEvent); virtual;
if ImprovedBehavior:
a selected block is not deleted whena keystoke is entered.
cmWordLeft - modified to leave cursor at end of line
cmBackSpace - modified if Wordwrap is true
cmDelChar - if ImprovedBehavior and HasSelection
and (VOptions and edvEnableDeleteKeyForBlockDelete <>0) then
DeleteSelect
otherwise original DeleteRange()
all cases
cmIndentMode: begin
AutoIndent := not AutoIndent;
UpDate(ufUpDate);
end;
cmBlockReformat: if (VOptions and edvReformat <> 0)
then Reformat(CurPtr, true);
cmSetMark : SetPlaceMarker(Ord(Event.InfoChar) - 48);
cmJumpToMark: JumpToPlaceMarker(Ord(Event.InfoChar)-48, SelectMode);
cmSelectWord : SelectWord;
cmSetRightMargin : StdEditorDialog(edSetRightMargin, @RightMargin);
cmSetTabWidth : StdEditorDialog(edSetTabWidth, @TabWidth);
cmToggleWordwrap : begin
WordWrap := not WordWrap;
UpDate(ufUpDate);
end;
* function HorzTextOffset : integer; virtual;
HorzTextOffset := 1;
* function ImprovedBehavior : boolean;
true if efImprovedBehavior flag set in EditorFlags and
not the Clipboard
+ function InsertBuffer(var P: PEditBuffer; Offset, Length: Word;
AllowUndo, SelectText: Boolean): Boolean;
updates Placemarkers.
if ImprovedBehavior then to kill instant delete of selected block.
much redrawing control
+ function InsertFrom(Editor: PEditor): Boolean; virtual;
add highlight to inserted block if improved behavior
+ function InsertText(Text: Pointer; Length: Word;
SelectText: Boolean): Boolean;
warn user if block is too big to Undo if in wordwrap
if WordWrap and CanUndo and NeedToDelete
and ((SelEnd-SelStart) > 255) then
StdEditorDialog(edWordWrapNoUndo, nil)
after calling InsertBuffer,
call Reformat(CurPtr, false) if Wordwrap is true.
* procedure Reformat(APtr : word; BlockReformat : boolean);
Does the reformating starting at line containing APtr.
If BlockReformat is true, leaves cursor at end of block,
otherwise cursor doesn't move.
+ procedure ScrollTo(X, Y: Integer);
use Spacing
+ function Search(const FindStr: String; Opts: Word): Boolean;
+ procedure SetSelect(NewStart, NewEnd: Word; CurStart: Boolean);
added much redrawing control
* procedure SetSpacing(XSpacing, YSpacing : byte); virtual;
Spacing.x := Charlen; {required in v2.0}
if YSpacing = 0 then Spacing.y := 1; {prevent divide by zero}
else Spacing.y := YSpacing;
UpDate(ufView); {redraw}
+ procedure Store(var S: TStream);
store Wordwrap,Spacing,TabWidth,RightMargin
+ procedure TrackCursor(Center: Boolean);
use Spacing
+ procedure Undo;
usual code, then add
>NeedToDelete := true;
InsertBuffer(Buffer, CurPtr + GapLen - Length, Length, False, True);
>if WordWrap then Reformat(CurPtr, false);
* function VertTextSpacing : integer; virtual;
VertTextSpacing := Spacing.y;
private
+ UpdateFlags: word {Byte};
* OldSelStart : word; {temp, previous value of SelStart}
* OldSelEnd : word; {temp, previous vsalue of SelEnd}
* OldDelta : TPoint; {temp, saves previous value of Delta - see if
scrolled}
* SaveCurPtr : word; {temp, saves previous value of CurPtr}
* Reformating : boolean;
* PlaceMarker : array [0..9] of Word; {array to hold place markers}
* SaveDelCount : word;
* procedure JumpToPlaceMarker (MarkNum : Byte; SelectMode : Byte);
* procedure SetPlaceMarker(MarkNum : Byte);
* procedure UpdatePlaceMarkers (AddCount : Word; EraseCount : Word;
StartPtr, EndPtr : Word);
* procedure SelectWord;
if CutPtr is in a word, this procedure will select the entire word.
* function WrapWordLength(APtr : word) : word;
returns length of word at APtr based on what word wrap
algorithm can wrap. returns 0 for CR only line.
+ function CharPos(P, Target: Word): Integer;
+ function CharPtr(P: Word; Target: Integer): Word;
+ function ClipCopy: Boolean;
ClipBoard^.CanUndo := false; {Borland recommendation}
+ procedure DeleteRange(StartPtr, EndPtr: Word; DelSelect: Boolean);
If DelSelect is true, does not call DeleteSelect for existing
select block if ImprovedBehavior is true.
If DelSelect is false, only calls DeleteSelect if there is
something to delete between StartPtr,CurPtr,EndPtr.
+ procedure DoUpdate;
redrawing code completely rewritten
+ procedure DoSearchReplace;
use Spacing
set NeedToDelete := true; before calling InsertText
set last parameter in call to InsertText to ImprovedBehavior
+ procedure DrawLines(Y, Count: Integer; LinePtr: Word;
DrawAll : boolean); virtual;
{rewritten, added new parameter}
+ procedure FormatLine(var DrawBuf; LinePtr: Word;
Width: Integer; Colors: Word; TabWidth_1 : byte; EdFlags : word);
{new parameter}
+ function GetMousePtr(Mouse: TPoint): Word;
+ function NextWord(P: Word; Deleting : boolean): Word;
added new parameter
code rewritten so Ctrl-T (^T) in last word of line doesn't
erase the line's carriage return.
If not deleting the word, go to first WordChar of next word.
If deleting, then go to last WordChar of this word. Like IDE.
+ function PrevLine(P: Word): Word;
+ procedure SetBufLen(Length: Word); virtual; {add virtual 1/28/94}
+ procedure Update(AFlags: word {Byte});
end;
type
PMemo = ^TMemo; only new or changed methods/fields listed
TMemo = object(TEditor)
* constructor Init(var Bounds: TRect;
AHScrollBar, AVScrollBar: PScrollBar;
AIndicator: PIndicator; ABufSize: Word);
calls inherited Init, then
sets ofFramed flag in Options to draw its own frame
function GetPalette: PPalette; virtual;
uses modified CMemo palette
private
* procedure SetBufLen(Length: Word); virtual;
TEditor.SetBufLen(Length);
Limit.X := MaxMemoLineLength;
end;
type
PFileEditor = ^TFileEditor; only new or changed methods/fields listed
TFileEditor = object(TEditor)
FileName: FNameStr;
+ constructor Init(var Bounds: TRect;
AHScrollBar, AVScrollBar: PScrollBar;
AIndicator: PIndicator; AFileName: FNameStr);
if memory for the file FileName is not available (file too
big, etc.), resets the filename to ''. This prevents user from
accidently saving an empty file on top of an existing file.
if not IsValid then begin
FileName := '';
Message(Owner, evBroadcast, cmUpdateTitle, nil);
end;
end;
type
PEditWindow = ^TEditWindow;
TEditWindow = object(TWindow)
Editor: PFileEditor;
+ constructor Init(var Bounds: TRect;
FileName: FNameStr; ANumber: Integer);
+ procedure HandleEvent(var Event: TEvent); virtual;
for broadcasts of cmUpdateTitle then
begin
Frame^.DrawView;
with Editor^ do begin
if HScrollBar <> nil then HScrollBar^.DrawView;
if VScrollBar <> nil then VScrollBar^.DrawView;
if Indicator <> nil then Indicator^.DrawView;
end;
end;
ClearEvent;
* procedure SizeLimits(var Min, Max: TPoint); virtual;
Min.X := 23*Charlen-1; {was 23}
end;