diff options
Diffstat (limited to 'packages/fv/src/editors.pas')
| -rw-r--r-- | packages/fv/src/editors.pas | 3797 |
1 files changed, 3797 insertions, 0 deletions
diff --git a/packages/fv/src/editors.pas b/packages/fv/src/editors.pas new file mode 100644 index 0000000000..4503b8359c --- /dev/null +++ b/packages/fv/src/editors.pas @@ -0,0 +1,3797 @@ +unit Editors; + +{$i platform.inc} + +{$ifdef PPC_FPC} + {$H-} +{$else} + {$F+,O+,E+,N+} +{$endif} +{$X+,R-,I-,Q-,V-} +{$ifndef OS_UNIX} + {$S-} +{$endif} + + +{$define UNIXLF} + +{2.0 compatibility} +{$ifdef VER2_0} + {$macro on} + {$define resourcestring := const} +{$endif} + +interface + +uses + Objects, Drivers,Views,Dialogs,FVCommon,FVConsts; + +const + { Length constants. } + Tab_Stop_Length = 74; + +{$ifdef PPC_BP} + MaxLineLength = 1024; + MinBufLength = $1000; + MaxBufLength = $ff00; + NotFoundValue = $ffff; + LineInfoGrow = 256; + MaxLines = 16000; +{$else} + MaxLineLength = 4096; + MinBufLength = $1000; + MaxBufLength = $7fffff00; + NotFoundValue = $ffffffff; + LineInfoGrow = 1024; + MaxLines = $7ffffff; +{$endif} + + + { Editor constants for dialog boxes. } + edOutOfMemory = 0; + edReadError = 1; + edWriteError = 2; + edCreateError = 3; + edSaveModify = 4; + edSaveUntitled = 5; + edSaveAs = 6; + edFind = 7; + edSearchFailed = 8; + edReplace = 9; + edReplacePrompt = 10; + + edJumpToLine = 11; + edPasteNotPossible = 12; + edReformatDocument = 13; + edReformatNotAllowed = 14; + edReformNotPossible = 15; + edReplaceNotPossible = 16; + edRightMargin = 17; + edSetTabStops = 18; + edWrapNotPossible = 19; + + { Editor flag constants for dialog options. } + efCaseSensitive = $0001; + efWholeWordsOnly = $0002; + efPromptOnReplace = $0004; + efReplaceAll = $0008; + efDoReplace = $0010; + efBackupFiles = $0100; + + { Constants for object palettes. } + CIndicator = #2#3; + CEditor = #6#7; + CMemo = #26#27; + +type + TEditorDialog = function (Dialog : Integer; Info : Pointer) : Word; + + PIndicator = ^TIndicator; + TIndicator = object (TView) + Location : Objects.TPoint; + Modified : Boolean; + AutoIndent : Boolean; { Added boolean for AutoIndent mode. } + WordWrap : Boolean; { Added boolean for WordWrap mode. } + constructor Init (var Bounds : TRect); + procedure Draw; virtual; + function GetPalette : PPalette; virtual; + procedure SetState (AState : Word; Enable : Boolean); virtual; + procedure SetValue (ALocation : Objects.TPoint; IsAutoIndent : Boolean; + IsModified : Boolean; + IsWordWrap : Boolean); + end; + + TLineInfoRec = record + Len,Attr : Sw_word; + end; + TLineInfoArr = array[0..MaxLines] of TLineInfoRec; + PLineInfoArr = ^TLineInfoArr; + + PLineInfo = ^TLineInfo; + TLineInfo = object + Info : PLineInfoArr; + MaxPos : Sw_Word; + constructor Init; + destructor Done; + procedure Grow(pos:Sw_word); + procedure SetLen(pos,val:Sw_Word); + procedure SetAttr(pos,val:Sw_Word); + function GetLen(pos:Sw_Word):Sw_Word; + function GetAttr(pos:Sw_Word):Sw_Word; + end; + + + PEditBuffer = ^TEditBuffer; + TEditBuffer = array[0..MaxBufLength] of Char; + + PEditor = ^TEditor; + TEditor = object (TView) + HScrollBar : PScrollBar; + VScrollBar : PScrollBar; + Indicator : PIndicator; + Buffer : PEditBuffer; + BufSize : Sw_Word; + BufLen : Sw_Word; + GapLen : Sw_Word; + SelStart : Sw_Word; + SelEnd : Sw_Word; + CurPtr : Sw_Word; + CurPos : Objects.TPoint; + Delta : Objects.TPoint; + Limit : Objects.TPoint; + DrawLine : Sw_Integer; + DrawPtr : Sw_Word; + DelCount : Sw_Word; + InsCount : Sw_Word; + Flags : Longint; + IsReadOnly : Boolean; + IsValid : Boolean; + CanUndo : Boolean; + Modified : Boolean; + Selecting : Boolean; + Overwrite : Boolean; + AutoIndent : Boolean; + NoSelect : Boolean; + TabSize : Sw_Word; { tabsize for displaying } + BlankLine : Sw_Word; { First blank line after a paragraph. } + Word_Wrap : Boolean; { Added boolean to toggle wordwrap on/off. } + Line_Number : string[8]; { Holds line number to jump to. } + Right_Margin : Sw_Integer; { Added integer to set right margin. } + Tab_Settings : String[Tab_Stop_Length]; { Added string to hold tab stops. } + + constructor Init (var Bounds : TRect; AHScrollBar, AVScrollBar : PScrollBar; + AIndicator : PIndicator; ABufSize : Sw_Word); + constructor Load (var S : Objects.TStream); + destructor Done; virtual; + function BufChar (P : Sw_Word) : Char; + function BufPtr (P : Sw_Word) : Sw_Word; + procedure ChangeBounds (var Bounds : TRect); virtual; + procedure ConvertEvent (var Event : Drivers.TEvent); virtual; + function CursorVisible : Boolean; + procedure DeleteSelect; + procedure DoneBuffer; virtual; + procedure Draw; virtual; + procedure FormatLine (var DrawBuf; LinePtr : Sw_Word; Width : Sw_Integer; Colors : Word);virtual; + function GetPalette : PPalette; virtual; + procedure HandleEvent (var Event : Drivers.TEvent); virtual; + procedure InitBuffer; virtual; + function InsertBuffer (var P : PEditBuffer; Offset, Length : Sw_Word;AllowUndo, SelectText : Boolean) : Boolean; + function InsertFrom (Editor : PEditor) : Boolean; virtual; + function InsertText (Text : Pointer; Length : Sw_Word; SelectText : Boolean) : Boolean; + procedure ScrollTo (X, Y : Sw_Integer); + function Search (const FindStr : String; Opts : Word) : Boolean; + function SetBufSize (NewSize : Sw_Word) : Boolean; virtual; + procedure SetCmdState (Command : Word; Enable : Boolean); + procedure SetSelect (NewStart, NewEnd : Sw_Word; CurStart : Boolean); + procedure SetCurPtr (P : Sw_Word; SelectMode : Byte); + procedure SetState (AState : Word; Enable : Boolean); virtual; + procedure Store (var S : Objects.TStream); + procedure TrackCursor (Center : Boolean); + procedure Undo; + procedure UpdateCommands; virtual; + function Valid (Command : Word) : Boolean; virtual; + + private + KeyState : Integer; + LockCount : Byte; + UpdateFlags : Byte; + Place_Marker : Array [1..10] of Sw_Word; { Inserted array to hold place markers. } + Search_Replace : Boolean; { Added boolean to test for Search and Replace insertions. } + + procedure Center_Text (Select_Mode : Byte); + function CharPos (P, Target : Sw_Word) : Sw_Integer; + function CharPtr (P : Sw_Word; Target : Sw_Integer) : Sw_Word; + procedure Check_For_Word_Wrap (Select_Mode : Byte; Center_Cursor : Boolean); + function ClipCopy : Boolean; + procedure ClipCut; + procedure ClipPaste; + procedure DeleteRange (StartPtr, EndPtr : Sw_Word; DelSelect : Boolean); + procedure DoSearchReplace; + procedure DoUpdate; + function Do_Word_Wrap (Select_Mode : Byte; Center_Cursor : Boolean) : Boolean; + procedure DrawLines (Y, Count : Sw_Integer; LinePtr : Sw_Word); + procedure Find; + function GetMousePtr (Mouse : Objects.TPoint) : Sw_Word; + function HasSelection : Boolean; + procedure HideSelect; + procedure Insert_Line (Select_Mode : Byte); + function IsClipboard : Boolean; + procedure Jump_Place_Marker (Element : Byte; Select_Mode : Byte); + procedure Jump_To_Line (Select_Mode : Byte); + function LineEnd (P : Sw_Word) : Sw_Word; + function LineMove (P : Sw_Word; Count : Sw_Integer) : Sw_Word; + function LineStart (P : Sw_Word) : Sw_Word; + function LineNr (P : Sw_Word) : Sw_Word; + procedure Lock; + function NewLine (Select_Mode : Byte) : Boolean; + function NextChar (P : Sw_Word) : Sw_Word; + function NextLine (P : Sw_Word) : Sw_Word; + function NextWord (P : Sw_Word) : Sw_Word; + function PrevChar (P : Sw_Word) : Sw_Word; + function PrevLine (P : Sw_Word) : Sw_Word; + function PrevWord (P : Sw_Word) : Sw_Word; + procedure Reformat_Document (Select_Mode : Byte; Center_Cursor : Boolean); + function Reformat_Paragraph (Select_Mode : Byte; Center_Cursor : Boolean) : Boolean; + procedure Remove_EOL_Spaces (Select_Mode : Byte); + procedure Replace; + procedure Scroll_Down; + procedure Scroll_Up; + procedure Select_Word; + procedure SetBufLen (Length : Sw_Word); + procedure Set_Place_Marker (Element : Byte); + procedure Set_Right_Margin; + procedure Set_Tabs; + procedure StartSelect; + procedure Tab_Key (Select_Mode : Byte); + procedure ToggleInsMode; + procedure Unlock; + procedure Update (AFlags : Byte); + procedure Update_Place_Markers (AddCount : Word; KillCount : Word; StartPtr,EndPtr : Sw_Word); + end; + + TMemoData = record + Length : Sw_Word; + Buffer : TEditBuffer; + end; + + PMemo = ^TMemo; + TMemo = object (TEditor) + constructor Load (var S : Objects.TStream); + function DataSize : Sw_Word; virtual; + procedure GetData (var Rec); virtual; + function GetPalette : PPalette; virtual; + procedure HandleEvent (var Event : Drivers.TEvent); virtual; + procedure SetData (var Rec); virtual; + procedure Store (var S : Objects.TStream); + end; + + PFileEditor = ^TFileEditor; + TFileEditor = object (TEditor) + FileName : FNameStr; + constructor Init (var Bounds : TRect; AHScrollBar, AVScrollBar : PScrollBar; + AIndicator : PIndicator; AFileName : FNameStr); + constructor Load (var S : Objects.TStream); + procedure DoneBuffer; virtual; + procedure HandleEvent (var Event : Drivers.TEvent); virtual; + procedure InitBuffer; virtual; + function LoadFile : Boolean; + function Save : Boolean; + function SaveAs : Boolean; + function SaveFile : Boolean; + function SetBufSize (NewSize : Sw_Word) : Boolean; virtual; + procedure Store (var S : Objects.TStream); + procedure UpdateCommands; virtual; + function Valid (Command : Word) : Boolean; virtual; + end; + + PEditWindow = ^TEditWindow; + TEditWindow = object (TWindow) + Editor : PFileEditor; + constructor Init (var Bounds : TRect; FileName : FNameStr; ANumber : Integer); + constructor Load (var S : Objects.TStream); + procedure Close; virtual; + function GetTitle (MaxSize : Sw_Integer) : TTitleStr; virtual; + procedure HandleEvent (var Event : Drivers.TEvent); virtual; + procedure SizeLimits(var Min, Max: TPoint); virtual; + procedure Store (var S : Objects.TStream); + end; + + +function DefEditorDialog (Dialog : Integer; Info : Pointer) : Word; +function CreateFindDialog: PDialog; +function CreateReplaceDialog: PDialog; +function JumpLineDialog : PDialog; +function ReformDocDialog : PDialog; +function RightMarginDialog : PDialog; +function TabStopDialog : Dialogs.PDialog; +function StdEditorDialog(Dialog: Integer; Info: Pointer): Word; + +const + WordChars : set of Char = ['!'..#255]; + + LineBreak : string[2]= +{$ifdef UNIXLF} + #10; +{$else} + #13#10; +{$endif} + + + { The Allow_Reformat boolean is a programmer hook. } + { I've placed this here to allow programmers to } + { determine whether or not paragraph and document } + { reformatting are allowed if Word_Wrap is not } + { active. Some people say don't allow, and others } + { say allow it. I've left it up to the programmer. } + { Set to FALSE if not allowed, or TRUE if allowed. } + Allow_Reformat : Boolean = True; + + EditorDialog : TEditorDialog = {$ifdef fpc}@{$endif}DefEditorDialog; + EditorFlags : Word = efBackupFiles + efPromptOnReplace; + FindStr : String[80] = ''; + ReplaceStr : String[80] = ''; + Clipboard : PEditor = nil; + + ToClipCmds : TCommandSet = ([cmCut,cmCopy,cmClear]); + FromClipCmds : TCommandSet = ([cmPaste]); + UndoCmds : TCommandSet = ([cmUndo,cmRedo]); + +TYPE + TFindDialogRec = +{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT} + packed +{$endif FPC_REQUIRES_PROPER_ALIGNMENT} + record + Find : String[80]; + Options : Word; + end; + + TReplaceDialogRec = +{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT} + packed +{$endif FPC_REQUIRES_PROPER_ALIGNMENT} + record + Find : String[80]; + Replace : String[80]; + Options : Word; + end; + + TRightMarginRec = +{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT} + packed +{$endif FPC_REQUIRES_PROPER_ALIGNMENT} + record + Margin_Position : String[3]; + end; + + TTabStopRec = +{$ifndef FPC_REQUIRES_PROPER_ALIGNMENT} + packed +{$endif FPC_REQUIRES_PROPER_ALIGNMENT} + record + Tab_String : String [Tab_Stop_Length]; + end; + +CONST + { VMT constants. } + REditor : TStreamRec = (ObjType : 70; + VmtLink : Ofs (TypeOf (TEditor)^); + Load : @TEditor.Load; + Store : @TEditor.Store); + + RMemo : TStreamRec = (ObjType : 71; + VmtLink : Ofs (TypeOf (TMemo)^); + Load : @TMemo.Load; + Store : @TMemo.Store); + + RFileEditor : TStreamRec = (ObjType : 72; + VmtLink : Ofs (TypeOf (TFileEditor)^); + Load : @TFileEditor.Load; + Store : @TFileEditor.Store); + + RIndicator : TStreamRec = (ObjType : 73; + VmtLink : Ofs (TypeOf (TIndicator)^); + Load : @TIndicator.Load; + Store : @TIndicator.Store); + + REditWindow : TStreamRec = (ObjType : 74; + VmtLink : Ofs (TypeOf (TEditWindow)^); + Load : @TEditWindow.Load; + Store : @TEditWindow.Store); + +procedure RegisterEditors; + + +{**************************************************************************** + Implementation +****************************************************************************} + +implementation + +uses + Dos, App, StdDlg, MsgBox{, Resource}; + +type + pword = ^word; + +resourcestring sClipboard='Clipboard'; + sFileCreateError='Error creating file %s'; + sFileReadError='Error reading file %s'; + sFileUntitled='Save untitled file?'; + sFileWriteError='Error writing to file %s'; + sFind='Find'; + sJumpTo='Jump To'; + sModified=''#3'%s'#13#10#13#3'has been modified. Save?'; + sOutOfMemory='Not enough memory for this operation.'; + sPasteNotPossible='Wordwrap on: Paste not possible in current margins when at end of line.'; + sReformatDocument='Reformat Document'; + sReformatNotPossible='Paragraph reformat not possible while trying to wrap current line with current margins.'; + sReformattingTheDocument='Reformatting the document:'; + sReplaceNotPossible='Wordwrap on: Replace not possible in current margins when at end of line.'; + sReplaceThisOccurence='Replace this occurence?'; + sRightMargin='Right Margin'; + sSearchStringNotFound='Search string not found.'; + sSelectWhereToBegin='Please select where to begin.'; + sSetting='Setting:'; + sTabSettings='Tab Settings'; + sUnknownDialog='Unknown dialog requested!'; + sUntitled='Untitled'; + sWordWrapNotPossible='Wordwrap on: Wordwrap not possible in current margins with continuous line.'; + sWordWrapOff='You must turn on wordwrap before you can reformat.'; + + slCaseSensitive='~C~ase sensitive'; + slCurrentLine='~C~urrent line'; + slEntireDocument='~E~ntire document'; + slLineNumber='~L~ine number'; + slNewText='~N~ew text'; + slPromptOnReplace='~P~rompt on replace'; + slReplace='~R~eplace'; + slReplaceAll='~R~eplace all'; + slTextToFind='~T~ext to find'; + slWholeWordsOnly='~W~hole words only'; + + +CONST + { Update flag constants. } + ufUpdate = $01; + ufLine = $02; + ufView = $04; + ufStats = $05; + + { SelectMode constants. } + smExtend = $01; + smDouble = $02; + + sfSearchFailed = NotFoundValue; + + { Arrays that hold all the command keys and options. } + FirstKeys : array[0..46 * 2] of Word = (46, Ord (^A), cmWordLeft, + Ord (^B), cmReformPara, + Ord (^C), cmPageDown, + Ord (^D), cmCharRight, + Ord (^E), cmLineUp, + Ord (^F), cmWordRight, + Ord (^G), cmDelChar, + Ord (^H), cmBackSpace, + Ord (^I), cmTabKey, + Ord (^J), $FF04, + Ord (^K), $FF02, + Ord (^L), cmSearchAgain, + Ord (^M), cmNewLine, + Ord (^N), cmInsertLine, + Ord (^O), $FF03, + Ord (^Q), $FF01, + Ord (^R), cmPageUp, + Ord (^S), cmCharLeft, + Ord (^T), cmDelWord, + Ord (^U), cmUndo, + Ord (^V), cmInsMode, + Ord (^W), cmScrollUp, + Ord (^X), cmLineDown, + Ord (^Y), cmDelLine, + Ord (^Z), cmScrollDown, + kbLeft, cmCharLeft, + kbRight, cmCharRight, + kbCtrlLeft, cmWordLeft, + kbCtrlRight, cmWordRight, + kbHome, cmLineStart, + kbEnd, cmLineEnd, + kbCtrlHome, cmHomePage, + kbCtrlEnd, cmEndPage, + kbUp, cmLineUp, + kbDown, cmLineDown, + kbPgUp, cmPageUp, + kbPgDn, cmPageDown, + kbCtrlPgUp, cmTextStart, + kbCtrlPgDn, cmTextEnd, + kbIns, cmInsMode, + kbDel, cmDelChar, + kbCtrlBack, cmDelStart, + kbShiftIns, cmPaste, + kbShiftDel, cmCut, + kbCtrlIns, cmCopy, + kbCtrlDel, cmClear); + + { SCRLUP - Stop. } { Added ^W to scroll screen up. } + { SCRLDN - Stop. } { Added ^Z to scroll screen down. } + { REFORM - Stop. } { Added ^B for paragraph reformatting. } + { PRETAB - Stop. } { Added ^I for preset tabbing. } + { JLINE - Stop. } { Added ^J to jump to a line number. } + { INSLIN - Stop. } { Added ^N to insert line at cursor. } + { INDENT - Stop. } { Removed ^O and put it into ^QI. } + { HOMEND - Stop. } { Added kbCtrlHome and kbCtrlEnd pages. } + { CTRLBK - Stop. } { Added kbCtrlBack same as ^QH. } + + QuickKeys : array[0..21 * 2] of Word = (21, Ord ('0'), cmJumpMark0, + Ord ('1'), cmJumpMark1, + Ord ('2'), cmJumpMark2, + Ord ('3'), cmJumpMark3, + Ord ('4'), cmJumpMark4, + Ord ('5'), cmJumpMark5, + Ord ('6'), cmJumpMark6, + Ord ('7'), cmJumpMark7, + Ord ('8'), cmJumpMark8, + Ord ('9'), cmJumpMark9, + Ord ('A'), cmReplace, + Ord ('C'), cmTextEnd, + Ord ('D'), cmLineEnd, + Ord ('F'), cmFind, + Ord ('H'), cmDelStart, + Ord ('I'), cmIndentMode, + Ord ('L'), cmUndo, + Ord ('R'), cmTextStart, + Ord ('S'), cmLineStart, + Ord ('U'), cmReformDoc, + Ord ('Y'), cmDelEnd); + + { UNDO - Stop. } { Added IDE undo feature of ^QL. } + { REFDOC - Stop. } { Added document reformat feature if ^QU pressed. } + { MARK - Stop. } { Added cmJumpMark# to allow place marking. } + { INDENT - Stop. } { Moved IndentMode here from Firstkeys. } + + BlockKeys : array[0..20 * 2] of Word = (20, Ord ('0'), cmSetMark0, + Ord ('1'), cmSetMark1, + Ord ('2'), cmSetMark2, + Ord ('3'), cmSetMark3, + Ord ('4'), cmSetMark4, + Ord ('5'), cmSetMark5, + Ord ('6'), cmSetMark6, + Ord ('7'), cmSetMark7, + Ord ('8'), cmSetMark8, + Ord ('9'), cmSetMark9, + Ord ('B'), cmStartSelect, + Ord ('C'), cmPaste, + Ord ('D'), cmSave, + Ord ('F'), cmSaveAs, + Ord ('H'), cmHideSelect, + Ord ('K'), cmCopy, + Ord ('S'), cmSave, + Ord ('T'), cmSelectWord, + Ord ('Y'), cmCut, + Ord ('X'), cmSaveDone); + + { SELWRD - Stop. } { Added ^KT to select word only. } + { SAVE - Stop. } { Added ^KD, ^KF, ^KS, ^KX key commands. } + { MARK - Stop. } { Added cmSetMark# to allow place marking. } + + FormatKeys : array[0..5 * 2] of Word = (5, Ord ('C'), cmCenterText, + Ord ('T'), cmCenterText, + Ord ('I'), cmSetTabs, + Ord ('R'), cmRightMargin, + Ord ('W'), cmWordWrap); + + { WRAP - Stop. } { Added Wordwrap feature if ^OW pressed. } + { RMSET - Stop. } { Added set right margin feature if ^OR pressed. } + { PRETAB - Stop. } { Added preset tab feature if ^OI pressed. } + { CENTER - Stop. } { Added center text option ^OC for a line. } + + JumpKeys : array[0..1 * 2] of Word = (1, Ord ('L'), cmJumpLine); + + { JLINE - Stop. } { Added jump to line number feature if ^JL pressed. } + + KeyMap : array[0..4] of Pointer = (@FirstKeys, + @QuickKeys, + @BlockKeys, + @FormatKeys, + @JumpKeys); + + { WRAP - Stop. } { Added @FormatKeys for new ^O? keys. } + { PRETAB - Stop. } { Added @FormatKeys for new ^O? keys. } + { JLINE - Stop. } { Added @JumpKeys for new ^J? keys. } + { CENTER - Stop. } { Added @FormatKeys for new ^O? keys. } + + +{**************************************************************************** + Dialogs +****************************************************************************} + +function DefEditorDialog (Dialog : Integer; Info : Pointer) : Word; +begin + DefEditorDialog := cmCancel; +end; { DefEditorDialog } + + +function CreateFindDialog: PDialog; +var + D: PDialog; + Control: PView; + R: TRect; +begin + R.Assign(0, 0, 38, 12); + D := New(PDialog, Init(R,sFind)); + with D^ do + begin + Options := Options or ofCentered; + + R.Assign(3, 3, 32, 4); + Control := New(PInputLine, Init(R, 80)); + Control^.HelpCtx := hcDFindText; + Insert(Control); + R.Assign(2, 2, 15, 3); + Insert(New(PLabel, Init(R, slTextToFind, Control))); + R.Assign(32, 3, 35, 4); + Insert(New(PHistory, Init(R, PInputLine(Control), 10))); + + R.Assign(3, 5, 35, 7); + Control := New(PCheckBoxes, Init(R, + NewSItem (slCaseSensitive, + NewSItem (slWholeWordsOnly,nil)))); + Control^.HelpCtx := hcCCaseSensitive; + Insert(Control); + + R.Assign(14, 9, 24, 11); + Control := New (PButton, Init(R,slOK,cmOk,bfDefault)); + Control^.HelpCtx := hcDOk; + Insert (Control); + + Inc(R.A.X, 12); Inc(R.B.X, 12); + Control := New (PButton, Init(R,slCancel,cmCancel, bfNormal)); + Control^.HelpCtx := hcDCancel; + Insert (Control); + + SelectNext(False); + end; + CreateFindDialog := D; +end; + + +function CreateReplaceDialog: PDialog; +var + D: PDialog; + Control: PView; + R: TRect; +begin + R.Assign(0, 0, 40, 16); + D := New(PDialog, Init(R,slReplace)); + with D^ do + begin + Options := Options or ofCentered; + + R.Assign(3, 3, 34, 4); + Control := New(PInputLine, Init(R, 80)); + Control^.HelpCtx := hcDFindText; + Insert(Control); + R.Assign(2, 2, 15, 3); + Insert(New(PLabel, Init(R,slTextToFind, Control))); + R.Assign(34, 3, 37, 4); + Insert(New(PHistory, Init(R, PInputLine(Control), 10))); + + R.Assign(3, 6, 34, 7); + Control := New(PInputLine, Init(R, 80)); + Control^.HelpCtx := hcDReplaceText; + Insert(Control); + R.Assign(2, 5, 12, 6); + Insert(New(PLabel, Init(R,slNewText, Control))); + R.Assign(34, 6, 37, 7); + Insert(New(PHistory, Init(R, PInputLine(Control), 11))); + + R.Assign(3, 8, 37, 12); + Control := New (Dialogs.PCheckBoxes, Init (R, + NewSItem (slCasesensitive, + NewSItem (slWholewordsonly, + NewSItem (slPromptonreplace, + NewSItem (slReplaceall, nil)))))); + Control^.HelpCtx := hcCCaseSensitive; + Insert (Control); + + R.Assign (8, 13, 18, 15); + Control := New (PButton, Init (R,slOK, cmOk, bfDefault)); + Control^.HelpCtx := hcDOk; + Insert (Control); + + R.Assign (22, 13, 32, 15); + Control := New (PButton, Init (R,slCancel, cmCancel, bfNormal)); + Control^.HelpCtx := hcDCancel; + Insert (Control); + + SelectNext(False); + end; + CreateReplaceDialog := D; +end; + + +function JumpLineDialog : PDialog; +VAR + D : PDialog; + R : TRect; + Control: PView; +Begin + R.Assign (0, 0, 26, 8); + D := New(PDialog, Init(R,sJumpTo)); + with D^ do + begin + Options := Options or ofCentered; + + R.Assign (3, 2, 15, 3); + Control := New (Dialogs.PStaticText, Init (R,slLineNumber)); + Insert (Control); + + R.Assign (15, 2, 21, 3); + Control := New (Dialogs.PInputLine, Init (R, 4)); + Control^.HelpCtx := hcDLineNumber; + Insert (Control); + + R.Assign (21, 2, 24, 3); + Insert (New (Dialogs.PHistory, Init (R, Dialogs.PInputLine (Control), 12))); + + R.Assign (2, 5, 12, 7); + Control := New (Dialogs.PButton, Init (R, slOK, cmOK, Dialogs.bfDefault)); + Control^.HelpCtx := hcDOk; + Insert (Control); + + R.Assign (14, 5, 24, 7); + Control := New (Dialogs.PButton, Init (R, slCancel, cmCancel, Dialogs.bfNormal)); + Control^.HelpCtx := hcDCancel; + Insert (Control); + + SelectNext (False); + end; + JumpLineDialog := D; +end; { JumpLineDialog } + + +function ReformDocDialog : Dialogs.PDialog; + { This is a local function that brings up a dialog box } + { that asks where to start reformatting the document. } +VAR + R : TRect; + D : Dialogs.PDialog; + Control : PView; +Begin + R.Assign (0, 0, 32, 11); + D := New (Dialogs.PDialog, Init (R, sReformatDocument)); + with D^ do + begin + Options := Options or ofCentered; + + R.Assign (2, 2, 30, 3); + Control := New (Dialogs.PStaticText, Init (R, sSelectWhereToBegin)); + Insert (Control); + + R.Assign (3, 3, 29, 4); + Control := New (Dialogs.PStaticText, Init (R, sReformattingTheDocument)); + Insert (Control); + + R.Assign (50, 5, 68, 6); + Control := New (Dialogs.PLabel, Init (R, sReformatDocument, Control)); + Insert (Control); + + R.Assign (5, 5, 26, 7); + Control := New (Dialogs.PRadioButtons, Init (R, + NewSItem (slCurrentLine, + NewSItem (slEntireDocument, Nil)))); + Control^.HelpCtx := hcDReformDoc; + Insert (Control); + + R.Assign (4, 8, 14, 10); + Control := New (Dialogs.PButton, Init (R, slOK, cmOK, Dialogs.bfDefault)); + Control^.HelpCtx := hcDOk; + Insert (Control); + + R.Assign (17, 8, 27, 10); + Control := New (Dialogs.PButton, Init (R, slCancel, cmCancel, Dialogs.bfNormal)); + Control^.HelpCtx := hcDCancel; + Insert (Control); + + SelectNext (False); + end; + ReformDocDialog := D; +end; { ReformDocDialog } + + +function RightMarginDialog : Dialogs.PDialog; + { This is a local function that brings up a dialog box } + { that allows the user to change the Right_Margin. } +VAR + R : TRect; + D : PDialog; + Control : PView; +Begin + R.Assign (0, 0, 26, 8); + D := New (Dialogs.PDialog, Init (R, sRightMargin)); + with D^ do + begin + Options := Options or ofCentered; + + R.Assign (5, 2, 13, 3); + Control := New (Dialogs.PStaticText, Init (R, sSetting)); + Insert (Control); + + R.Assign (13, 2, 18, 3); + Control := New (Dialogs.PInputLine, Init (R, 3)); + Control^.HelpCtx := hcDRightMargin; + Insert (Control); + + R.Assign (18, 2, 21, 3); + Insert (New (Dialogs.PHistory, Init (R, Dialogs.PInputLine (Control), 13))); + + R.Assign (2, 5, 12, 7); + Control := New (Dialogs.PButton, Init (R, slOK, cmOK, Dialogs.bfDefault)); + Control^.HelpCtx := hcDOk; + Insert (Control); + + R.Assign (14, 5, 24, 7); + Control := New (Dialogs.PButton, Init (R, slCancel, cmCancel, Dialogs.bfNormal)); + Control^.HelpCtx := hcDCancel; + Insert (Control); + + SelectNext (False); + end; + RightMarginDialog := D; +end; { RightMarginDialog; } + + +function TabStopDialog : Dialogs.PDialog; + { This is a local function that brings up a dialog box } + { that allows the user to set their own tab stops. } +VAR + Index : Sw_Integer; { Local Indexing variable. } + R : TRect; + D : PDialog; + Control : PView; + Tab_Stop : String[2]; { Local string to print tab column number. } +Begin + R.Assign (0, 0, 80, 8); + D := New (Dialogs.PDialog, Init (R, sTabSettings)); + with D^ do + begin + Options := Options or ofCentered; + + R.Assign (2, 2, 77, 3); + Control := New (Dialogs.PStaticText, Init (R, + ' ....|....|....|....|....|....|....|....|....|....|....|....|....|....|....')); + Insert (Control); + + for Index := 1 to 7 do + begin + R.Assign (Index * 10 + 1, 1, Index * 10 + 3, 2); + Str (Index * 10, Tab_Stop); + Control := New (Dialogs.PStaticText, Init (R, Tab_Stop)); + Insert (Control); + end; + + R.Assign (2, 3, 78, 4); + Control := New (Dialogs.PInputLine, Init (R, 74)); + Control^.HelpCtx := hcDTabStops; + Insert (Control); + + R.Assign (38, 5, 41, 6); + Insert (New (Dialogs.PHistory, Init (R, Dialogs.PInputLine (Control), 14))); + + R.Assign (27, 5, 37, 7); + Control := New (Dialogs.PButton, Init (R, slOK, cmOK, Dialogs.bfDefault)); + Control^.HelpCtx := hcDOk; + Insert (Control); + + R.Assign (42, 5, 52, 7); + Control := New (Dialogs.PButton, Init (R, slCancel, cmCancel, Dialogs.bfNormal)); + Control^.HelpCtx := hcDCancel; + Insert (Control); + SelectNext (False); + end; + TabStopDialog := D; +end { TabStopDialog }; + + +function StdEditorDialog(Dialog: Integer; Info: Pointer): Word; +var + R: TRect; + T: TPoint; +begin + case Dialog of + edOutOfMemory: + StdEditorDialog := MessageBox(sOutOfMemory, nil, mfError + mfOkButton); + edReadError: + StdEditorDialog := MessageBox(sFileReadError, @Info, mfError + mfOkButton); + edWriteError: + StdEditorDialog := MessageBox(sFileWriteError, @Info, mfError + mfOkButton); + edCreateError: + StdEditorDialog := MessageBox(sFileCreateError, @Info, mfError + mfOkButton); + edSaveModify: + StdEditorDialog := MessageBox(sModified, @Info, mfInformation + mfYesNoCancel); + edSaveUntitled: + StdEditorDialog := MessageBox(sFileUntitled, nil, mfInformation + mfYesNoCancel); + edSaveAs: + StdEditorDialog := Application^.ExecuteDialog(New(PFileDialog, Init('*.*', + slSaveFileAs, slName, fdOkButton, 101)), Info); + edFind: + StdEditorDialog := Application^.ExecuteDialog(CreateFindDialog, Info); + edSearchFailed: + StdEditorDialog := MessageBox(sSearchStringNotFound, nil, mfError + mfOkButton); + edReplace: + StdEditorDialog := Application^.ExecuteDialog(CreateReplaceDialog, Info); + edReplacePrompt: + begin + { Avoid placing the dialog on the same line as the cursor } + R.Assign(0, 1, 40, 8); + R.Move((Desktop^.Size.X - R.B.X) div 2, 0); + Desktop^.MakeGlobal(R.B, T); + Inc(T.Y); + if PPoint(Info)^.Y <= T.Y then + R.Move(0, Desktop^.Size.Y - R.B.Y - 2); + StdEditorDialog := MessageBoxRect(R, sReplaceThisOccurence, + nil, mfYesNoCancel + mfInformation); + end; + edJumpToLine: + StdEditorDialog := Application^.ExecuteDialog(JumpLineDialog, Info); + edSetTabStops: + StdEditorDialog := Application^.ExecuteDialog(TabStopDialog, Info); + edPasteNotPossible: + StdEditorDialog := MessageBox (sPasteNotPossible, nil, mfError + mfOkButton); + edReformatDocument: + StdEditorDialog := Application^.ExecuteDialog(ReformDocDialog, Info); + edReformatNotAllowed: + StdEditorDialog := MessageBox (sWordWrapOff, nil, mfError + mfOkButton); + edReformNotPossible: + StdEditorDialog := MessageBox (sReformatNotPossible, nil, mfError + mfOkButton); + edReplaceNotPossible: + StdEditorDialog := MessageBox (sReplaceNotPossible, nil, mfError + mfOkButton); + edRightMargin: + StdEditorDialog := Application^.ExecuteDialog(RightMarginDialog, Info); + edWrapNotPossible: + StdEditorDialog := MessageBox (sWordWrapNotPossible, nil, mfError + mfOKButton); + else + StdEditorDialog := MessageBox (sUnknownDialog, nil, mfError + mfOkButton); + end; +end; + + +{**************************************************************************** + Helpers +****************************************************************************} + +function CountLines(var Buf; Count: sw_Word): sw_Integer; +var + p : pchar; + lines : sw_word; +begin + p:=pchar(@buf); + lines:=0; + while (count>0) do + begin + if p^ in [#10,#13] then + begin + inc(lines); + if ord((p+1)^)+ord(p^)=23 then + begin + inc(p); + dec(count); + if count=0 then + break; + end; + end; + inc(p); + dec(count); + end; + CountLines:=Lines; +end; + + +procedure GetLimits(var Buf; Count: sw_Word;var lim:objects.TPoint); +{ Get the limits needed for Buf, its an extended version of countlines (lim.y), + which also gets the maximum line length in lim.x } +var + p : pchar; + len : sw_word; +begin + lim.x:=0; + lim.y:=0; + len:=0; + p:=pchar(@buf); + while (count>0) do + begin + if p^ in [#10,#13] then + begin + if len>lim.x then + lim.x:=len; + inc(lim.y); + if ord((p+1)^)+ord(p^)=23 then + begin + inc(p); + dec(count); + end; + len:=0; + end + else + inc(len); + inc(p); + dec(count); + end; +end; + + +function ScanKeyMap(KeyMap: Pointer; KeyCode: Word): Word; +var + p : pword; + count : sw_word; +begin + p:=keymap; + count:=p^; + inc(p); + while (count>0) do + begin + if (lo(p^)=lo(keycode)) and + ((hi(p^)=0) or (hi(p^)=hi(keycode))) then + begin + inc(p); + scankeymap:=p^; + exit; + end; + inc(p,2); + dec(count); + end; + scankeymap:=0; +end; + + +Type + Btable = Array[0..255] of Byte; +Procedure BMMakeTable(const s:string; Var t : Btable); +{ Makes a Boyer-Moore search table. s = the search String t = the table } +Var + x : sw_integer; +begin + FillChar(t,sizeof(t),length(s)); + For x := length(s) downto 1 do + if (t[ord(s[x])] = length(s)) then + t[ord(s[x])] := length(s) - x; +end; + + +function Scan(var Block; Size: Sw_Word;const Str: String): Sw_Word; +Var + buffer : Array[0..MaxBufLength-1] of Byte Absolute block; + s2 : String; + len, + numb : Sw_Word; + found : Boolean; + bt : Btable; +begin + BMMakeTable(str,bt); + len:=length(str); + s2[0]:=chr(len); { sets the length to that of the search String } + found:=False; + numb:=pred(len); + While (not found) and (numb<(size-len)) do + begin + { partial match } + if buffer[numb] = ord(str[len]) then + begin + { less partial! } + if buffer[numb-pred(len)] = ord(str[1]) then + begin + move(buffer[numb-pred(len)],s2[1],len); + if (str=s2) then + begin + found:=true; + break; + end; + end; + inc(numb); + end + else + inc(numb,Bt[buffer[numb]]); + end; + if not found then + Scan := NotFoundValue + else + Scan := numb - pred(len); +end; + + +function IScan(var Block; Size: Sw_Word;const Str: String): Sw_Word; +Var + buffer : Array[0..MaxBufLength-1] of Char Absolute block; + s : String; + len, + numb, + x : Sw_Word; + found : Boolean; + bt : Btable; + p : pchar; + c : char; +begin + len:=length(str); + if (len=0) or (len>size) then + begin + IScan := NotFoundValue; + exit; + end; + { create uppercased string } + s[0]:=chr(len); + for x:=1 to len do + begin + if str[x] in ['a'..'z'] then + s[x]:=chr(ord(str[x])-32) + else + s[x]:=str[x]; + end; + BMMakeTable(s,bt); + found:=False; + numb:=pred(len); + While (not found) and (numb<(size-len)) do + begin + { partial match } + c:=buffer[numb]; + if c in ['a'..'z'] then + c:=chr(ord(c)-32); + if (c=s[len]) then + begin + { less partial! } + p:=@buffer[numb-pred(len)]; + x:=1; + while (x<=len) do + begin + if not(((p^ in ['a'..'z']) and (chr(ord(p^)-32)=s[x])) or + (p^=s[x])) then + break; + inc(p); + inc(x); + end; + if (x>len) then + begin + found:=true; + break; + end; + inc(numb); + end + else + inc(numb,Bt[ord(c)]); + end; + if not found then + IScan := NotFoundValue + else + IScan := numb - pred(len); +end; + + +{**************************************************************************** + TIndicator +****************************************************************************} + +constructor TIndicator.Init (var Bounds : TRect); +begin + Inherited Init (Bounds); + GrowMode := gfGrowLoY + gfGrowHiY; +end; { TIndicator.Init } + + +procedure TIndicator.Draw; +VAR + Color : Byte; + Frame : Char; + L : array[0..1] of Longint; + S : String[15]; + B : TDrawBuffer; +begin + if State and sfDragging = 0 then + begin + Color := GetColor (1); + Frame := #205; + end + else + begin + Color := GetColor (2); + Frame := #196; + end; + MoveChar (B, Frame, Color, Size.X); + { If the text has been modified, put an 'M' in the TIndicator display. } + if Modified then + WordRec (B[1]).Lo := 77; + { If WordWrap is active put a 'W' in the TIndicator display. } + if WordWrap then + WordRec (B[2]).Lo := 87 + else + WordRec (B[2]).Lo := Byte (Frame); + { If AutoIndent is active put an 'I' in TIndicator display. } + if AutoIndent then + WordRec (B[0]).Lo := 73 + else + WordRec (B[0]).Lo := Byte (Frame); + L[0] := Location.Y + 1; + L[1] := Location.X + 1; + FormatStr (S, ' %d:%d ', L); + MoveStr (B[9 - Pos (':', S)], S, Color); { Changed original 8 to 9. } + WriteBuf (0, 0, Size.X, 1, B); +end; { TIndicator.Draw } + + +function TIndicator.GetPalette : PPalette; +const + P : string[Length (CIndicator)] = CIndicator; +begin + GetPalette := PPalette(@P); +end; { TIndicator.GetPalette } + + +procedure TIndicator.SetState (AState : Word; Enable : Boolean); +begin + Inherited SetState (AState, Enable); + if AState = sfDragging then + DrawView; +end; { TIndicator.SetState } + + +procedure TIndicator.SetValue (ALocation : Objects.TPoint; IsAutoIndent : Boolean; + IsModified : Boolean; + IsWordWrap : Boolean); +begin + if (Location.X<>ALocation.X) or + (Location.Y<>ALocation.Y) or + (AutoIndent <> IsAutoIndent) or + (Modified <> IsModified) or + (WordWrap <> IsWordWrap) then + begin + Location := ALocation; + AutoIndent := IsAutoIndent; { Added provisions to show AutoIndent. } + Modified := IsModified; + WordWrap := IsWordWrap; { Added provisions to show WordWrap. } + DrawView; + end; +end; { TIndicator.SetValue } + + +{**************************************************************************** + TLineInfo +****************************************************************************} + +constructor TLineInfo.Init; +begin + MaxPos:=0; + Grow(1); +end; + + +destructor TLineInfo.Done; +begin + FreeMem(Info,MaxPos*sizeof(TLineInfoRec)); + Info := nil; +end; + + +procedure TLineInfo.Grow(pos:Sw_word); +var + NewSize : Sw_word; + P : pointer; +begin + NewSize:=(Pos+LineInfoGrow-(Pos mod LineInfoGrow)); + GetMem(P,NewSize*sizeof(TLineInfoRec)); + FillChar(P^,NewSize*sizeof(TLineInfoRec),0); + Move(Info^,P^,MaxPos*sizeof(TLineInfoRec)); + Freemem(Info,MaxPos*sizeof(TLineInfoRec)); + Info:=P; +end; + + +procedure TLineInfo.SetLen(pos,val:Sw_Word); +begin + if pos>=MaxPos then + Grow(Pos); + Info^[Pos].Len:=val +end; + + +procedure TLineInfo.SetAttr(pos,val:Sw_Word); +begin + if pos>=MaxPos then + Grow(Pos); + Info^[Pos].Attr:=val +end; + + +function TLineInfo.GetLen(pos:Sw_Word):Sw_Word; +begin + GetLen:=Info^[Pos].Len; +end; + + +function TLineInfo.GetAttr(pos:Sw_Word):Sw_Word; +begin + GetAttr:=Info^[Pos].Attr; +end; + + + +{**************************************************************************** + TEditor +****************************************************************************} + +constructor TEditor.Init (var Bounds : TRect; + AHScrollBar, AVScrollBar : PScrollBar; + AIndicator : PIndicator; ABufSize : Sw_Word); +var + Element : Byte; { Place_Marker array element to initialize array with. } +begin + Inherited Init (Bounds); + GrowMode := gfGrowHiX + gfGrowHiY; + Options := Options or ofSelectable; + Flags := EditorFlags; + EventMask := evMouseDown + evKeyDown + evCommand + evBroadcast; + ShowCursor; + + HScrollBar := AHScrollBar; + VScrollBar := AVScrollBar; + + Indicator := AIndicator; + BufSize := ABufSize; + CanUndo := True; + InitBuffer; + + if assigned(Buffer) then + IsValid := True + else + begin + EditorDialog (edOutOfMemory, nil); + BufSize := 0; + end; + + SetBufLen (0); + + for Element := 1 to 10 do + Place_Marker[Element] := 0; + + Element := 1; + while Element <= 70 do + begin + if Element mod 5 = 0 then + Insert ('x', Tab_Settings, Element) + else + Insert (#32, Tab_Settings, Element); + Inc (Element); + end; + { Default Right_Margin value. Change it if you want another. } + Right_Margin := 76; + TabSize:=8; +end; { TEditor.Init } + + +constructor TEditor.Load (var S : Objects.TStream); +begin + Inherited Load (S); + GetPeerViewPtr (S, HScrollBar); + GetPeerViewPtr (S, VScrollBar); + GetPeerViewPtr (S, Indicator); + S.Read (BufSize, SizeOf (BufSize)); + S.Read (CanUndo, SizeOf (CanUndo)); + S.Read (AutoIndent, SizeOf (AutoIndent)); + S.Read (Line_Number, SizeOf (Line_Number)); + S.Read (Place_Marker, SizeOf (Place_Marker)); + S.Read (Right_Margin, SizeOf (Right_Margin)); + S.Read (Tab_Settings, SizeOf (Tab_Settings)); + S.Read (Word_Wrap, SizeOf (Word_Wrap)); + InitBuffer; + if Assigned(Buffer) then + IsValid := True + else + begin + EditorDialog (edOutOfMemory, nil); + BufSize := 0; + end; + Lock; + SetBufLen (0); +end; { TEditor.Load } + + +destructor TEditor.Done; +begin + DoneBuffer; + Inherited Done; +end; { TEditor.Done } + + +function TEditor.BufChar(P: Sw_Word): Char; +begin + if P>=CurPtr then + inc(P,Gaplen); + BufChar:=Buffer^[P]; +end; + + +function TEditor.BufPtr(P: Sw_Word): Sw_Word; +begin + if P>=CurPtr then + BufPtr:=P+GapLen + else + BufPtr:=P; +end; + + +procedure TEditor.Center_Text (Select_Mode : Byte); +{ This procedure will center the current line of text. } +{ Centering is based on the current Right_Margin. } +{ If the Line_Length exceeds the Right_Margin, or the } +{ line is just a blank line, we exit and do nothing. } +VAR + Spaces : array [1..80] of Char; { Array to hold spaces we'll insert. } + Index : Byte; { Index into Spaces array. } + Line_Length : Sw_Integer; { Holds the length of the line. } + E,S : Sw_Word; { End of the current line. } +begin + E := LineEnd (CurPtr); + S := LineStart (CurPtr); + { If the line is blank (only a CR/LF on it) then do noting. } + if E = S then + Exit; + { Set CurPtr to start of line. Check if line begins with a space. } + { We must strip out any spaces from the beginning, or end of lines. } + { If line does not start with space, make sure line length does not } + { exceed the Right_Margin. If it does, then do nothing. } + SetCurPtr (S, Select_Mode); + Remove_EOL_Spaces (Select_Mode); + if Buffer^[CurPtr] = #32 then + begin + { If the next word is greater than the end of line then do nothing. } + { If the line length is greater than Right_Margin then do nothing. } + { Otherwise, delete all spaces at the start of line. } + { Then reset end of line and put CurPtr at start of modified line. } + E := LineEnd (CurPtr); + if NextWord (CurPtr) > E then + Exit; + if E - NextWord (CurPtr) > Right_Margin then + Exit; + DeleteRange (CurPtr, NextWord (CurPtr), True); + E := LineEnd (CurPtr); + SetCurPtr (LineStart (CurPtr), Select_Mode); + end + else + if E - CurPtr > Right_Margin then + Exit; + { Now we determine the real length of the line. } + { Then we subtract the Line_Length from Right_Margin. } + { Dividing the result by two tells us how many spaces } + { must be inserted at start of line to center it. } + { When we're all done, set the CurPtr to end of line. } + Line_Length := E - CurPtr; + for Index := 1 to ((Right_Margin - Line_Length) shr 1) do + Spaces[Index] := #32; + InsertText (@Spaces, Index, False); + SetCurPtr (LineEnd (CurPtr), Select_Mode); +end; { TEditor.Center_Text } + + +procedure TEditor.ChangeBounds (var Bounds : TRect); +begin + SetBounds (Bounds); + Delta.X := Max (0, Min (Delta.X, Limit.X - Size.X)); + Delta.Y := Max (0, Min (Delta.Y, Limit.Y - Size.Y)); + Update (ufView); +end; { TEditor.ChangeBounds } + + +function TEditor.CharPos (P, Target : Sw_Word) : Sw_Integer; +VAR + Pos : Sw_Integer; +begin + Pos := 0; + while P < Target do + begin + if BufChar (P) = #9 then + Pos := Pos or 7; + Inc (Pos); + Inc (P); + end; + CharPos := Pos; +end; { TEditor.CharPos } + + +function TEditor.CharPtr (P : Sw_Word; Target : Sw_Integer) : Sw_Word; +VAR + Pos : Sw_Integer; +begin + Pos := 0; + while (Pos < Target) and (P < BufLen) and not(BufChar (P) in [#10,#13]) do + begin + if BufChar (P) = #9 then + Pos := Pos or 7; + Inc (Pos); + Inc (P); + end; + if Pos > Target then + Dec (P); + CharPtr := P; +end; { TEditor.CharPtr } + + +procedure TEditor.Check_For_Word_Wrap (Select_Mode : Byte; + Center_Cursor : Boolean); +{ This procedure checks if CurPos.X > Right_Margin. } +{ If it is, then we Do_Word_Wrap. Simple, eh? } +begin + if CurPos.X > Right_Margin then + Do_Word_Wrap (Select_Mode, Center_Cursor); +end; {Check_For_Word_Wrap} + + +function TEditor.ClipCopy : Boolean; +begin + ClipCopy := False; + if Assigned(Clipboard) and (Clipboard <> @Self) then + begin + ClipCopy := Clipboard^.InsertFrom (@Self); + Selecting := False; + Update (ufUpdate); + end; +end; { TEditor.ClipCopy } + + +procedure TEditor.ClipCut; +begin + if ClipCopy then + begin + Update_Place_Markers (0, + Self.SelEnd - Self.SelStart, + Self.SelStart, + Self.SelEnd); + DeleteSelect; + end; +end; { TEditor.ClipCut } + + +procedure TEditor.ClipPaste; +begin + if Assigned(Clipboard) and (Clipboard <> @Self) then + begin + { Do not allow paste operations that will exceed } + { the Right_Margin when Word_Wrap is active and } + { cursor is at EOL. } + if Word_Wrap and (CurPos.X > Right_Margin) then + begin + EditorDialog (edPasteNotPossible, nil); + Exit; + end; + { The editor will not copy selected text if the CurPtr } + { is not the same value as the SelStart. However, it } + { does return an InsCount. This may, or may not, be a } + { bug. We don't want to update the Place_Marker if } + { there's no text copied. } + if CurPtr = SelStart then + Update_Place_Markers (Clipboard^.SelEnd - Clipboard^.SelStart, + 0, + Clipboard^.SelStart, + Clipboard^.SelEnd); + InsertFrom (Clipboard); + end; +end; { TEditor.ClipPaste } + + +procedure TEditor.ConvertEvent (var Event : Drivers.TEvent); +VAR + ShiftState : Byte; + Key : Word; +begin + ShiftState:=GetShiftState; + if Event.What = evKeyDown then + begin + if (ShiftState and $03 <> 0) + and (Event.ScanCode >= $47) + and (Event.ScanCode <= $51) then + Event.CharCode := #0; + Key := Event.KeyCode; + if KeyState <> 0 then + begin + if (Lo (Key) >= $01) and (Lo (Key) <= $1A) then + Inc (Key, $40); + if (Lo (Key) >= $61) and (Lo (Key) <= $7A) then + Dec (Key, $20); + end; + Key := ScanKeyMap (KeyMap[KeyState], Key); + KeyState := 0; + if Key <> 0 then + if Hi (Key) = $FF then + begin + KeyState := Lo (Key); + ClearEvent (Event); + end + else + begin + Event.What := evCommand; + Event.Command := Key; + end; + end; +end; { TEditor.ConvertEvent } + + +function TEditor.CursorVisible : Boolean; +begin + CursorVisible := (CurPos.Y >= Delta.Y) and (CurPos.Y < Delta.Y + Size.Y); +end; { TEditor.CursorVisible } + + +procedure TEditor.DeleteRange (StartPtr, EndPtr : Sw_Word; DelSelect : Boolean); +begin + { This will update Place_Marker for all deletions. } + { EXCEPT the Remove_EOL_Spaces deletion. } + Update_Place_Markers (0, EndPtr - StartPtr, StartPtr, EndPtr); + if HasSelection and DelSelect then + DeleteSelect + else + begin + SetSelect (CurPtr, EndPtr, True); + DeleteSelect; + SetSelect (StartPtr, CurPtr, False); + DeleteSelect; + end; +end; { TEditor.DeleteRange } + + +procedure TEditor.DeleteSelect; +begin + InsertText (nil, 0, False); +end; { TEditor.DeleteSelect } + + +procedure TEditor.DoneBuffer; +begin + ReAllocMem(Buffer, 0); +end; { TEditor.DoneBuffer } + + +procedure TEditor.DoSearchReplace; +VAR + I : Sw_Word; + C : Objects.TPoint; +begin + repeat + I := cmCancel; + if not Search (FindStr, Flags) then + begin + if Flags and (efReplaceAll + efDoReplace) <> (efReplaceAll + efDoReplace) then + EditorDialog (edSearchFailed, nil) + end + else + if Flags and efDoReplace <> 0 then + begin + I := cmYes; + if Flags and efPromptOnReplace <> 0 then + begin + MakeGlobal (Cursor, C); + I := EditorDialog (edReplacePrompt, Pointer(@C)); + end; + if I = cmYes then + begin + { If Word_Wrap is active and we are at EOL } + { disallow replace by bringing up a dialog } + { stating that replace is not possible. } + if Word_Wrap and + ((CurPos.X + (Length (ReplaceStr) - Length (FindStr))) > Right_Margin) then + EditorDialog (edReplaceNotPossible, nil) + else + begin + Lock; + Search_Replace := True; + if length (ReplaceStr) < length (FindStr) then + Update_Place_Markers (0, + Length (FindStr) - Length (ReplaceStr), + CurPtr - Length (FindStr) + Length (ReplaceStr), + CurPtr) + else + if length (ReplaceStr) > length (FindStr) then + Update_Place_Markers (Length (ReplaceStr) - Length (FindStr), + 0, + CurPtr, + CurPtr + (Length (ReplaceStr) - Length (FindStr))); + InsertText (@ReplaceStr[1], Length (ReplaceStr), False); + Search_Replace := False; + TrackCursor (False); + Unlock; + end; + end; + end; + until (I = cmCancel) or (Flags and efReplaceAll = 0); +end; { TEditor.DoSearchReplace } + + +procedure TEditor.DoUpdate; +begin + if UpdateFlags <> 0 then + begin + SetCursor (CurPos.X - Delta.X, CurPos.Y - Delta.Y); + if UpdateFlags and ufView <> 0 then + DrawView + else + if UpdateFlags and ufLine <> 0 then + DrawLines (CurPos.Y - Delta.Y, 1, LineStart (CurPtr)); + if assigned(HScrollBar) then + HScrollBar^.SetParams (Delta.X, 0, Limit.X - Size.X, Size.X div 2, 1); + if assigned(VScrollBar) then + VScrollBar^.SetParams (Delta.Y, 0, Limit.Y - Size.Y, Size.Y - 1, 1); + if assigned(Indicator) then + Indicator^.SetValue (CurPos, AutoIndent, Modified, Word_Wrap); + if State and sfActive <> 0 then + UpdateCommands; + UpdateFlags := 0; + end; +end; { TEditor.DoUpdate } + + +function TEditor.Do_Word_Wrap (Select_Mode : Byte; + Center_Cursor : Boolean) : Boolean; +{ This procedure does the actual wordwrap. It always assumes the CurPtr } +{ is at Right_Margin + 1. It makes several tests for special conditions } +{ and processes those first. If they all fail, it does a normal wrap. } +VAR + A : Sw_Word; { Distance between line start and first word on line. } + C : Sw_Word; { Current pointer when we come into procedure. } + L : Sw_Word; { BufLen when we come into procedure. } + P : Sw_Word; { Position of pointer at any given moment. } + S : Sw_Word; { Start of a line. } +begin + Do_Word_Wrap := False; + Select_Mode := 0; + if BufLen >= (BufSize - 1) then + exit; + C := CurPtr; + L := BufLen; + S := LineStart (CurPtr); + { If first character in the line is a space and autoindent mode is on } + { then we check to see if NextWord(S) exceeds the CurPtr. If it does, } + { we set CurPtr as the AutoIndent marker. If it doesn't, we will set } + { NextWord(S) as the AutoIndent marker. If neither, we set it to S. } + if AutoIndent and (Buffer^[S] = ' ') then + begin + if NextWord (S) > CurPtr then + A := CurPtr + else + A := NextWord (S); + end + else + A := NextWord (S); + { Though NewLine will remove EOL spaces, we do it here too. } + { This catches the instance where a user may try to space } + { completely across the line, in which case CurPtr.X = 0. } + Remove_EOL_Spaces (Select_Mode); + if CurPos.X = 0 then + begin + NewLine (Select_Mode); + Do_Word_Wrap := True; + Exit; + end; + { At this point we have one of five situations: } + { } + { 1) AutoIndent is on and this line is all spaces before CurPtr. } + { 2) AutoIndent is off and this line is all spaces before CurPtr. } + { 3) AutoIndent is on and this line is continuous characters before CurPtr. } + { 4) AutoIndent is off and this line is continuous characters before CurPtr. } + { 5) This is just a normal line of text. } + { } + { Conditions 1 through 4 have to be taken into account before condition 5. } + { First, we see if there are all spaces and/or all characters. } + { Then we determine which one it really is. Finally, we take } + { a course of action based on the state of AutoIndent. } + if PrevWord (CurPtr) <= S then + begin + P := CurPtr - 1; + while ((Buffer^[P] <> ' ') and (P > S)) do + Dec (P); + { We found NO SPACES. Conditions 4 and 5 are treated the same. } + { We can NOT do word wrap and put up a dialog box stating such. } + { Delete character just entered so we don't exceed Right_Margin. } + if P = S then + begin + EditorDialog (edWrapNotPossible, nil); + DeleteRange (PrevChar (CurPtr), CurPtr, True); + Exit; + end + else + begin + { There are spaces. Now find out if they are all spaces. } + { If so, see if AutoIndent is on. If it is, turn it off, } + { do a NewLine, and turn it back on. Otherwise, just do } + { the NewLine. We go through all of these gyrations for } + { AutoIndent. Being way out here with a preceding line } + { of spaces and wrapping with AutoIndent on is real dumb! } + { However, the user expects something. The wrap will NOT } + { autoindent, but they had no business being here anyway! } + P := CurPtr - 1; + while ((Buffer^[P] = ' ') and (P > S)) do + Dec (P); + if P = S then + begin + if Autoindent then + begin + AutoIndent := False; + NewLine (Select_Mode); + AutoIndent := True; + end + else + NewLine (Select_Mode); + end; { AutoIndent } + end; { P = S for spaces } + end { P = S for no spaces } + else { PrevWord (CurPtr) <= S } + begin + { Hooray! We actually had a plain old line of text to wrap! } + { Regardless if we are pushing out a line beyond the Right_Margin, } + { or at the end of a line itself, the following will determine } + { exactly where to do the wrap and re-set the cursor accordingly. } + { However, if P = A then we can't wrap. Show dialog and exit. } + P := CurPtr; + while P - S > Right_Margin do + P := PrevWord (P); + if (P = A) then + begin + EditorDialog (edReformNotPossible, nil); + SetCurPtr (P, Select_Mode); + Exit; + end; + SetCurPtr (P, Select_Mode); + NewLine (Select_Mode); + end; { PrevWord (CurPtr <= S } + { Track the cursor here (it is at CurPos.X = 0) so the view } + { will redraw itself at column 0. This eliminates having it } + { redraw starting at the current cursor and not being able } + { to see text before the cursor. Of course, we also end up } + { redrawing the view twice, here and back in HandleEvent. } + { } + { Reposition cursor so user can pick up where they left off. } + TrackCursor (Center_Cursor); + SetCurPtr (C - (L - BufLen), Select_Mode); + Do_Word_Wrap := True; +end; { TEditor.Do_Word_Wrap } + + +procedure TEditor.Draw; +begin + if DrawLine <> Delta.Y then + begin + DrawPtr := LineMove (DrawPtr, Delta.Y - DrawLine); + DrawLine := Delta.Y; + end; + DrawLines (0, Size.Y, DrawPtr); +end; { TEditor.Draw } + + +procedure TEditor.DrawLines (Y, Count : Sw_Integer; LinePtr : Sw_Word); +VAR + Color : Word; + B : array[0..MaxLineLength - 1] of Sw_Word; +begin + Color := GetColor ($0201); + while Count > 0 do + begin + FormatLine (B, LinePtr, Delta.X + Size.X, Color); + WriteBuf (0, Y, Size.X, 1, B[Delta.X]); + LinePtr := NextLine (LinePtr); + Inc (Y); + Dec (Count); + end; +end; { TEditor.DrawLines } + + +procedure TEditor.Find; +VAR + FindRec : TFindDialogRec; +begin + with FindRec do + begin + Find := FindStr; + Options := Flags; + if EditorDialog (edFind, @FindRec) <> cmCancel then + begin + FindStr := Find; + Flags := Options and not efDoReplace; + DoSearchReplace; + end; + end; +end; { TEditor.Find } + + +procedure TEditor.FormatLine (var DrawBuf; LinePtr : Sw_Word; + Width : Sw_Integer; + Colors : Word); +var + outptr : pword; + outcnt, + idxpos : Sw_Word; + attr : Word; + + procedure FillSpace(i:Sw_Word); + var + w : word; + begin + inc(OutCnt,i); + w:=32 or attr; + while (i>0) do + begin + OutPtr^:=w; + inc(OutPtr); + dec(i); + end; + end; + + function FormatUntil(endpos:Sw_word):boolean; + var + p : pchar; + begin + FormatUntil:=false; + p:=pchar(Buffer)+idxpos; + while endpos>idxpos do + begin + if OutCnt>=Width then + exit; + case p^ of + #9 : + FillSpace(Tabsize-(outcnt mod Tabsize)); + #10,#13 : + begin + FillSpace(Width-OutCnt); + FormatUntil:=true; + exit; + end; + else + begin + inc(OutCnt); + OutPtr^:=ord(p^) or attr; + inc(OutPtr); + end; + end; { case } + inc(p); + inc(idxpos); + end; + end; + +begin + OutCnt:=0; + OutPtr:=@DrawBuf; + idxPos:=LinePtr; + attr:=lo(Colors) shl 8; + if FormatUntil(SelStart) then + exit; + attr:=hi(Colors) shl 8; + if FormatUntil(CurPtr) then + exit; + inc(idxPos,GapLen); + if FormatUntil(SelEnd+GapLen) then + exit; + attr:=lo(Colors) shl 8; + if FormatUntil(BufSize) then + exit; +{ fill up until width } + FillSpace(Width-OutCnt); +end; {TEditor.FormatLine} + + +function TEditor.GetMousePtr (Mouse : Objects.TPoint) : Sw_Word; +begin + MakeLocal (Mouse, Mouse); + Mouse.X := Max (0, Min (Mouse.X, Size.X - 1)); + Mouse.Y := Max (0, Min (Mouse.Y, Size.Y - 1)); + GetMousePtr := CharPtr (LineMove (DrawPtr, Mouse.Y + Delta.Y - DrawLine), + Mouse.X + Delta.X); +end; { TEditor.GetMousePtr } + + +function TEditor.GetPalette : PPalette; +CONST + P : String[Length (CEditor)] = CEditor; +begin + GetPalette := PPalette(@P); +end; { TEditor.GetPalette } + + +procedure TEditor.HandleEvent (var Event : Drivers.TEvent); +VAR + ShiftState : Byte; + CenterCursor : Boolean; + SelectMode : Byte; + D : Objects.TPoint; + Mouse : Objects.TPoint; + + function CheckScrollBar (P : PScrollBar; var D : Sw_Integer) : Boolean; + begin + CheckScrollBar := FALSE; + if (Event.InfoPtr = P) and (P^.Value <> D) then + begin + D := P^.Value; + Update (ufView); + CheckScrollBar := TRUE; + end; + end; {CheckScrollBar} + +begin + Inherited HandleEvent (Event); + ConvertEvent (Event); + CenterCursor := not CursorVisible; + SelectMode := 0; + ShiftState:=GetShiftState; + if Selecting or (ShiftState and $03 <> 0) then + SelectMode := smExtend; + case Event.What of + Drivers.evMouseDown: + begin + if Event.Double then + SelectMode := SelectMode or smDouble; + repeat + Lock; + if Event.What = evMouseAuto then + begin + MakeLocal (Event.Where, Mouse); + D := Delta; + if Mouse.X < 0 then + Dec (D.X); + if Mouse.X >= Size.X then + Inc (D.X); + if Mouse.Y < 0 then + Dec (D.Y); + if Mouse.Y >= Size.Y then + Inc (D.Y); + ScrollTo (D.X, D.Y); + end; + SetCurPtr (GetMousePtr (Event.Where), SelectMode); + SelectMode := SelectMode or smExtend; + Unlock; + until not MouseEvent (Event, evMouseMove + evMouseAuto); + end; { Drivers.evMouseDown } + + Drivers.evKeyDown: + case Event.CharCode of + #32..#255: + begin + Lock; + if Overwrite and not HasSelection then + if CurPtr <> LineEnd (CurPtr) then + SelEnd := NextChar (CurPtr); + InsertText (@Event.CharCode, 1, False); + if Word_Wrap then + Check_For_Word_Wrap (SelectMode, CenterCursor); + TrackCursor (CenterCursor); + Unlock; + end; + + else + Exit; + end; { Drivers.evKeyDown } + + Drivers.evCommand: + case Event.Command of + cmFind : Find; + cmReplace : Replace; + cmSearchAgain : DoSearchReplace; + else + begin + Lock; + case Event.Command of + cmCut : ClipCut; + cmCopy : ClipCopy; + cmPaste : ClipPaste; + cmUndo : Undo; + cmClear : DeleteSelect; + cmCharLeft : SetCurPtr (PrevChar (CurPtr), SelectMode); + cmCharRight : SetCurPtr (NextChar (CurPtr), SelectMode); + cmWordLeft : SetCurPtr (PrevWord (CurPtr), SelectMode); + cmWordRight : SetCurPtr (NextWord (CurPtr), SelectMode); + cmLineStart : SetCurPtr (LineStart (CurPtr), SelectMode); + cmLineEnd : SetCurPtr (LineEnd (CurPtr), SelectMode); + cmLineUp : SetCurPtr (LineMove (CurPtr, -1), SelectMode); + cmLineDown : SetCurPtr (LineMove (CurPtr, 1), SelectMode); + cmPageUp : SetCurPtr (LineMove (CurPtr, - (Size.Y - 1)), SelectMode); + cmPageDown : SetCurPtr (LineMove (CurPtr, Size.Y - 1), SelectMode); + cmTextStart : SetCurPtr (0, SelectMode); + cmTextEnd : SetCurPtr (BufLen, SelectMode); + cmNewLine : NewLine (SelectMode); + cmBackSpace : DeleteRange (PrevChar (CurPtr), CurPtr, True); + cmDelChar : DeleteRange (CurPtr, NextChar (CurPtr), True); + cmDelWord : DeleteRange (CurPtr, NextWord (CurPtr), False); + cmDelStart : DeleteRange (LineStart (CurPtr), CurPtr, False); + cmDelEnd : DeleteRange (CurPtr, LineEnd (CurPtr), False); + cmDelLine : DeleteRange (LineStart (CurPtr), NextLine (CurPtr), False); + cmInsMode : ToggleInsMode; + cmStartSelect : StartSelect; + cmHideSelect : HideSelect; + cmIndentMode : begin + AutoIndent := not AutoIndent; + Update (ufStats); + end; { Added provision to update TIndicator if ^QI pressed. } + cmCenterText : Center_Text (SelectMode); + cmEndPage : SetCurPtr (LineMove (CurPtr, Delta.Y - CurPos.Y + Size.Y - 1), SelectMode); + cmHomePage : SetCurPtr (LineMove (CurPtr, -(CurPos.Y - Delta.Y)), SelectMode); + cmInsertLine : Insert_Line (SelectMode); + cmJumpLine : Jump_To_Line (SelectMode); + cmReformDoc : Reformat_Document (SelectMode, CenterCursor); + cmReformPara : Reformat_Paragraph (SelectMode, CenterCursor); + cmRightMargin : Set_Right_Margin; + cmScrollDown : Scroll_Down; + cmScrollUp : Scroll_Up; + cmSelectWord : Select_Word; + cmSetTabs : Set_Tabs; + cmTabKey : Tab_Key (SelectMode); + cmWordWrap : begin + Word_Wrap := not Word_Wrap; + Update (ufStats); + end; { Added provision to update TIndicator if ^OW pressed. } + cmSetMark0 : Set_Place_Marker (10); + cmSetMark1 : Set_Place_Marker (1); + cmSetMark2 : Set_Place_Marker (2); + cmSetMark3 : Set_Place_Marker (3); + cmSetMark4 : Set_Place_Marker (4); + cmSetMark5 : Set_Place_Marker (5); + cmSetMark6 : Set_Place_Marker (6); + cmSetMark7 : Set_Place_Marker (7); + cmSetMark8 : Set_Place_Marker (8); + cmSetMark9 : Set_Place_Marker (9); + cmJumpMark0 : Jump_Place_Marker (10, SelectMode); + cmJumpMark1 : Jump_Place_Marker (1, SelectMode); + cmJumpMark2 : Jump_Place_Marker (2, SelectMode); + cmJumpMark3 : Jump_Place_Marker (3, SelectMode); + cmJumpMark4 : Jump_Place_Marker (4, SelectMode); + cmJumpMark5 : Jump_Place_Marker (5, SelectMode); + cmJumpMark6 : Jump_Place_Marker (6, SelectMode); + cmJumpMark7 : Jump_Place_Marker (7, SelectMode); + cmJumpMark8 : Jump_Place_Marker (8, SelectMode); + cmJumpMark9 : Jump_Place_Marker (9, SelectMode); + else + Unlock; + Exit; + end; { Event.Command (Inner) } + TrackCursor (CenterCursor); + { If the user presses any key except cmNewline or cmBackspace } + { we need to check if the file has been modified yet. There } + { can be no spaces at the end of a line, or wordwrap doesn't } + { work properly. We don't want to do this if the file hasn't } + { been modified because the user could be bringing in an ASCII } + { file from an editor that allows spaces at the EOL. If we } + { took them out in that scenario the "M" would appear on the } + { TIndicator line and the user would get upset or confused. } + if (Event.Command <> cmNewLine) and + (Event.Command <> cmBackSpace) and + (Event.Command <> cmTabKey) and + Modified then + Remove_EOL_Spaces (SelectMode); + Unlock; + end; { Event.Command (Outer) } + end; { Drivers.evCommand } + + Drivers.evBroadcast: + case Event.Command of + cmScrollBarChanged: + if (Event.InfoPtr = HScrollBar) or + (Event.InfoPtr = VScrollBar) then + begin + CheckScrollBar (HScrollBar, Delta.X); + CheckScrollBar (VScrollBar, Delta.Y); + end + else + exit; + else + Exit; + end; { Drivers.evBroadcast } + + end; + ClearEvent (Event); +end; { TEditor.HandleEvent } + + +function TEditor.HasSelection : Boolean; +begin + HasSelection := SelStart <> SelEnd; +end; { TEditor.HasSelection } + + +procedure TEditor.HideSelect; +begin + Selecting := False; + SetSelect (CurPtr, CurPtr, False); +end; { TEditor.HideSelect } + + +procedure TEditor.InitBuffer; +begin + Assert(Buffer = nil, 'TEditor.InitBuffer: Buffer is not nil'); + ReAllocMem(Buffer, BufSize); +end; { TEditor.InitBuffer } + + +function TEditor.InsertBuffer (var P : PEditBuffer; + Offset, Length : Sw_Word; + AllowUndo, SelectText : Boolean) : Boolean; +VAR + SelLen : Sw_Word; + DelLen : Sw_Word; + SelLines : Sw_Word; + Lines : Sw_Word; + NewSize : Longint; +begin + InsertBuffer := True; + Selecting := False; + SelLen := SelEnd - SelStart; + if (SelLen = 0) and (Length = 0) then + Exit; + DelLen := 0; + if AllowUndo then + if CurPtr = SelStart then + DelLen := SelLen + else + if SelLen > InsCount then + DelLen := SelLen - InsCount; + NewSize := Longint (BufLen + DelCount - SelLen + DelLen) + Length; + if NewSize > BufLen + DelCount then + if (NewSize > MaxBufLength) or not SetBufSize (NewSize) then + begin + EditorDialog (edOutOfMemory, nil); + InsertBuffer := False; + SelEnd := SelStart; + Exit; + end; + SelLines := CountLines (Buffer^[BufPtr (SelStart)], SelLen); + if CurPtr = SelEnd then + begin + if AllowUndo then + begin + if DelLen > 0 then + Move (Buffer^[SelStart], Buffer^[CurPtr + GapLen - DelCount - DelLen], DelLen); + Dec (InsCount, SelLen - DelLen); + end; + CurPtr := SelStart; + Dec (CurPos.Y, SelLines); + end; + if Delta.Y > CurPos.Y then + begin + Dec (Delta.Y, SelLines); + if Delta.Y < CurPos.Y then + Delta.Y := CurPos.Y; + end; + if Length > 0 then + Move (P^[Offset], Buffer^[CurPtr], Length); + Lines := CountLines (Buffer^[CurPtr], Length); + Inc (CurPtr, Length); + Inc (CurPos.Y, Lines); + DrawLine := CurPos.Y; + DrawPtr := LineStart (CurPtr); + CurPos.X := CharPos (DrawPtr, CurPtr); + if not SelectText then + SelStart := CurPtr; + SelEnd := CurPtr; + if Length>Sellen then + begin + Inc (BufLen, Length - SelLen); + Dec (GapLen, Length - SelLen); + end + else + begin + Dec (BufLen, Sellen - Length); + Inc (GapLen, Sellen - Length); + end; + if AllowUndo then + begin + Inc (DelCount, DelLen); + Inc (InsCount, Length); + end; + Inc (Limit.Y, Lines - SelLines); + Delta.Y := Max (0, Min (Delta.Y, Limit.Y - Size.Y)); + if not IsClipboard then + Modified := True; + SetBufSize (BufLen + DelCount); + if (SelLines = 0) and (Lines = 0) then + Update (ufLine) + else + Update (ufView); +end; { TEditor.InsertBuffer } + + +function TEditor.InsertFrom (Editor : PEditor) : Boolean; +begin + InsertFrom := InsertBuffer (Editor^.Buffer, + Editor^.BufPtr (Editor^.SelStart), + Editor^.SelEnd - Editor^.SelStart, CanUndo, IsClipboard); +end; { TEditor.InsertFrom } + + +procedure TEditor.Insert_Line (Select_Mode : Byte); +{ This procedure inserts a newline at the current cursor position } +{ if a ^N is pressed. Unlike cmNewLine, the cursor will return } +{ to its original position. If the cursor was at the end of a } +{ line, and its spaces were removed, the cursor returns to the } +{ end of the line instead. } +begin + NewLine (Select_Mode); + SetCurPtr (LineEnd (LineMove (CurPtr, -1)), Select_Mode); +end; { TEditor.Insert_Line } + + +function TEditor.InsertText (Text : Pointer; + Length : Sw_Word; + SelectText : Boolean) : Boolean; +begin + if assigned(Text) and not Search_Replace then + Update_Place_Markers (Length, 0, Self.SelStart, Self.SelEnd); + InsertText := InsertBuffer (PEditBuffer (Text), + 0, Length, CanUndo, SelectText); +end; { TEditor.InsertText } + + +function TEditor.IsClipboard : Boolean; +begin + IsClipboard := Clipboard = @Self; +end; { TEditor.IsClipboard } + + +procedure TEditor.Jump_Place_Marker (Element : Byte; Select_Mode : Byte); +{ This procedure jumps to a place marker if ^Q# is pressed. } +{ We don't go anywhere if Place_Marker[Element] is not zero. } +begin + if (not IsClipboard) and (Place_Marker[Element] <> 0) then + SetCurPtr (Place_Marker[Element], Select_Mode); +end; { TEditor.Jump_Place_Marker } + + +procedure TEditor.Jump_To_Line (Select_Mode : Byte); +{ This function brings up a dialog box that allows } +{ the user to select a line number to jump to. } +VAR + Code : Integer; { Used for Val conversion. } + Temp_Value : Longint; { Holds converted dialog value. } +begin + if EditorDialog (edJumpToLine, @Line_Number) <> cmCancel then + begin + { Convert the Line_Number string to an interger. } + { Put it into Temp_Value. If the number is not } + { in the range 1..9999 abort. If the number is } + { our current Y position, abort. Otherwise, } + { go to top of document, and jump to the line. } + { There are faster methods. This one's easy. } + { Note that CurPos.Y is always 1 less than what } + { the TIndicator line says. } + val (Line_Number, Temp_Value, Code); + if (Temp_Value < 1) or (Temp_Value > 9999999) then + Exit; + if Temp_Value = CurPos.Y + 1 then + Exit; + SetCurPtr (0, Select_Mode); + SetCurPtr (LineMove (CurPtr, Temp_Value - 1), Select_Mode); + end; +end; {TEditor.Jump_To_Line} + + +function TEditor.LineEnd (P : Sw_Word) : Sw_Word; +var + start, + i : Sw_word; + pc : pchar; +begin + if P<CurPtr then + begin + i:=CurPtr-P; + pc:=pchar(Buffer)+P; + while (i>0) do + begin + if pc^ in [#10,#13] then + begin + LineEnd:=pc-pchar(Buffer); + exit; + end; + inc(pc); + dec(i); + end; + start:=CurPtr; + end + else + start:=P; + i:=BufLen-Start; + pc:=pchar(Buffer)+GapLen+start; + while (i>0) do + begin + if pc^ in [#10,#13] then + begin + LineEnd:=pc-(pchar(Buffer)+Gaplen); + exit; + end; + inc(pc); + dec(i); + end; + LineEnd:=pc-(pchar(Buffer)+Gaplen); +end; { TEditor.LineEnd } + + +function TEditor.LineMove (P : Sw_Word; Count : Sw_Integer) : Sw_Word; +VAR + Pos : Sw_Integer; + I : Sw_Word; +begin + I := P; + P := LineStart (P); + Pos := CharPos (P, I); + while Count <> 0 do + begin + I := P; + if Count < 0 then + begin + P := PrevLine (P); + Inc (Count); + end + else + begin + P := NextLine (P); + Dec (Count); + end; + end; + if P <> I then + P := CharPtr (P, Pos); + LineMove := P; +end; { TEditor.LineMove } + + +function TEditor.LineStart (P : Sw_Word) : Sw_Word; +var + i : Sw_word; + start,pc : pchar; + oc : char; +begin + if P>CurPtr then + begin + start:=pchar(Buffer)+GapLen; + pc:=start; + i:=P-CurPtr; + dec(pc); + while (i>0) do + begin + if pc^ in [#10,#13] then + break; + dec(pc); + dec(i); + end; + end + else + i:=0; + if i=0 then + begin + start:=pchar(Buffer); + i:=P; + pc:=start+p; + dec(pc); + while (i>0) do + begin + if pc^ in [#10,#13] then + break; + dec(pc); + dec(i); + end; + if i=0 then + begin + LineStart:=0; + exit; + end; + end; + oc:=pc^; + LineStart:=pc-start+1; +end; { TEditor.LineStart } + + +function TEditor.LineNr (P : Sw_Word) : Sw_Word; +var + pc,endp : pchar; + lines : sw_word; +begin + endp:=pchar(Buffer)+BufPtr(P); + pc:=pchar(Buffer); + lines:=0; + while (pc<endp) do + begin + if pc^ in [#10,#13] then + begin + inc(lines); + if ord((pc+1)^)+ord(pc^)=23 then + begin + inc(pc); + if (pc>=endp) then + break; + end; + end; + inc(pc); + end; + LineNr:=Lines; +end; + + +procedure TEditor.Lock; +begin + Inc (LockCount); +end; { TEditor.Lock } + + +function TEditor.NewLine (Select_Mode : Byte) : Boolean; +VAR + I : Sw_Word; { Used to track spaces for AutoIndent. } + P : Sw_Word; { Position of Cursor when we arrive and after Newline. } +begin + P := LineStart (CurPtr); + I := P; + { The first thing we do is remove any End Of Line spaces. } + { Then we check to see how many spaces are on beginning } + { of a line. We need this check to add them after CR/LF } + { if AutoIndenting. Last of all we insert spaces required } + { for the AutoIndenting, if it was on. } + Remove_EOL_Spaces (Select_Mode); + while (I < CurPtr) and ((Buffer^[I] in [#9,' '])) do + Inc (I); + if InsertText (@LineBreak[1], length(LineBreak), False) = FALSE then + exit; + if AutoIndent then + InsertText (@Buffer^[P], I - P, False); + { Remember where the CurPtr is at this moment. } + { Remember the length of the buffer at the moment. } + { Go to the previous line and remove EOL spaces. } + { Once removed, re-set the cursor to where we were } + { minus any spaces that might have been removed. } + I := BufLen; + P := CurPtr; + SetCurPtr (LineMove (CurPtr, - 1), Select_Mode); + Remove_EOL_Spaces (Select_Mode); + if I - BufLen <> 0 then + SetCurPtr (P - (I - BufLen), Select_Mode) + else + SetCurPtr (P, Select_Mode); + NewLine:=true; +end; { TEditor.NewLine } + + +function TEditor.NextChar (P : Sw_Word) : Sw_Word; +var + pc : pchar; +begin + if P<>BufLen then + begin + inc(P); + if P<>BufLen then + begin + pc:=pchar(Buffer); + if P>=CurPtr then + inc(pc,GapLen); + inc(pc,P-1); + if ord(pc^)+ord((pc+1)^)=23 then + inc(p); + end; + end; + NextChar:=P; +end; { TEditor.NextChar } + + +function TEditor.NextLine (P : Sw_Word) : Sw_Word; +begin + NextLine := NextChar (LineEnd (P)); +end; { TEditor.NextLine } + + +function TEditor.NextWord (P : Sw_Word) : Sw_Word; +begin + { skip word } + while (P < BufLen) and (BufChar (P) in WordChars) do + P := NextChar (P); + { skip spaces } + while (P < BufLen) and not (BufChar (P) in WordChars) do + P := NextChar (P); + NextWord := P; +end; { TEditor.NextWord } + + +function TEditor.PrevChar (P : Sw_Word) : Sw_Word; +var + pc : pchar; +begin + if p<>0 then + begin + dec(p); + if p<>0 then + begin + pc:=pchar(Buffer); + if P>=CurPtr then + inc(pc,GapLen); + inc(pc,P-1); + if ord(pc^)+ord((pc+1)^)=23 then + dec(p); + end; + end; + PrevChar:=P; +end; { TEditor.PrevChar } + + +function TEditor.PrevLine (P : Sw_Word) : Sw_Word; +begin + PrevLine := LineStart (PrevChar (P)); +end; { TEditor.PrevLine } + + +function TEditor.PrevWord (P : Sw_Word) : Sw_Word; +begin + { skip spaces } + while (P > 0) and not (BufChar (PrevChar (P)) in WordChars) do + P := PrevChar (P); + { skip word } + while (P > 0) and (BufChar (PrevChar (P)) in WordChars) do + P := PrevChar (P); + PrevWord := P; +end; { TEditor.PrevWord } + + +procedure TEditor.Reformat_Document (Select_Mode : Byte; Center_Cursor : Boolean); +{ This procedure will do a reformat of the entire document, or just } +{ from the current line to the end of the document, if ^QU is pressed. } +{ It simply brings up the correct dialog box, and then calls the } +{ TEditor.Reformat_Paragraph procedure to do the actual reformatting. } +CONST + efCurrentLine = $0000; { Radio button #1 selection for dialog box. } + efWholeDocument = $0001; { Radio button #2 selection for dialog box. } +VAR + Reformat_Options : Word; { Holds the dialog options for reformatting. } +begin + { Check if Word_Wrap is toggled on. If NOT on, check if programmer } + { allows reformatting of document and if not show user dialog that } + { says reformatting is not permissable. } + if not Word_Wrap then + begin + if not Allow_Reformat then + begin + EditorDialog (edReformatNotAllowed, nil); + Exit; + end; + Word_Wrap := True; + Update (ufStats); + end; + { Default radio button option to 1st one. Bring up dialog box. } + Reformat_Options := efCurrentLine; + if EditorDialog (edReformatDocument, @Reformat_Options) <> cmCancel then + begin + { If the option to reformat the whole document was selected } + { we need to go back to start of document. Otherwise we stay } + { on the current line. Call Reformat_Paragraph until we get } + { to the end of the document to do the reformatting. } + if Reformat_Options and efWholeDocument <> 0 then + SetCurPtr (0, Select_Mode); + Unlock; + repeat + Lock; + if NOT Reformat_Paragraph (Select_Mode, Center_Cursor) then + Exit; + TrackCursor (False); + Unlock; + until CurPtr = BufLen; + end; +end; { TEditor.Reformat_Document } + + +function TEditor.Reformat_Paragraph (Select_Mode : Byte; + Center_Cursor : Boolean) : Boolean; +{ This procedure will do a reformat of the current paragraph if ^B pressed. } +{ The feature works regardless if wordrap is on or off. It also supports } +{ the AutoIndent feature. Reformat is not possible if the CurPos exceeds } +{ the Right_Margin. Right_Margin is where the EOL is considered to be. } +CONST + Space : array [1..2] of Char = #32#32; +VAR + C : Sw_Word; { Position of CurPtr when we come into procedure. } + E : Sw_Word; { End of a line. } + S : Sw_Word; { Start of a line. } +begin + Reformat_Paragraph := False; + { Check if Word_Wrap is toggled on. If NOT on, check if programmer } + { allows reformatting of paragraph and if not show user dialog that } + { says reformatting is not permissable. } + if not Word_Wrap then + begin + if not Allow_Reformat then + begin + EditorDialog (edReformatNotAllowed, nil); + Exit; + end; + Word_Wrap := True; + Update (ufStats); + end; + C := CurPtr; + E := LineEnd (CurPtr); + S := LineStart (CurPtr); + { Reformat possible only if current line is NOT blank! } + if E <> S then + begin + { Reformat is NOT possible if the first word } + { on the line is beyond the Right_Margin. } + S := LineStart (CurPtr); + if NextWord (S) - S >= Right_Margin - 1 then + begin + EditorDialog (edReformNotPossible, nil); + Exit; + end; + { First objective is to find the first blank line } + { after this paragraph so we know when to stop. } + { That could be the end of the document. } + Repeat + SetCurPtr (LineMove (CurPtr, 1), Select_Mode); + E := LineEnd (CurPtr); + S := LineStart (CurPtr); + BlankLine := E; + until ((CurPtr = BufLen) or (E = S)); + SetCurPtr (C, Select_Mode); + repeat + { Set CurPtr to LineEnd and remove the EOL spaces. } + { Pull up the next line and remove its EOL space. } + { First make sure the next line is not BlankLine! } + { Insert spaces as required between the two lines. } + SetCurPtr (LineEnd (CurPtr), Select_Mode); + Remove_EOL_Spaces (Select_Mode); + if CurPtr <> Blankline - 2 then + DeleteRange (CurPtr, Nextword (CurPtr), True); + Remove_EOL_Spaces (Select_Mode); + case Buffer^[CurPtr-1] of + '!' : InsertText (@Space, 2, False); + '.' : InsertText (@Space, 2, False); + ':' : InsertText (@Space, 2, False); + '?' : InsertText (@Space, 2, False); + else + InsertText (@Space, 1, False); + end; + { Reset CurPtr to EOL. While line length is > Right_Margin } + { go Do_Word_Wrap. If wordrap failed, exit routine. } + SetCurPtr (LineEnd (CurPtr), Select_Mode); + while LineEnd (CurPtr) - LineStart (CurPtr) > Right_Margin do + if not Do_Word_Wrap (Select_Mode, Center_Cursor) then + Exit; + { If LineEnd - LineStart > Right_Margin then set CurPtr } + { to Right_Margin on current line. Otherwise we set the } + { CurPtr to LineEnd. This gyration sets up the conditions } + { to test for time of loop exit. } + if LineEnd (CurPtr) - LineStart (CurPtr) > Right_Margin then + SetCurPtr (LineStart (CurPtr) + Right_Margin, Select_Mode) + else + SetCurPtr (LineEnd (CurPtr), Select_Mode); + until ((CurPtr >= BufLen) or (CurPtr >= BlankLine - 2)); + end; + { If not at the end of the document reset CurPtr to start of next line. } + { This should be a blank line between paragraphs. } + if CurPtr < BufLen then + SetCurPtr (LineMove (CurPtr, 1), Select_Mode); + Reformat_Paragraph := True; +end; { TEditor.Reformat_Paragraph } + + +procedure TEditor.Remove_EOL_Spaces (Select_Mode : Byte); +{ This procedure tests to see if there are consecutive spaces } +{ at the end of a line (EOL). If so, we delete all spaces } +{ after the last non-space character to the end of line. } +{ We then reset the CurPtr to where we ended up at. } +VAR + C : Sw_Word; { Current pointer when we come into procedure. } + E : Sw_Word; { End of line. } + P : Sw_Word; { Position of pointer at any given moment. } + S : Sw_Word; { Start of a line. } +begin + C := CurPtr; + E := LineEnd (CurPtr); + P := E; + S := LineStart (CurPtr); + { Start at the end of a line and move towards the start. } + { Find first non-space character in that direction. } + while (P > S) and (BufChar (PrevChar (P)) = #32) do + P := PrevChar (P); + { If we found any spaces then delete them. } + if P < E then + begin + SetSelect (P, E, True); + DeleteSelect; + Update_Place_Markers (0, E - P, P, E); + end; + { If C, our pointer when we came into this procedure, } + { is less than the CurPtr then reset CurPtr to C so } + { cursor is where we started. Otherwise, set it to } + { the new CurPtr, for we have deleted characters. } + if C < CurPtr then + SetCurPtr (C, Select_Mode) + else + SetCurPtr (CurPtr, Select_Mode); +end; { TEditor.Remove_EOL_Spaces } + + +procedure TEditor.Replace; +VAR + ReplaceRec : TReplaceDialogRec; +begin + with ReplaceRec do + begin + Find := FindStr; + Replace := ReplaceStr; + Options := Flags; + if EditorDialog (edReplace, @ReplaceRec) <> cmCancel then + begin + FindStr := Find; + ReplaceStr := Replace; + Flags := Options or efDoReplace; + DoSearchReplace; + end; + end; +end; { TEditor.Replace } + + +procedure TEditor.Scroll_Down; +{ This procedure will scroll the screen up, and always keep } +{ the cursor on the CurPos.Y position, but not necessarily on } +{ the CurPos.X. If CurPos.Y scrolls off the screen, the cursor } +{ will stay in the upper left corner of the screen. This will } +{ simulate the same process in the IDE. The CurPos.X coordinate } +{ only messes up if we are on long lines and we then encounter } +{ a shorter or blank line beneath the current one as we scroll. } +{ In that case, it goes to the end of the new line. } +VAR + C : Sw_Word; { Position of CurPtr when we enter procedure. } + P : Sw_Word; { Position of CurPtr at any given time. } + W : Objects.TPoint; { CurPos.Y of CurPtr and P ('.X and '.Y). } +begin + { Remember current cursor position. Remember current CurPos.Y position. } + { Now issue the equivalent of a [Ctrl]-[End] command so the cursor will } + { go to the bottom of the current screen. Reset the cursor to this new } + { position and then send FALSE to TrackCursor so we fool it into } + { incrementing Delta.Y by only +1. If we didn't do this it would try } + { to center the cursor on the screen by fiddling with Delta.Y. } + C := CurPtr; + W.X := CurPos.Y; + P := LineMove (CurPtr, Delta.Y - CurPos.Y + Size.Y); + SetCurPtr (P, 0); + TrackCursor (False); + { Now remember where the new CurPos.Y is. See if distance between new } + { CurPos.Y and old CurPos.Y are greater than the current screen size. } + { If they are, we need to move cursor position itself down by one. } + { Otherwise, send the cursor back to our original CurPtr. } + W.Y := CurPos.Y; + if W.Y - W.X > Size.Y - 1 then + SetCurPtr (LineMove (C, 1), 0) + else + SetCurPtr (C, 0); +end; { TEditor.Scroll_Down } + + +procedure TEditor.Scroll_Up; +{ This procedure will scroll the screen down, and always keep } +{ the cursor on the CurPos.Y position, but not necessarily on } +{ the CurPos.X. If CurPos.Y scrolls off the screen, the cursor } +{ will stay in the bottom left corner of the screen. This will } +{ simulate the same process in the IDE. The CurPos.X coordinate } +{ only messes up if we are on long lines and we then encounter } +{ a shorter or blank line beneath the current one as we scroll. } +{ In that case, it goes to the end of the new line. } +VAR + C : Sw_Word; { Position of CurPtr when we enter procedure. } + P : Sw_Word; { Position of CurPtr at any given time. } + W : Objects.TPoint; { CurPos.Y of CurPtr and P ('.X and '.Y). } +begin + { Remember current cursor position. Remember current CurPos.Y position. } + { Now issue the equivalent of a [Ctrl]-[Home] command so the cursor will } + { go to the top of the current screen. Reset the cursor to this new } + { position and then send FALSE to TrackCursor so we fool it into } + { decrementing Delta.Y by only -1. If we didn't do this it would try } + { to center the cursor on the screen by fiddling with Delta.Y. } + C := CurPtr; + W.Y := CurPos.Y; + P := LineMove (CurPtr, -(CurPos.Y - Delta.Y + 1)); + SetCurPtr (P, 0); + TrackCursor (False); + { Now remember where the new CurPos.Y is. See if distance between new } + { CurPos.Y and old CurPos.Y are greater than the current screen size. } + { If they are, we need to move the cursor position itself up by one. } + { Otherwise, send the cursor back to our original CurPtr. } + W.X := CurPos.Y; + if W.Y - W.X > Size.Y - 1 then + SetCurPtr (LineMove (C, -1), 0) + else + SetCurPtr (C, 0); +end; { TEditor.Scroll_Up } + + +procedure TEditor.ScrollTo (X, Y : Sw_Integer); +begin + X := Max (0, Min (X, Limit.X - Size.X)); + Y := Max (0, Min (Y, Limit.Y - Size.Y)); + if (X <> Delta.X) or (Y <> Delta.Y) then + begin + Delta.X := X; + Delta.Y := Y; + Update (ufView); + end; +end; { TEditor.ScrollTo } + + +function TEditor.Search (const FindStr : String; Opts : Word) : Boolean; +VAR + I,Pos : Sw_Word; +begin + Search := False; + Pos := CurPtr; + repeat + if Opts and efCaseSensitive <> 0 then + I := Scan (Buffer^[BufPtr (Pos)], BufLen - Pos, FindStr) + else + I := IScan (Buffer^[BufPtr (Pos)], BufLen - Pos, FindStr); + if (I <> sfSearchFailed) then + begin + Inc (I, Pos); + if (Opts and efWholeWordsOnly = 0) or + not (((I <> 0) and (BufChar (I - 1) in WordChars)) or + ((I + Length (FindStr) <> BufLen) and + (BufChar (I + Length (FindStr)) in WordChars))) then + begin + Lock; + SetSelect (I, I + Length (FindStr), False); + TrackCursor (not CursorVisible); + Unlock; + Search := True; + Exit; + end + else + Pos := I + 1; + end; + until I = sfSearchFailed; +end; { TEditor.Search } + + +procedure TEditor.Select_Word; +{ This procedure will select the a word to put into the clipboard. } +{ I've added it just to maintain compatibility with the IDE editor. } +{ Note that selection starts at the current cursor position and ends } +{ when a space or the end of line is encountered. } +VAR + E : Sw_Word; { End of the current line. } + Select_Mode : Byte; { Allows us to turn select mode on inside procedure. } +begin + E := LineEnd (CurPtr); + { If the cursor is on a space or at the end of a line, abort. } + { Stupid action on users part for you can't select blanks! } + if (BufChar (CurPtr) = #32) or (CurPtr = E) then + Exit; + { Turn on select mode and tell editor to start selecting text. } + { As long as we have a character > a space (this is done to } + { exclude CR/LF pairs at end of a line) and we are NOT at the } + { end of a line, set the CurPtr to the next character. } + { Once we find a space or CR/LF, selection is done and we } + { automatically put the selected word into the Clipboard. } + Select_Mode := smExtend; + StartSelect; + while (BufChar (NextChar (CurPtr)) > #32) and (CurPtr < E) do + SetCurPtr (NextChar (CurPtr), Select_Mode); + SetCurPtr (NextChar (CurPtr), Select_Mode); + ClipCopy; +end; {TEditor.Select_Word } + + +procedure TEditor.SetBufLen (Length : Sw_Word); +begin + BufLen := Length; + GapLen := BufSize - Length; + SelStart := 0; + SelEnd := 0; + CurPtr := 0; + CurPos.X:=0; + CurPos.Y:=0; + Delta.X:=0; + Delta.Y:=0; + GetLimits(Buffer^[GapLen], BufLen,Limit); + inc(Limit.X); + inc(Limit.Y); + DrawLine := 0; + DrawPtr := 0; + DelCount := 0; + InsCount := 0; + Modified := False; + Update (ufView); +end; { TEditor.SetBufLen } + + +function TEditor.SetBufSize (NewSize : Sw_Word) : Boolean; +begin + ReAllocMem(Buffer, NewSize); + BufSize := NewSize; + SetBufSize := True; +end; { TEditor.SetBufSize } + + +procedure TEditor.SetCmdState (Command : Word; Enable : Boolean); +VAR + S : TCommandSet; +begin + S := [Command]; + if Enable and (State and sfActive <> 0) then + EnableCommands (S) + else + DisableCommands (S); +end; { TEditor.SetCmdState } + + +procedure TEditor.SetCurPtr (P : Sw_Word; SelectMode : Byte); +VAR + Anchor : Sw_Word; +begin + if SelectMode and smExtend = 0 then + Anchor := P + else + if CurPtr = SelStart then + Anchor := SelEnd + else + Anchor := SelStart; + if P < Anchor then + begin + if SelectMode and smDouble <> 0 then + begin + P := PrevLine (NextLine (P)); + Anchor := NextLine (PrevLine (Anchor)); + end; + SetSelect (P, Anchor, True); + end + else + begin + if SelectMode and smDouble <> 0 then + begin + P := NextLine (P); + Anchor := PrevLine (NextLine (Anchor)); + end; + SetSelect (Anchor, P, False); + end; +end; { TEditor.SetCurPtr } + + +procedure TEditor.Set_Place_Marker (Element : Byte); +{ This procedure sets a place marker for the CurPtr if ^K# is pressed. } +begin + if not IsClipboard then + Place_Marker[Element] := CurPtr; +end; { TEditor.Set_Place_Marker } + + +procedure TEditor.Set_Right_Margin; +{ This procedure will bring up a dialog box } +{ that allows the user to set Right_Margin. } +{ Values must be < MaxLineLength and > 9. } +VAR + Code : Integer; { Used for Val conversion. } + Margin_Data : TRightMarginRec; { Holds dialog results. } + Temp_Value : Sw_Integer; { Holds converted dialog value. } +begin + with Margin_Data do + begin + Str (Right_Margin, Margin_Position); + if EditorDialog (edRightMargin, @Margin_Position) <> cmCancel then + begin + val (Margin_Position, Temp_Value, Code); + if (Temp_Value <= MaxLineLength) and (Temp_Value > 9) then + Right_Margin := Temp_Value; + end; + end; +end; { TEditor.Set_Right_Margin } + + +procedure TEditor.SetSelect (NewStart, NewEnd : Sw_Word; CurStart : Boolean); +VAR + UFlags : Byte; + P : Sw_Word; + L : Sw_Word; +begin + if CurStart then + P := NewStart + else + P := NewEnd; + UFlags := ufUpdate; + if (NewStart <> SelStart) or (NewEnd <> SelEnd) then + if (NewStart <> NewEnd) or (SelStart <> SelEnd) then + UFlags := ufView; + if P <> CurPtr then + begin + if P > CurPtr then + begin + L := P - CurPtr; + Move (Buffer^[CurPtr + GapLen], Buffer^[CurPtr], L); + Inc (CurPos.Y, CountLines (Buffer^[CurPtr], L)); + CurPtr := P; + end + else + begin + L := CurPtr - P; + CurPtr := P; + Dec (CurPos.Y, CountLines (Buffer^[CurPtr], L)); + Move (Buffer^[CurPtr], Buffer^[CurPtr + GapLen], L); + end; + DrawLine := CurPos.Y; + DrawPtr := LineStart (P); + CurPos.X := CharPos (DrawPtr, P); + DelCount := 0; + InsCount := 0; + SetBufSize (BufLen); + end; + SelStart := NewStart; + SelEnd := NewEnd; + Update (UFlags); +end; { TEditor.Select } + + +procedure TEditor.SetState (AState : Word; Enable : Boolean); +begin + Inherited SetState (AState, Enable); + case AState of + sfActive: begin + if assigned(HScrollBar) then + HScrollBar^.SetState (sfVisible, Enable); + if assigned(VScrollBar) then + VScrollBar^.SetState (sfVisible, Enable); + if assigned(Indicator) then + Indicator^.SetState (sfVisible, Enable); + UpdateCommands; + end; + sfExposed: if Enable then Unlock; + end; +end; { TEditor.SetState } + + +procedure TEditor.Set_Tabs; +{ This procedure will bring up a dialog box } +{ that allows the user to set tab stops. } +VAR + Index : Sw_Integer; { Index into string array. } + Tab_Data : TTabStopRec; { Holds dialog results. } +begin + with Tab_Data do + begin + { Assign current Tab_Settings to Tab_String. } + { Bring up the tab dialog so user can set tabs. } + Tab_String := Copy (Tab_Settings, 1, Tab_Stop_Length); + if EditorDialog (edSetTabStops, @Tab_String) <> cmCancel then + begin + { If Tab_String comes back as empty then set Tab_Settings to nil. } + { Otherwise, find the last character in Tab_String that is not } + { a space and copy Tab_String into Tab_Settings up to that spot. } + if Length (Tab_String) = 0 then + begin + FillChar (Tab_Settings, SizeOf (Tab_Settings), #0); + Tab_Settings[0] := #0; + Exit; + end + else + begin + Index := Length (Tab_String); + while Tab_String[Index] <= #32 do + Dec (Index); + Tab_Settings := Copy (Tab_String, 1, Index); + end; + end; + end; +end; { TEditor.Set_Tabs } + + +procedure TEditor.StartSelect; +begin + HideSelect; + Selecting := True; +end; { TEditor.StartSelect } + + +procedure TEditor.Store (var S : Objects.TStream); +begin + Inherited Store (S); + PutPeerViewPtr (S, HScrollBar); + PutPeerViewPtr (S, VScrollBar); + PutPeerViewPtr (S, Indicator); + S.Write (BufSize, SizeOf (BufSize)); + S.Write (Canundo, SizeOf (Canundo)); + S.Write (AutoIndent, SizeOf (AutoIndent)); + S.Write (Line_Number, SizeOf (Line_Number)); + S.Write (Place_Marker, SizeOf (Place_Marker)); + S.Write (Right_Margin, SizeOf (Right_Margin)); + S.Write (Tab_Settings, SizeOf (Tab_Settings)); + S.Write (Word_Wrap, SizeOf (Word_Wrap)); +end; { Editor.Store } + + +procedure TEditor.Tab_Key (Select_Mode : Byte); +{ This function determines if we are in overstrike or insert mode, } +{ and then moves the cursor if overstrike, or adds spaces if insert. } +VAR + E : Sw_Word; { End of current line. } + Index : Sw_Integer; { Loop counter. } + Position : Sw_Integer; { CurPos.X position. } + S : Sw_Word; { Start of current line. } + Spaces : array [1..80] of Char; { Array to hold spaces for insertion. } +begin + E := LineEnd (CurPtr); + S := LineStart (CurPtr); + { Find the current horizontal cursor position. } + { Now loop through the Tab_Settings string and } + { find the next available tab stop. } + Position := CurPos.X + 1; + repeat + Inc (Position); + until (Tab_Settings[Position] <> #32) or (Position >= Ord (Tab_Settings[0])); + E := CurPos.X; + Index := 1; + { Now we enter a loop to go to the next tab position. } + { If we are in overwrite mode, we just move the cursor } + { through the text to the next tab stop. If we are in } + { insert mode, we add spaces to the Spaces array for } + { the number of times we loop. } + while Index < Position - E do + begin + if Overwrite then + begin + if (Position > LineEnd (CurPtr) - LineStart (CurPtr)) + or (Position > Ord (Tab_Settings[0])) then + begin + SetCurPtr (LineStart (LineMove (CurPtr, 1)), Select_Mode); + Exit; + end + else + if CurPtr < BufLen then + SetCurPtr (NextChar (CurPtr), Select_Mode); + end + else + begin + if (Position > Right_Margin) or (Position > Ord (Tab_Settings[0])) then + begin + SetCurPtr (LineStart (LineMove (CurPtr, 1)), Select_Mode); + Exit; + end + else + Spaces[Index] := #32; + end; + Inc (Index); + end; + { If we are insert mode, we insert spaces to the next tab stop. } + { When we're all done, the cursor will be sitting on the new tab stop. } + if not OverWrite then + InsertText (@Spaces, Index - 1, False); +end; { TEditor.Tab_Key } + + +procedure TEditor.ToggleInsMode; +begin + Overwrite := not Overwrite; + SetState (sfCursorIns, not GetState (sfCursorIns)); +end; { TEditor.ToggleInsMode } + + +procedure TEditor.TrackCursor (Center : Boolean); +begin + if Center then + ScrollTo (CurPos.X - Size.X + 1, CurPos.Y - Size.Y div 2) + else + ScrollTo (Max (CurPos.X - Size.X + 1, Min (Delta.X, CurPos.X)), + Max (CurPos.Y - Size.Y + 1, Min (Delta.Y, CurPos.Y))); +end; { TEditor.TrackCursor } + + +procedure TEditor.Undo; +VAR + Length : Sw_Word; +begin + if (DelCount <> 0) or (InsCount <> 0) then + begin + Update_Place_Markers (DelCount, 0, CurPtr, CurPtr + DelCount); + SelStart := CurPtr - InsCount; + SelEnd := CurPtr; + Length := DelCount; + DelCount := 0; + InsCount := 0; + InsertBuffer (Buffer, CurPtr + GapLen - Length, Length, False, True); + end; +end; { TEditor.Undo } + + +procedure TEditor.Unlock; +begin + if LockCount > 0 then + begin + Dec (LockCount); + if LockCount = 0 then + DoUpdate; + end; +end; { TEditor.Unlock } + + +procedure TEditor.Update (AFlags : Byte); +begin + UpdateFlags := UpdateFlags or AFlags; + if LockCount = 0 then + DoUpdate; +end; { TEditor.Update } + + +procedure TEditor.UpdateCommands; +begin + SetCmdState (cmUndo, (DelCount <> 0) or (InsCount <> 0)); + if not IsClipboard then + begin + SetCmdState (cmCut, HasSelection); + SetCmdState (cmCopy, HasSelection); + SetCmdState (cmPaste, assigned(Clipboard) and (Clipboard^.HasSelection)); + end; + SetCmdState (cmClear, HasSelection); + SetCmdState (cmFind, True); + SetCmdState (cmReplace, True); + SetCmdState (cmSearchAgain, True); +end; { TEditor.UpdateCommands } + + +procedure TEditor.Update_Place_Markers (AddCount : Word; KillCount : Word; + StartPtr,EndPtr : Sw_Word); +{ This procedure updates the position of the place markers } +{ as the user inserts and deletes text in the document. } +VAR + Element : Byte; { Place_Marker array element to traverse array with. } +begin + for Element := 1 to 10 do + begin + if AddCount > 0 then + begin + if (Place_Marker[Element] >= Curptr) + and (Place_Marker[Element] <> 0) then + Place_Marker[Element] := Place_Marker[Element] + AddCount; + end + else + begin + if Place_Marker[Element] >= StartPtr then + begin + if (Place_Marker[Element] >= StartPtr) and + (Place_Marker[Element] < EndPtr) then + Place_marker[Element] := 0 + else + begin + if integer (Place_Marker[Element]) - integer (KillCount) > 0 then + Place_Marker[Element] := Place_Marker[Element] - KillCount + else + Place_Marker[Element] := 0; + end; + end; + end; + end; + if AddCount > 0 then + BlankLine := BlankLine + AddCount + else + begin + if integer (BlankLine) - Integer (KillCount) > 0 then + BlankLine := BlankLine - KillCount + else + BlankLine := 0; + end; +end; { TEditor.Update_Place_Markers } + + +function TEditor.Valid (Command : Word) : Boolean; +begin + Valid := IsValid; +end; { TEditor.Valid } + + +{**************************************************************************** + TMEMO +****************************************************************************} + +constructor TMemo.Load (var S : Objects.TStream); +VAR + Length : Sw_Word; +begin + Inherited Load (S); + S.Read (Length, SizeOf (Length)); + if IsValid then + begin + S.Read (Buffer^[BufSize - Length], Length); + SetBufLen (Length); + end + else + S.Seek (S.GetPos + Length); +end; { TMemo.Load } + + +function TMemo.DataSize : Sw_Word; +begin + DataSize := BufSize + SizeOf (Sw_Word); +end; { TMemo.DataSize } + + +procedure TMemo.GetData (var Rec); +VAR + Data : TMemoData absolute Rec; +begin + Data.Length := BufLen; + Move (Buffer^, Data.Buffer, CurPtr); + Move (Buffer^[CurPtr + GapLen], Data.Buffer[CurPtr], BufLen - CurPtr); + FillChar (Data.Buffer[BufLen], BufSize - BufLen, 0); +end; { TMemo.GetData } + + +function TMemo.GetPalette : PPalette; +CONST + P : String[Length (CMemo)] = CMemo; +begin + GetPalette := PPalette(@P); +end; { TMemo.GetPalette } + + +procedure TMemo.HandleEvent (var Event : Drivers.TEvent); +begin + if (Event.What <> Drivers.evKeyDown) or (Event.KeyCode <> Drivers.kbTab) then + Inherited HandleEvent (Event); +end; { TMemo.HandleEvent } + + +procedure TMemo.SetData (var Rec); +VAR + Data : TMemoData absolute Rec; +begin + Move (Data.Buffer, Buffer^[BufSize - Data.Length], Data.Length); + SetBufLen (Data.Length); +end; { TMemo.SetData } + + +procedure TMemo.Store (var S : Objects.TStream); +begin + Inherited Store (S); + S.Write (BufLen, SizeOf (BufLen)); + S.Write (Buffer^, CurPtr); + S.Write (Buffer^[CurPtr + GapLen], BufLen - CurPtr); +end; { TMemo.Store } + + +{**************************************************************************** + TFILEEDITOR +****************************************************************************} + + +constructor TFileEditor.Init (var Bounds : TRect; + AHScrollBar, AVScrollBar : PScrollBar; + AIndicator : PIndicator; + AFileName : FNameStr); +begin + Inherited Init (Bounds, AHScrollBar, AVScrollBar, AIndicator, 0); + if AFileName <> '' then + begin + FileName := FExpand (AFileName); + if IsValid then + IsValid := LoadFile; + end; +end; { TFileEditor.Init } + + +constructor TFileEditor.Load (var S : Objects.TStream); +VAR + SStart,SEnd,Curs : Sw_Word; +begin + Inherited Load (S); + BufSize := 0; + S.Read (FileName[0], SizeOf (Byte)); + S.Read (Filename[1], Length (FileName)); + if IsValid then + IsValid := LoadFile; + S.Read (SStart, SizeOf (SStart)); + S.Read (SEnd, SizeOf (SEnd)); + S.Read (Curs, SizeOf (Curs)); + if IsValid and (SEnd <= BufLen) then + begin + SetSelect (SStart, SEnd, Curs = SStart); + TrackCursor (True); + end; +end; { TFileEditor.Load } + + +procedure TFileEditor.DoneBuffer; +begin + ReAllocMem(Buffer, 0); +end; { TFileEditor.DoneBuffer } + + +procedure TFileEditor.HandleEvent (var Event : Drivers.TEvent); +begin + Inherited HandleEvent (Event); + case Event.What of + Drivers.evCommand: + case Event.Command of + cmSave : Save; + cmSaveAs : SaveAs; + cmSaveDone : if Save then + Message (Owner, Drivers.evCommand, cmClose, nil); + else + Exit; + end; + else + Exit; + end; + ClearEvent (Event); +end; { TFileEditor.HandleEvent } + + +procedure TFileEditor.InitBuffer; +begin + Assert(Buffer = nil, 'TFileEditor.InitBuffer: Buffer is not nil'); + ReAllocMem(Buffer, MinBufLength); + BufSize := MinBufLength; +end; { TFileEditor.InitBuffer } + + +function TFileEditor.LoadFile: Boolean; +VAR + Length : Sw_Word; + FSize : Longint; + FRead : Sw_Integer; + F : File; +begin + LoadFile := False; + Length := 0; + Assign(F, FileName); + Reset(F, 1); + if IOResult <> 0 then + EditorDialog(edReadError, @FileName) + else + begin + FSize := FileSize(F); + if (FSize > MaxBufLength) or not SetBufSize(FSize) then + EditorDialog(edOutOfMemory, nil) + else + begin + BlockRead(F, Buffer^[BufSize-FSize], FSize, FRead); + if (IOResult <> 0) or (FRead<>FSize) then + EditorDialog(edReadError, @FileName) + else + begin + LoadFile := True; + Length := FRead; + end; + end; + Close(F); + end; + SetBufLen(Length); +end; { TFileEditor.LoadFile } + + +function TFileEditor.Save : Boolean; +begin + if FileName = '' then + Save := SaveAs + else + Save := SaveFile; +end; { TFileEditor.Save } + + +function TFileEditor.SaveAs : Boolean; +begin + SaveAs := False; + if EditorDialog (edSaveAs, @FileName) <> cmCancel then + begin + FileName := FExpand (FileName); + Message (Owner, Drivers.evBroadcast, cmUpdateTitle, nil); + SaveAs := SaveFile; + if IsClipboard then + FileName := ''; + end; +end; { TFileEditor.SaveAs } + + +function TFileEditor.SaveFile : Boolean; +VAR + F : File; + BackupName : Objects.FNameStr; + D : DOS.DirStr; + N : DOS.NameStr; + E : DOS.ExtStr; +begin + SaveFile := False; + if Flags and efBackupFiles <> 0 then + begin + FSplit (FileName, D, N, E); + BackupName := D + N + '.bak'; + Assign (F, BackupName); + Erase (F); + Assign (F, FileName); + Rename (F, BackupName); + InOutRes := 0; + end; + Assign (F, FileName); + Rewrite (F, 1); + if IOResult <> 0 then + EditorDialog (edCreateError, @FileName) + else + begin + BlockWrite (F, Buffer^, CurPtr); + BlockWrite (F, Buffer^[CurPtr + GapLen], BufLen - CurPtr); + if IOResult <> 0 then + EditorDialog (edWriteError, @FileName) + else + begin + Modified := False; + Update (ufUpdate); + SaveFile := True; + end; + Close (F); + end; +end; { TFileEditor.SaveFile } + + +function TFileEditor.SetBufSize (NewSize : Sw_Word) : Boolean; +VAR + N : Sw_Word; +begin + SetBufSize := False; + if NewSize = 0 then + NewSize := MinBufLength + else + if NewSize > (MaxBufLength-MinBufLength) then + NewSize := MaxBufLength + else + NewSize := (NewSize + (MinBufLength-1)) and (MaxBufLength and (not (MinBufLength-1))); + if NewSize <> BufSize then + begin + if NewSize > BufSize then ReAllocMem(Buffer, NewSize); + N := BufLen - CurPtr + DelCount; + Move(Buffer^[BufSize - N], Buffer^[NewSize - N], N); + if NewSize < BufSize then ReAllocMem(Buffer, NewSize); + BufSize := NewSize; + GapLen := BufSize - BufLen; + end; + SetBufSize := True; +end; { TFileEditor.SetBufSize } + + +procedure TFileEditor.Store (var S : Objects.TStream); +begin + Inherited Store (S); + S.Write (FileName, Length (FileName) + 1); + S.Write (SelStart, SizeOf (SelStart)); + S.Write (SelEnd, SizeOf (SelEnd)); + S.Write (CurPtr, SizeOf (CurPtr)); +end; { TFileEditor.Store } + + +procedure TFileEditor.UpdateCommands; +begin + Inherited UpdateCommands; + SetCmdState (cmSave, True); + SetCmdState (cmSaveAs, True); + SetCmdState (cmSaveDone, True); +end; { TFileEditor.UpdateCommands } + + +function TFileEditor.Valid (Command : Word) : Boolean; +VAR + D : Integer; +begin + if Command = cmValid then + Valid := IsValid + else + begin + Valid := True; + if Modified then + begin + if FileName = '' then + D := edSaveUntitled + else + D := edSaveModify; + case EditorDialog (D, @FileName) of + cmYes : Valid := Save; + cmNo : Modified := False; + cmCancel : Valid := False; + end; + end; + end; +end; { TFileEditor.Valid } + + +{**************************************************************************** + TEDITWINDOW +****************************************************************************} + +constructor TEditWindow.Init (var Bounds : TRect; + FileName : Objects.FNameStr; + ANumber : Integer); +var + HScrollBar : PScrollBar; + VScrollBar : PScrollBar; + Indicator : PIndicator; + R : TRect; +begin + Inherited Init (Bounds, '', ANumber); + Options := Options or ofTileable; + + R.Assign (18, Size.Y - 1, Size.X - 2, Size.Y); + HScrollBar := New (PScrollBar, Init (R)); + HScrollBar^.Hide; + Insert (HScrollBar); + + R.Assign (Size.X - 1, 1, Size.X, Size.Y - 1); + VScrollBar := New (PScrollBar, Init (R)); + VScrollBar^.Hide; + Insert (VScrollBar); + + R.Assign (2, Size.Y - 1, 16, Size.Y); + Indicator := New (PIndicator, Init (R)); + Indicator^.Hide; + Insert (Indicator); + + GetExtent (R); + R.Grow (-1, -1); + Editor := New (PFileEditor, Init (R, HScrollBar, VScrollBar, Indicator, FileName)); + Insert (Editor); +end; { TEditWindow.Init } + + +constructor TEditWindow.Load (var S : Objects.TStream); +begin + Inherited Load (S); + GetSubViewPtr (S, Editor); +end; { TEditWindow.Load } + + +procedure TEditWindow.Close; +begin + if Editor^.IsClipboard then + Hide + else + Inherited Close; +end; { TEditWindow.Close } + + +function TEditWindow.GetTitle (MaxSize : Sw_Integer) : TTitleStr; +begin + if Editor^.IsClipboard then + GetTitle := sClipboard + else + if Editor^.FileName = '' then + GetTitle := sUntitled + else + GetTitle := Editor^.FileName; +end; { TEditWindow.GetTile } + + +procedure TEditWindow.HandleEvent (var Event : Drivers.TEvent); +begin + Inherited HandleEvent (Event); + if (Event.What = Drivers.evBroadcast) then + { and (Event.Command = cmUpdateTitle) then } + { Changed if statement above so I could test for cmBlugeonStats. } + { Stats would not show up when loading a file until a key was pressed. } + case Event.Command of + cmUpdateTitle : + begin + Frame^.DrawView; + ClearEvent (Event); + end; + cmBludgeonStats : + begin + Editor^.Update (ufStats); + ClearEvent (Event); + end; + end; +end; { TEditWindow.HandleEvent } + + +procedure TEditWindow.SizeLimits(var Min, Max: TPoint); +begin + inherited SizeLimits(Min, Max); + Min.X := 23; +end; + + +procedure TEditWindow.Store (var S : Objects.TStream); +begin + Inherited Store (S); + PutSubViewPtr (S, Editor); +end; { TEditWindow.Store } + + +procedure RegisterEditors; +begin + RegisterType (REditor); + RegisterType (RMemo); + RegisterType (RFileEditor); + RegisterType (RIndicator); + RegisterType (REditWindow); +end; { RegisterEditors } + + +end. { Unit NewEdit } |
