home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / T-Pascal.70 / SOURCE.ZIP / EDITORS.PAS < prev    next >
Pascal/Delphi Source File  |  1992-10-30  |  54KB  |  2,088 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Turbo Pascal Version 7.0                        }
  5. {       Turbo Vision Unit                               }
  6. {                                                       }
  7. {       Copyright (c) 1992 Borland International        }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. unit Editors;
  12.  
  13. {$I-,O+,F+,V-,X+,S-}
  14.  
  15. interface
  16.  
  17. uses Drivers, Objects, Views, Dialogs;
  18.  
  19. const
  20.   cmFind        = 82;
  21.   cmReplace     = 83;
  22.   cmSearchAgain = 84;
  23.  
  24. const
  25.   cmCharLeft    = 500;
  26.   cmCharRight   = 501;
  27.   cmWordLeft    = 502;
  28.   cmWordRight   = 503;
  29.   cmLineStart   = 504;
  30.   cmLineEnd     = 505;
  31.   cmLineUp      = 506;
  32.   cmLineDown    = 507;
  33.   cmPageUp      = 508;
  34.   cmPageDown    = 509;
  35.   cmTextStart   = 510;
  36.   cmTextEnd     = 511;
  37.   cmNewLine     = 512;
  38.   cmBackSpace   = 513;
  39.   cmDelChar     = 514;
  40.   cmDelWord     = 515;
  41.   cmDelStart    = 516;
  42.   cmDelEnd      = 517;
  43.   cmDelLine     = 518;
  44.   cmInsMode     = 519;
  45.   cmStartSelect = 520;
  46.   cmHideSelect  = 521;
  47.   cmIndentMode  = 522;
  48.   cmUpdateTitle = 523;
  49.  
  50. const
  51.   edOutOfMemory   = 0;
  52.   edReadError     = 1;
  53.   edWriteError    = 2;
  54.   edCreateError   = 3;
  55.   edSaveModify    = 4;
  56.   edSaveUntitled  = 5;
  57.   edSaveAs        = 6;
  58.   edFind          = 7;
  59.   edSearchFailed  = 8;
  60.   edReplace       = 9;
  61.   edReplacePrompt = 10;
  62.  
  63. const
  64.   efCaseSensitive   = $0001;
  65.   efWholeWordsOnly  = $0002;
  66.   efPromptOnReplace = $0004;
  67.   efReplaceAll      = $0008;
  68.   efDoReplace       = $0010;
  69.   efBackupFiles     = $0100;
  70.  
  71. const
  72.   CIndicator = #2#3;
  73.   CEditor    = #6#7;
  74.   CMemo      = #26#27;
  75.  
  76. const
  77.   MaxLineLength = 256;
  78.  
  79. type
  80.   TEditorDialog = function(Dialog: Integer; Info: Pointer): Word;
  81.  
  82. type
  83.   PIndicator = ^TIndicator;
  84.   TIndicator = object(TView)
  85.     Location: TPoint;
  86.     Modified: Boolean;
  87.     constructor Init(var Bounds: TRect);
  88.     procedure Draw; virtual;
  89.     function GetPalette: PPalette; virtual;
  90.     procedure SetState(AState: Word; Enable: Boolean); virtual;
  91.     procedure SetValue(ALocation: TPoint; AModified: Boolean);
  92.   end;
  93.  
  94. type
  95.   PEditBuffer = ^TEditBuffer;
  96.   TEditBuffer = array[0..65519] of Char;
  97.  
  98. type
  99.   PEditor = ^TEditor;
  100.   TEditor = object(TView)
  101.     HScrollBar: PScrollBar;
  102.     VScrollBar: PScrollBar;
  103.     Indicator: PIndicator;
  104.     Buffer: PEditBuffer;
  105.     BufSize: Word;
  106.     BufLen: Word;
  107.     GapLen: Word;
  108.     SelStart: Word;
  109.     SelEnd: Word;
  110.     CurPtr: Word;
  111.     CurPos: TPoint;
  112.     Delta: TPoint;
  113.     Limit: TPoint;
  114.     DrawLine: Integer;
  115.     DrawPtr: Word;
  116.     DelCount: Word;
  117.     InsCount: Word;
  118.     IsValid: Boolean;
  119.     CanUndo: Boolean;
  120.     Modified: Boolean;
  121.     Selecting: Boolean;
  122.     Overwrite: Boolean;
  123.     AutoIndent: Boolean;
  124.     constructor Init(var Bounds: TRect;
  125.       AHScrollBar, AVScrollBar: PScrollBar;
  126.       AIndicator: PIndicator; ABufSize: Word);
  127.     constructor Load(var S: TStream);
  128.     destructor Done; virtual;
  129.     function BufChar(P: Word): Char;
  130.     function BufPtr(P: Word): Word;
  131.     procedure ChangeBounds(var Bounds: TRect); virtual;
  132.     procedure ConvertEvent(var Event: TEvent); virtual;
  133.     function CursorVisible: Boolean;
  134.     procedure DeleteSelect;
  135.     procedure DoneBuffer; virtual;
  136.     procedure Draw; virtual;
  137.     function GetPalette: PPalette; virtual;
  138.     procedure HandleEvent(var Event: TEvent); virtual;
  139.     procedure InitBuffer; virtual;
  140.     function InsertBuffer(var P: PEditBuffer; Offset, Length: Word;
  141.       AllowUndo, SelectText: Boolean): Boolean;
  142.     function InsertFrom(Editor: PEditor): Boolean; virtual;
  143.     function InsertText(Text: Pointer; Length: Word;
  144.       SelectText: Boolean): Boolean;
  145.     procedure ScrollTo(X, Y: Integer);
  146.     function Search(const FindStr: String; Opts: Word): Boolean;
  147.     function SetBufSize(NewSize: Word): Boolean; virtual;
  148.     procedure SetCmdState(Command: Word; Enable: Boolean);
  149.     procedure SetSelect(NewStart, NewEnd: Word; CurStart: Boolean);
  150.     procedure SetState(AState: Word; Enable: Boolean); virtual;
  151.     procedure Store(var S: TStream);
  152.     procedure TrackCursor(Center: Boolean);
  153.     procedure Undo;
  154.     procedure UpdateCommands; virtual;
  155.     function Valid(Command: Word): Boolean; virtual;
  156.   private
  157.     LockCount: Byte;
  158.     UpdateFlags: Byte;
  159.     KeyState: Integer;
  160.     function CharPos(P, Target: Word): Integer;
  161.     function CharPtr(P: Word; Target: Integer): Word;
  162.     function ClipCopy: Boolean;
  163.     procedure ClipCut;
  164.     procedure ClipPaste;
  165.     procedure DeleteRange(StartPtr, EndPtr: Word; DelSelect: Boolean);
  166.     procedure DoUpdate;
  167.     procedure DoSearchReplace;
  168.     procedure DrawLines(Y, Count: Integer; LinePtr: Word);
  169.     procedure FormatLine(var DrawBuf; LinePtr: Word;
  170.       Width: Integer; Colors: Word);
  171.     procedure Find;
  172.     function GetMousePtr(Mouse: TPoint): Word;
  173.     function HasSelection: Boolean;
  174.     procedure HideSelect;
  175.     function IsClipboard: Boolean;
  176.     function LineEnd(P: Word): Word;
  177.     function LineMove(P: Word; Count: Integer): Word;
  178.     function LineStart(P: Word): Word;
  179.     procedure Lock;
  180.     procedure NewLine;
  181.     function NextChar(P: Word): Word;
  182.     function NextLine(P: Word): Word;
  183.     function NextWord(P: Word): Word;
  184.     function PrevChar(P: Word): Word;
  185.     function PrevLine(P: Word): Word;
  186.     function PrevWord(P: Word): Word;
  187.     procedure Replace;
  188.     procedure SetBufLen(Length: Word);
  189.     procedure SetCurPtr(P: Word; SelectMode: Byte);
  190.     procedure StartSelect;
  191.     procedure ToggleInsMode;
  192.     procedure Unlock;
  193.     procedure Update(AFlags: Byte);
  194.   end;
  195.  
  196. type
  197.   TMemoData = record
  198.     Length: Word;
  199.     Buffer: TEditBuffer;
  200.   end;
  201.  
  202. type
  203.   PMemo = ^TMemo;
  204.   TMemo = object(TEditor)
  205.     constructor Load(var S: TStream);
  206.     function DataSize: Word; virtual;
  207.     procedure GetData(var Rec); virtual;
  208.     function GetPalette: PPalette; virtual;
  209.     procedure HandleEvent(var Event: TEvent); virtual;
  210.     procedure SetData(var Rec); virtual;
  211.     procedure Store(var S: TStream);
  212.   end;
  213.  
  214. type
  215.   PFileEditor = ^TFileEditor;
  216.   TFileEditor = object(TEditor)
  217.     FileName: FNameStr;
  218.     constructor Init(var Bounds: TRect;
  219.       AHScrollBar, AVScrollBar: PScrollBar;
  220.       AIndicator: PIndicator; AFileName: FNameStr);
  221.     constructor Load(var S: TStream);
  222.     procedure DoneBuffer; virtual;
  223.     procedure HandleEvent(var Event: TEvent); virtual;
  224.     procedure InitBuffer; virtual;
  225.     function LoadFile: Boolean;
  226.     function Save: Boolean;
  227.     function SaveAs: Boolean;
  228.     function SaveFile: Boolean;
  229.     function SetBufSize(NewSize: Word): Boolean; virtual;
  230.     procedure Store(var S: TStream);
  231.     procedure UpdateCommands; virtual;
  232.     function Valid(Command: Word): Boolean; virtual;
  233.   end;
  234.  
  235. type
  236.   PEditWindow = ^TEditWindow;
  237.   TEditWindow = object(TWindow)
  238.     Editor: PFileEditor;
  239.     constructor Init(var Bounds: TRect;
  240.       FileName: FNameStr; ANumber: Integer);
  241.     constructor Load(var S: TStream);
  242.     procedure Close; virtual;
  243.     function GetTitle(MaxSize: Integer): TTitleStr; virtual;
  244.     procedure HandleEvent(var Event: TEvent); virtual;
  245.     procedure SizeLimits(var Min, Max: TPoint); virtual;
  246.     procedure Store(var S: TStream);
  247.   end;
  248.  
  249. function DefEditorDialog(Dialog: Integer; Info: Pointer): Word;
  250. function CreateFindDialog: PDialog;
  251. function CreateReplaceDialog: PDialog;
  252. function StdEditorDialog(Dialog: Integer; Info: Pointer): Word;
  253.  
  254. const
  255.   WordChars: set of Char = ['0'..'9', 'A'..'Z', '_', 'a'..'z'];
  256.   EditorDialog: TEditorDialog = DefEditorDialog;
  257.   EditorFlags: Word = efBackupFiles + efPromptOnReplace;
  258.   FindStr: String[80] = '';
  259.   ReplaceStr: String[80] = '';
  260.   Clipboard: PEditor = nil;
  261.  
  262. type
  263.   TFindDialogRec = record
  264.     Find: String[80];
  265.     Options: Word;
  266.   end;
  267.  
  268. type
  269.   TReplaceDialogRec = record
  270.     Find: String[80];
  271.     Replace: String[80];
  272.     Options: Word;
  273.   end;
  274.  
  275. const
  276.   REditor: TStreamRec = (
  277.     ObjType: 70;
  278.     VmtLink: Ofs(TypeOf(TEditor)^);
  279.     Load: @TEditor.Load;
  280.     Store: @TEditor.Store
  281.   );
  282.   RMemo: TStreamRec = (
  283.     ObjType: 71;
  284.     VmtLink: Ofs(TypeOf(TMemo)^);
  285.     Load: @TMemo.Load;
  286.     Store: @TMemo.Store
  287.   );
  288.   RFileEditor: TStreamRec = (
  289.     ObjType: 72;
  290.     VmtLink: Ofs(TypeOf(TFileEditor)^);
  291.     Load: @TFileEditor.Load;
  292.     Store: @TFileEditor.Store
  293.   );
  294.   RIndicator: TStreamRec = (
  295.     ObjType: 73;
  296.     VmtLink: Ofs(TypeOf(TIndicator)^);
  297.     Load: @TIndicator.Load;
  298.     Store: @TIndicator.Store
  299.   );
  300.   REditWindow: TStreamRec = (
  301.     ObjType: 74;
  302.     VmtLink: Ofs(TypeOf(TEditWindow)^);
  303.     Load: @TEditWindow.Load;
  304.     Store: @TEditWindow.Store
  305.   );
  306.  
  307. procedure RegisterEditors;
  308.  
  309. implementation
  310.  
  311. uses Memory, Dos, App, StdDlg, MsgBox;
  312.  
  313. const
  314.   ufUpdate = $01;
  315.   ufLine   = $02;
  316.   ufView   = $04;
  317.  
  318. const
  319.   smExtend = $01;
  320.   smDouble = $02;
  321.  
  322. const
  323.   sfSearchFailed = $FFFF;
  324.  
  325. const
  326.   FirstKeys: array[0..37 * 2] of Word = (37,
  327.     Ord(^A), cmWordLeft, Ord(^C), cmPageDown,
  328.     Ord(^D), cmCharRight, Ord(^E), cmLineUp,
  329.     Ord(^F), cmWordRight, Ord(^G), cmDelChar,
  330.     Ord(^H), cmBackSpace, Ord(^K), $FF02,
  331.     Ord(^L), cmSearchAgain, Ord(^M), cmNewLine,
  332.     Ord(^O), cmIndentMode, Ord(^Q), $FF01,
  333.     Ord(^R), cmPageUp, Ord(^S), cmCharLeft,
  334.     Ord(^T), cmDelWord, Ord(^U), cmUndo,
  335.     Ord(^V), cmInsMode, Ord(^X), cmLineDown,
  336.     Ord(^Y), cmDelLine, kbLeft, cmCharLeft,
  337.     kbRight, cmCharRight, kbCtrlLeft, cmWordLeft,
  338.     kbCtrlRight, cmWordRight, kbHome, cmLineStart,
  339.     kbEnd, cmLineEnd, kbUp, cmLineUp,
  340.     kbDown, cmLineDown, kbPgUp, cmPageUp,
  341.     kbPgDn, cmPageDown, kbCtrlPgUp, cmTextStart,
  342.     kbCtrlPgDn, cmTextEnd, kbIns, cmInsMode,
  343.     kbDel, cmDelChar, kbShiftIns, cmPaste,
  344.     kbShiftDel, cmCut, kbCtrlIns, cmCopy,
  345.     kbCtrlDel, cmClear);
  346.   QuickKeys: array[0..8 * 2] of Word = (8,
  347.     Ord('A'), cmReplace, Ord('C'), cmTextEnd,
  348.     Ord('D'), cmLineEnd, Ord('F'), cmFind,
  349.     Ord('H'), cmDelStart, Ord('R'), cmTextStart,
  350.     Ord('S'), cmLineStart, Ord('Y'), cmDelEnd);
  351.   BlockKeys: array[0..5 * 2] of Word = (5,
  352.     Ord('B'), cmStartSelect, Ord('C'), cmPaste,
  353.     Ord('H'), cmHideSelect, Ord('K'), cmCopy,
  354.     Ord('Y'), cmCut);
  355.   KeyMap: array[0..2] of Pointer = (@FirstKeys, @QuickKeys, @BlockKeys);
  356.  
  357. function DefEditorDialog(Dialog: Integer; Info: Pointer): Word;
  358. begin
  359.   DefEditorDialog := cmCancel;
  360. end;
  361.  
  362. function CreateFindDialog: PDialog;
  363. var
  364.   D: PDialog;
  365.   Control: PView;
  366.   R: TRect;
  367. begin
  368.   R.Assign(0, 0, 38, 12);
  369.   D := New(PDialog, Init(R, 'Find'));
  370.   with D^ do
  371.   begin
  372.     Options := Options or ofCentered;
  373.  
  374.     R.Assign(3, 3, 32, 4);
  375.     Control := New(PInputLine, Init(R, 80));
  376.     Insert(Control);
  377.     R.Assign(2, 2, 15, 3);
  378.     Insert(New(PLabel, Init(R, '~T~ext to find', Control)));
  379.     R.Assign(32, 3, 35, 4);
  380.     Insert(New(PHistory, Init(R, PInputLine(Control), 10)));
  381.  
  382.     R.Assign(3, 5, 35, 7);
  383.     Insert(New(PCheckBoxes, Init(R,
  384.       NewSItem('~C~ase sensitive',
  385.       NewSItem('~W~hole words only', nil)))));
  386.  
  387.     R.Assign(14, 9, 24, 11);
  388.     Insert(New(PButton, Init(R, 'O~K~', cmOk, bfDefault)));
  389.     Inc(R.A.X, 12); Inc(R.B.X, 12);
  390.     Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal)));
  391.  
  392.     SelectNext(False);
  393.   end;
  394.   CreateFindDialog := D;
  395. end;
  396.  
  397. function CreateReplaceDialog: PDialog;
  398. var
  399.   D: PDialog;
  400.   Control: PView;
  401.   R: TRect;
  402. begin
  403.   R.Assign(0, 0, 40, 16);
  404.   D := New(PDialog, Init(R, 'Replace'));
  405.   with D^ do
  406.   begin
  407.     Options := Options or ofCentered;
  408.  
  409.     R.Assign(3, 3, 34, 4);
  410.     Control := New(PInputLine, Init(R, 80));
  411.     Insert(Control);
  412.     R.Assign(2, 2, 15, 3);
  413.     Insert(New(PLabel, Init(R, '~T~ext to find', Control)));
  414.     R.Assign(34, 3, 37, 4);
  415.     Insert(New(PHistory, Init(R, PInputLine(Control), 10)));
  416.  
  417.     R.Assign(3, 6, 34, 7);
  418.     Control := New(PInputLine, Init(R, 80));
  419.     Insert(Control);
  420.     R.Assign(2, 5, 12, 6);
  421.     Insert(New(PLabel, Init(R, '~N~ew text', Control)));
  422.     R.Assign(34, 6, 37, 7);
  423.     Insert(New(PHistory, Init(R, PInputLine(Control), 11)));
  424.  
  425.     R.Assign(3, 8, 37, 12);
  426.     Insert(New(PCheckBoxes, Init(R,
  427.       NewSItem('~C~ase sensitive',
  428.       NewSItem('~W~hole words only',
  429.       NewSItem('~P~rompt on replace',
  430.       NewSItem('~R~eplace all', nil)))))));
  431.  
  432.     R.Assign(17, 13, 27, 15);
  433.     Insert(New(PButton, Init(R, 'O~K~', cmOk, bfDefault)));
  434.     R.Assign(28, 13, 38, 15);
  435.     Insert(New(PButton, Init(R, 'Cancel', cmCancel, bfNormal)));
  436.  
  437.     SelectNext(False);
  438.   end;
  439.   CreateReplaceDialog := D;
  440. end;
  441.  
  442. function StdEditorDialog(Dialog: Integer; Info: Pointer): Word;
  443. var
  444.   R: TRect;
  445.   T: TPoint;
  446. begin
  447.   case Dialog of
  448.     edOutOfMemory:
  449.       StdEditorDialog := MessageBox('Not enough memory for this operation.',
  450.         nil, mfError + mfOkButton);
  451.     edReadError:
  452.       StdEditorDialog := MessageBox('Error reading file %s.',
  453.         @Info, mfError + mfOkButton);
  454.     edWriteError:
  455.       StdEditorDialog := MessageBox('Error writing file %s.',
  456.         @Info, mfError + mfOkButton);
  457.     edCreateError:
  458.       StdEditorDialog := MessageBox('Error creating file %s.',
  459.         @Info, mfError + mfOkButton);
  460.     edSaveModify:
  461.       StdEditorDialog := MessageBox('%s has been modified. Save?',
  462.         @Info, mfInformation + mfYesNoCancel);
  463.     edSaveUntitled:
  464.       StdEditorDialog := MessageBox('Save untitled file?',
  465.         nil, mfInformation + mfYesNoCancel);
  466.     edSaveAs:
  467.       StdEditorDialog :=
  468.         Application^.ExecuteDialog(New(PFileDialog, Init('*.*',
  469.         'Save file as', '~N~ame', fdOkButton, 101)), Info);
  470.     edFind:
  471.       StdEditorDialog :=
  472.         Application^.ExecuteDialog(CreateFindDialog, Info);
  473.     edSearchFailed:
  474.       StdEditorDialog := MessageBox('Search string not found.',
  475.         nil, mfError + mfOkButton);
  476.     edReplace:
  477.       StdEditorDialog :=
  478.         Application^.ExecuteDialog(CreateReplaceDialog, Info);
  479.     edReplacePrompt:
  480.       begin
  481.         { Avoid placing the dialog on the same line as the cursor }
  482.         R.Assign(0, 1, 40, 8);
  483.         R.Move((Desktop^.Size.X - R.B.X) div 2, 0);
  484.         Desktop^.MakeGlobal(R.B, T);
  485.         Inc(T.Y);
  486.         if TPoint(Info).Y <= T.Y then
  487.           R.Move(0, Desktop^.Size.Y - R.B.Y - 2);
  488.         StdEditorDialog := MessageBoxRect(R, 'Replace this occurence?',
  489.           nil, mfYesNoCancel + mfInformation);
  490.       end;
  491.   end;
  492. end;
  493.  
  494. function Min(X, Y: Integer): Integer; near; assembler;
  495. asm
  496.         MOV     AX,X
  497.         CMP     AX,Y
  498.         JLE     @@1
  499.         MOV     AX,Y
  500. @@1:
  501. end;
  502.  
  503. function Max(X, Y: Integer): Integer; near; assembler;
  504. asm
  505.         MOV     AX,X
  506.         CMP     AX,Y
  507.         JGE     @@1
  508.         MOV     AX,Y
  509. @@1:
  510. end;
  511.  
  512. function MinWord(X, Y: Word): Word; near; assembler;
  513. asm
  514.         MOV     AX,X
  515.         CMP     AX,Y
  516.         JBE     @@1
  517.         MOV     AX,Y
  518. @@1:
  519. end;
  520.  
  521. function MaxWord(X, Y: Word): Word; near; assembler;
  522. asm
  523.         MOV     AX,X
  524.         CMP     AX,Y
  525.         JAE     @@1
  526.         MOV     AX,Y
  527. @@1:
  528. end;
  529.  
  530. function CountLines(var Buf; Count: Word): Integer; near; assembler;
  531. asm
  532.         LES     DI,Buf
  533.         MOV     CX,Count
  534.         XOR     DX,DX
  535.         MOV     AL,0DH
  536.         CLD
  537. @@1:    JCXZ    @@2
  538.         REPNE   SCASB
  539.         JNE     @@2
  540.         INC     DX
  541.         JMP     @@1
  542. @@2:    MOV     AX,DX
  543. end;
  544.  
  545. function ScanKeyMap(KeyMap: Pointer; KeyCode: Word): Word; near; assembler;
  546. asm
  547.         PUSH    DS
  548.         LDS     SI,KeyMap
  549.         MOV     DX,KeyCode
  550.         CLD
  551.         LODSW
  552.         MOV     CX,AX
  553. @@1:    LODSW
  554.         MOV     BX,AX
  555.         LODSW
  556.         CMP     BL,DL
  557.         JNE     @@3
  558.         OR      BH,BH
  559.         JE      @@4
  560.         CMP     BH,DH
  561.         JE      @@4
  562. @@3:    LOOP    @@1
  563.         XOR     AX,AX
  564. @@4:    POP     DS
  565. end;
  566.  
  567. function Scan(var Block; Size: Word; Str: String): Word; near; assembler;
  568. asm
  569.         PUSH    DS
  570.         LES     DI,Block
  571.         LDS     SI,Str
  572.         MOV     CX,Size
  573.         JCXZ    @@3
  574.         CLD
  575.         LODSB
  576.         CMP     AL,1
  577.         JB      @@5
  578.         JA      @@1
  579.         LODSB
  580.         REPNE   SCASB
  581.         JNE     @@3
  582.         JMP     @@5
  583. @@1:    XOR     AH,AH
  584.         MOV     BX,AX
  585.         DEC     BX
  586.         MOV     DX,CX
  587.         SUB     DX,AX
  588.         JB      @@3
  589.         LODSB
  590.         INC     DX
  591.         INC     DX
  592. @@2:    DEC     DX
  593.         MOV     CX,DX
  594.         REPNE   SCASB
  595.         JNE     @@3
  596.         MOV     DX,CX
  597.         MOV     CX,BX
  598.         REP     CMPSB
  599.         JE      @@4
  600.         SUB     CX,BX
  601.         ADD     SI,CX
  602.         ADD     DI,CX
  603.         INC     DI
  604.         OR      DX,DX
  605.         JNE     @@2
  606. @@3:    XOR     AX,AX
  607.         JMP     @@6
  608. @@4:    SUB     DI,BX
  609. @@5:    MOV     AX,DI
  610.         SUB     AX,WORD PTR Block
  611. @@6:    DEC     AX
  612.         POP     DS
  613. end;
  614.  
  615. function IScan(var Block; Size: Word; Str: String): Word; near; assembler;
  616. var
  617.   S: String;
  618. asm
  619.         PUSH    DS
  620.         MOV     AX,SS
  621.         MOV     ES,AX
  622.         LEA     DI,S
  623.         LDS     SI,Str
  624.         XOR     AH,AH
  625.         LODSB
  626.         STOSB
  627.         MOV     CX,AX
  628.         MOV     BX,AX
  629.         JCXZ    @@9
  630. @@1:    LODSB
  631.         CMP     AL,'a'
  632.         JB      @@2
  633.         CMP     AL,'z'
  634.         JA      @@2
  635.         SUB     AL,20H
  636. @@2:    STOSB
  637.         LOOP    @@1
  638.         SUB     DI,BX
  639.         LDS     SI,Block
  640.         MOV     CX,Size
  641.         JCXZ    @@8
  642.         CLD
  643.         SUB     CX,BX
  644.         JB      @@8
  645.         INC     CX
  646. @@4:    MOV     AH,ES:[DI]
  647.         AND     AH,$DF
  648. @@5:    LODSB
  649.         AND     AL,$DF
  650.         CMP     AL,AH
  651.         LOOPNE  @@5
  652.         JNE     @@8
  653.         DEC     SI
  654.         MOV     DX,CX
  655.         MOV     CX,BX
  656. @@6:    REPE    CMPSB
  657.         JE      @@10
  658.         MOV     AL,DS:[SI-1]
  659.         CMP     AL,'a'
  660.         JB      @@7
  661.         CMP     AL,'z'
  662.         JA      @@7
  663.         SUB     AL,20H
  664. @@7:    CMP     AL,ES:[DI-1]
  665.         JE      @@6
  666.         SUB     CX,BX
  667.         ADD     SI,CX
  668.         ADD     DI,CX
  669.         INC     SI
  670.         MOV     CX,DX
  671.         OR      CX,CX
  672.         JNE     @@4
  673. @@8:    XOR     AX,AX
  674.         JMP     @@11
  675. @@9:    MOV     AX, 1
  676.         JMP     @@11
  677. @@10:   SUB     SI,BX
  678.         MOV     AX,SI
  679.         SUB     AX,WORD PTR Block
  680.         INC     AX
  681. @@11:   DEC     AX
  682.         POP     DS
  683. end;
  684.  
  685. { TIndicator }
  686.  
  687. constructor TIndicator.Init(var Bounds: TRect);
  688. var
  689.   R: TRect;
  690. begin
  691.   inherited Init(Bounds);
  692.   GrowMode := gfGrowLoY + gfGrowHiY;
  693. end;
  694.  
  695. procedure TIndicator.Draw;
  696. var
  697.   Color: Byte;
  698.   Frame: Char;
  699.   L: array[0..1] of Longint;
  700.   S: String[15];
  701.   B: TDrawBuffer;
  702. begin
  703.   if State and sfDragging = 0 then
  704.   begin
  705.     Color := GetColor(1);
  706.     Frame := #205;
  707.   end else
  708.   begin
  709.     Color := GetColor(2);
  710.     Frame := #196;
  711.   end;
  712.   MoveChar(B, Frame, Color, Size.X);
  713.   if Modified then WordRec(B[0]).Lo := 15;
  714.   L[0] := Location.Y + 1;
  715.   L[1] := Location.X + 1;
  716.   FormatStr(S, ' %d:%d ', L);
  717.   MoveStr(B[8 - Pos(':', S)], S, Color);
  718.   WriteBuf(0, 0, Size.X, 1, B);
  719. end;
  720.  
  721. function TIndicator.GetPalette: PPalette;
  722. const
  723.   P: string[Length(CIndicator)] = CIndicator;
  724. begin
  725.   GetPalette := @P;
  726. end;
  727.  
  728. procedure TIndicator.SetState(AState: Word; Enable: Boolean);
  729. begin
  730.   inherited SetState(AState, Enable);
  731.   if AState = sfDragging then DrawView;
  732. end;
  733.  
  734. procedure TIndicator.SetValue(ALocation: TPoint; AModified: Boolean);
  735. begin
  736.   if (Longint(Location) <> Longint(ALocation)) or
  737.     (Modified <> AModified) then
  738.   begin
  739.     Location := ALocation;
  740.     Modified := AModified;
  741.     DrawView;
  742.   end;
  743. end;
  744.  
  745. { TEditor }
  746.  
  747. constructor TEditor.Init(var Bounds: TRect;
  748.   AHScrollBar, AVScrollBar: PScrollBar;
  749.   AIndicator: PIndicator; ABufSize: Word);
  750. begin
  751.   inherited Init(Bounds);
  752.   GrowMode := gfGrowHiX + gfGrowHiY;
  753.   Options := Options or ofSelectable;
  754.   EventMask := evMouseDown + evKeyDown + evCommand + evBroadcast;
  755.   ShowCursor;
  756.   HScrollBar := AHScrollBar;
  757.   VScrollBar := AVScrollBar;
  758.   Indicator := AIndicator;
  759.   BufSize := ABufSize;
  760.   CanUndo := True;
  761.   InitBuffer;
  762.   if Buffer <> nil then IsValid := True else
  763.   begin
  764.     EditorDialog(edOutOfMemory, nil);
  765.     BufSize := 0;
  766.   end;
  767.   SetBufLen(0);
  768. end;
  769.  
  770. constructor TEditor.Load(var S: TStream);
  771. begin
  772.   inherited Load(S);
  773.   GetPeerViewPtr(S, HScrollBar);
  774.   GetPeerViewPtr(S, VScrollBar);
  775.   GetPeerViewPtr(S, Indicator);
  776.   S.Read(BufSize, SizeOf(Word));
  777.   S.Read(CanUndo, SizeOf(Boolean));
  778.   InitBuffer;
  779.   if Buffer <> nil then IsValid := True else
  780.   begin
  781.     EditorDialog(edOutOfMemory, nil);
  782.     BufSize := 0;
  783.   end;
  784.   Lock;
  785.   SetBufLen(0);
  786. end;
  787.  
  788. destructor TEditor.Done;
  789. begin
  790.   DoneBuffer;
  791.   inherited Done;
  792. end;
  793.  
  794. function TEditor.BufChar(P: Word): Char; assembler;
  795. asm
  796.         LES     DI,Self
  797.         MOV     BX,P
  798.         CMP     BX,ES:[DI].TEditor.CurPtr
  799.         JB      @@1
  800.         ADD     BX,ES:[DI].TEditor.GapLen
  801. @@1:    LES     DI,ES:[DI].TEditor.Buffer
  802.         MOV     AL,ES:[DI+BX]
  803. end;
  804.  
  805. function TEditor.BufPtr(P: Word): Word; assembler;
  806. asm
  807.         LES     DI,Self
  808.         MOV     AX,P
  809.         CMP     AX,ES:[DI].TEditor.CurPtr
  810.         JB      @@1
  811.         ADD     AX,ES:[DI].TEditor.GapLen
  812. @@1:
  813. end;
  814.  
  815. procedure TEditor.ChangeBounds(var Bounds: TRect);
  816. begin
  817.   SetBounds(Bounds);
  818.   Delta.X := Max(0, Min(Delta.X, Limit.X - Size.X));
  819.   Delta.Y := Max(0, Min(Delta.Y, Limit.Y - Size.Y));
  820.   Update(ufView);
  821. end;
  822.  
  823. function TEditor.CharPos(P, Target: Word): Integer;
  824. var
  825.   Pos: Integer;
  826. begin
  827.   Pos := 0;
  828.   while P < Target do
  829.   begin
  830.     if BufChar(P) = #9 then Pos := Pos or 7;
  831.     Inc(Pos);
  832.     Inc(P);
  833.   end;
  834.   CharPos := Pos;
  835. end;
  836.  
  837. function TEditor.CharPtr(P: Word; Target: Integer): Word;
  838. var
  839.   Pos: Integer;
  840. begin
  841.   Pos := 0;
  842.   while (Pos < Target) and (P < BufLen) and (BufChar(P) <> #13) do
  843.   begin
  844.     if BufChar(P) = #9 then Pos := Pos or 7;
  845.     Inc(Pos);
  846.     Inc(P);
  847.   end;
  848.   if Pos > Target then Dec(P);
  849.   CharPtr := P;
  850. end;
  851.  
  852. function TEditor.ClipCopy: Boolean;
  853. begin
  854.   ClipCopy := False;
  855.   if (Clipboard <> nil) and (Clipboard <> @Self) then
  856.   begin
  857.     ClipCopy := Clipboard^.InsertFrom(@Self);
  858.     Selecting := False;
  859.     Update(ufUpdate);
  860.   end;
  861. end;
  862.  
  863. procedure TEditor.ClipCut;
  864. begin
  865.   if ClipCopy then DeleteSelect;
  866. end;
  867.  
  868. procedure TEditor.ClipPaste;
  869. begin
  870.   if (Clipboard <> nil) and (Clipboard <> @Self) then InsertFrom(Clipboard);
  871. end;
  872.  
  873. procedure TEditor.ConvertEvent(var Event: TEvent);
  874. var
  875.   ShiftState: Byte absolute $40:$17;
  876.   Key: Word;
  877. begin
  878.   if Event.What = evKeyDown then
  879.   begin
  880.     if (ShiftState and $03 <> 0) and
  881.       (Event.ScanCode >= $47) and (Event.ScanCode <= $51) then
  882.       Event.CharCode := #0;
  883.     Key := Event.KeyCode;
  884.     if KeyState <> 0 then
  885.     begin
  886.       if (Lo(Key) >= $01) and (Lo(Key) <= $1A) then Inc(Key, $40);
  887.       if (Lo(Key) >= $61) and (Lo(Key) <= $7A) then Dec(Key, $20);
  888.     end;
  889.     Key := ScanKeyMap(KeyMap[KeyState], Key);
  890.     KeyState := 0;
  891.     if Key <> 0 then
  892.       if Hi(Key) = $FF then
  893.       begin
  894.         KeyState := Lo(Key);
  895.         ClearEvent(Event);
  896.       end else
  897.       begin
  898.         Event.What := evCommand;
  899.         Event.Command := Key;
  900.       end;
  901.   end;
  902. end;
  903.  
  904. function TEditor.CursorVisible: Boolean;
  905. begin
  906.   CursorVisible := (CurPos.Y >= Delta.Y) and (CurPos.Y < Delta.Y + Size.Y);
  907. end;
  908.  
  909. procedure TEditor.DeleteRange(StartPtr, EndPtr: Word; DelSelect: Boolean);
  910. begin
  911.   if HasSelection and DelSelect then DeleteSelect else
  912.   begin
  913.     SetSelect(CurPtr, EndPtr, True);
  914.     DeleteSelect;
  915.     SetSelect(StartPtr, CurPtr, False);
  916.     DeleteSelect;
  917.   end;
  918. end;
  919.  
  920. procedure TEditor.DeleteSelect;
  921. begin
  922.   InsertText(nil, 0, False);
  923. end;
  924.  
  925. procedure TEditor.DoneBuffer;
  926. begin
  927.   if Buffer <> nil then
  928.   begin
  929.     FreeMem(Buffer, BufSize);
  930.     Buffer := nil;
  931.   end;
  932. end;
  933.  
  934. procedure TEditor.DoSearchReplace;
  935. var
  936.   I: Word;
  937.   C: TPoint;
  938. begin
  939.   repeat
  940.     I := cmCancel;
  941.     if not Search(FindStr, EditorFlags) then
  942.     begin
  943.       if EditorFlags and (efReplaceAll + efDoReplace) <>
  944.           (efReplaceAll + efDoReplace) then
  945.         EditorDialog(edSearchFailed, nil)
  946.     end
  947.     else if EditorFlags and efDoReplace <> 0 then
  948.     begin
  949.       I := cmYes;
  950.       if EditorFlags and efPromptOnReplace <> 0 then
  951.       begin
  952.         MakeGlobal(Cursor, C);
  953.         I := EditorDialog(edReplacePrompt, Pointer(C));
  954.       end;
  955.       if I = cmYes then
  956.       begin
  957.         Lock;
  958.         InsertText(@ReplaceStr[1], Length(ReplaceStr), False);
  959.         TrackCursor(False);
  960.         Unlock;
  961.       end;
  962.     end;
  963.   until (I = cmCancel) or (EditorFlags and efReplaceAll = 0);
  964. end;
  965.  
  966. procedure TEditor.DoUpdate;
  967. begin
  968.   if UpdateFlags <> 0 then
  969.   begin
  970.     SetCursor(CurPos.X - Delta.X, CurPos.Y - Delta.Y);
  971.     if UpdateFlags and ufView <> 0 then DrawView else
  972.       if UpdateFlags and ufLine <> 0 then
  973.         DrawLines(CurPos.Y - Delta.Y, 1, LineStart(CurPtr));
  974.     if HScrollBar <> nil then
  975.       HScrollBar^.SetParams(Delta.X, 0, Limit.X - Size.X, Size.X div 2, 1);
  976.     if VScrollBar <> nil then
  977.       VScrollBar^.SetParams(Delta.Y, 0, Limit.Y - Size.Y, Size.Y - 1, 1);
  978.     if Indicator <> nil then Indicator^.SetValue(CurPos, Modified);
  979.     if State and sfActive <> 0 then UpdateCommands;
  980.     UpdateFlags := 0;
  981.   end;
  982. end;
  983.  
  984. procedure TEditor.Draw;
  985. begin
  986.   if DrawLine <> Delta.Y then
  987.   begin
  988.     DrawPtr := LineMove(DrawPtr, Delta.Y - DrawLine);
  989.     DrawLine := Delta.Y;
  990.   end;
  991.   DrawLines(0, Size.Y, DrawPtr);
  992. end;
  993.  
  994. procedure TEditor.DrawLines(Y, Count: Integer; LinePtr: Word);
  995. var
  996.   Color: Word;
  997.   B: array[0..MaxLineLength - 1] of Word;
  998. begin
  999.   Color := GetColor($0201);
  1000.   while Count > 0 do
  1001.   begin
  1002.     FormatLine(B, LinePtr, Delta.X + Size.X, Color);
  1003.     WriteBuf(0, Y, Size.X, 1, B[Delta.X]);
  1004.     LinePtr := NextLine(LinePtr);
  1005.     Inc(Y);
  1006.     Dec(Count);
  1007.   end;
  1008. end;
  1009.  
  1010. procedure TEditor.Find;
  1011. var
  1012.   FindRec: TFindDialogRec;
  1013. begin
  1014.   with FindRec do
  1015.   begin
  1016.     Find := FindStr;
  1017.     Options := EditorFlags;
  1018.     if EditorDialog(edFind, @FindRec) <> cmCancel then
  1019.     begin
  1020.       FindStr := Find;
  1021.       EditorFlags := Options and not efDoReplace;
  1022.       DoSearchReplace;
  1023.     end;
  1024.   end;
  1025. end;
  1026.  
  1027. procedure TEditor.FormatLine(var DrawBuf; LinePtr: Word;
  1028.   Width: Integer; Colors: Word); assembler;
  1029. asm
  1030.         PUSH    DS
  1031.         LDS     BX,Self
  1032.         LES     DI,DrawBuf
  1033.         MOV     SI,LinePtr
  1034.         XOR     DX,DX
  1035.         CLD
  1036.         MOV     AH,Colors.Byte[0]
  1037.         MOV     CX,DS:[BX].TEditor.SelStart
  1038.         CALL    @@10
  1039.         MOV     AH,Colors.Byte[1]
  1040.         MOV     CX,DS:[BX].TEditor.CurPtr
  1041.         CALL    @@10
  1042.         ADD     SI,DS:[BX].TEditor.GapLen
  1043.         MOV     CX,DS:[BX].TEditor.SelEnd
  1044.         ADD     CX,DS:[BX].TEditor.GapLen
  1045.         CALL    @@10
  1046.         MOV     AH,Colors.Byte[0]
  1047.         MOV     CX,DS:[BX].TEditor.BufSize
  1048.         CALL    @@10
  1049.         JMP     @@31
  1050. @@10:   SUB     CX,SI
  1051.         JA      @@11
  1052.         RETN
  1053. @@11:   LDS     BX,DS:[BX].TEditor.Buffer
  1054.         ADD     SI,BX
  1055.         MOV     BX,Width
  1056. @@12:   LODSB
  1057.         CMP     AL,' '
  1058.         JB      @@20
  1059. @@13:   STOSW
  1060.         INC     DX
  1061. @@14:   CMP     DX,BX
  1062.         JAE     @@30
  1063.         LOOP    @@12
  1064.         LDS     BX,Self
  1065.         SUB     SI,DS:[BX].TEditor.Buffer.Word[0]
  1066.         RETN
  1067. @@20:   CMP     AL,0DH
  1068.         JE      @@30
  1069.         CMP     AL,09H
  1070.         JNE     @@13
  1071.         MOV     AL,' '
  1072. @@21:   STOSW
  1073.         INC     DX
  1074.         TEST    DL,7
  1075.         JNE     @@21
  1076.         JMP     @@14
  1077. @@30:   POP     CX
  1078. @@31:   MOV     AL,' '
  1079.         MOV     CX,Width
  1080.         SUB     CX,DX
  1081.         JBE     @@32
  1082.         REP     STOSW
  1083. @@32:   POP     DS
  1084. end;
  1085.  
  1086. function TEditor.GetMousePtr(Mouse: TPoint): Word;
  1087. begin
  1088.   MakeLocal(Mouse, Mouse);
  1089.   Mouse.X := Max(0, Min(Mouse.X, Size.X - 1));
  1090.   Mouse.Y := Max(0, Min(Mouse.Y, Size.Y - 1));
  1091.   GetMousePtr := CharPtr(LineMove(DrawPtr, Mouse.Y + Delta.Y - DrawLine),
  1092.     Mouse.X + Delta.X);
  1093. end;
  1094.  
  1095. function TEditor.GetPalette: PPalette;
  1096. const
  1097.   P: String[Length(CEditor)] = CEditor;
  1098. begin
  1099.   GetPalette := @P;
  1100. end;
  1101.  
  1102. procedure TEditor.HandleEvent(var Event: TEvent);
  1103. var
  1104.   ShiftState: Byte absolute $40:$17;
  1105.   CenterCursor: Boolean;
  1106.   SelectMode: Byte;
  1107.   I: Integer;
  1108.   NewPtr: Word;
  1109.   D, Mouse: TPoint;
  1110.  
  1111. procedure CheckScrollBar(P: PScrollBar; var D: Integer);
  1112. begin
  1113.   if (Event.InfoPtr = P) and (P^.Value <> D) then
  1114.   begin
  1115.     D := P^.Value;
  1116.     Update(ufView);
  1117.   end;
  1118. end;
  1119.  
  1120. begin
  1121.   inherited HandleEvent(Event);
  1122.   ConvertEvent(Event);
  1123.   CenterCursor := not CursorVisible;
  1124.   SelectMode := 0;
  1125.   if Selecting or (ShiftState and $03 <> 0) then SelectMode := smExtend;
  1126.   case Event.What of
  1127.     evMouseDown:
  1128.       begin
  1129.         if Event.Double then SelectMode := SelectMode or smDouble;
  1130.         repeat
  1131.           Lock;
  1132.           if Event.What = evMouseAuto then
  1133.           begin
  1134.             MakeLocal(Event.Where, Mouse);
  1135.             D := Delta;
  1136.             if Mouse.X < 0 then Dec(D.X);
  1137.             if Mouse.X >= Size.X then Inc(D.X);
  1138.             if Mouse.Y < 0 then Dec(D.Y);
  1139.             if Mouse.Y >= Size.Y then Inc(D.Y);
  1140.             ScrollTo(D.X, D.Y);
  1141.           end;
  1142.           SetCurPtr(GetMousePtr(Event.Where), SelectMode);
  1143.           SelectMode := SelectMode or smExtend;
  1144.           Unlock;
  1145.         until not MouseEvent(Event, evMouseMove + evMouseAuto);
  1146.       end;
  1147.     evKeyDown:
  1148.       case Event.CharCode of
  1149.         #9,#32..#255:
  1150.           begin
  1151.             Lock;
  1152.             if Overwrite and not HasSelection then
  1153.               if CurPtr <> LineEnd(CurPtr) then SelEnd := NextChar(CurPtr);
  1154.             InsertText(@Event.CharCode, 1, False);
  1155.             TrackCursor(CenterCursor);
  1156.             Unlock;
  1157.           end;
  1158.       else
  1159.         Exit;
  1160.       end;
  1161.     evCommand:
  1162.       case Event.Command of
  1163.         cmFind: Find;
  1164.         cmReplace: Replace;
  1165.         cmSearchAgain: DoSearchReplace;
  1166.       else
  1167.         begin
  1168.           Lock;
  1169.           case Event.Command of
  1170.             cmCut: ClipCut;
  1171.             cmCopy: ClipCopy;
  1172.             cmPaste: ClipPaste;
  1173.             cmUndo: Undo;
  1174.             cmClear: DeleteSelect;
  1175.             cmCharLeft: SetCurPtr(PrevChar(CurPtr), SelectMode);
  1176.             cmCharRight: SetCurPtr(NextChar(CurPtr), SelectMode);
  1177.             cmWordLeft: SetCurPtr(PrevWord(CurPtr), SelectMode);
  1178.             cmWordRight: SetCurPtr(NextWord(CurPtr), SelectMode);
  1179.             cmLineStart: SetCurPtr(LineStart(CurPtr), SelectMode);
  1180.             cmLineEnd: SetCurPtr(LineEnd(CurPtr), SelectMode);
  1181.             cmLineUp: SetCurPtr(LineMove(CurPtr, -1), SelectMode);
  1182.             cmLineDown: SetCurPtr(LineMove(CurPtr, 1), SelectMode);
  1183.             cmPageUp: SetCurPtr(LineMove(CurPtr, -(Size.Y - 1)), SelectMode);
  1184.             cmPageDown: SetCurPtr(LineMove(CurPtr, Size.Y - 1), SelectMode);
  1185.             cmTextStart: SetCurPtr(0, SelectMode);
  1186.             cmTextEnd: SetCurPtr(BufLen, SelectMode);
  1187.             cmNewLine: NewLine;
  1188.             cmBackSpace: DeleteRange(PrevChar(CurPtr), CurPtr, True);
  1189.             cmDelChar: DeleteRange(CurPtr, NextChar(CurPtr), True);
  1190.             cmDelWord: DeleteRange(CurPtr, NextWord(CurPtr), False);
  1191.             cmDelStart: DeleteRange(LineStart(CurPtr), CurPtr, False);
  1192.             cmDelEnd: DeleteRange(CurPtr, LineEnd(CurPtr), False);
  1193.             cmDelLine: DeleteRange(LineStart(CurPtr), NextLine(CurPtr), False);
  1194.             cmInsMode: ToggleInsMode;
  1195.             cmStartSelect: StartSelect;
  1196.             cmHideSelect: HideSelect;
  1197.             cmIndentMode: AutoIndent := not AutoIndent;
  1198.           else
  1199.             Unlock;
  1200.             Exit;
  1201.           end;
  1202.           TrackCursor(CenterCursor);
  1203.           Unlock;
  1204.         end;
  1205.       end;
  1206.     evBroadcast:
  1207.       case Event.Command of
  1208.         cmScrollBarChanged:
  1209.           if (Event.InfoPtr = HScrollBar) or
  1210.             (Event.InfoPtr = VScrollBar) then
  1211.           begin
  1212.             CheckScrollBar(HScrollBar, Delta.X);
  1213.             CheckScrollBar(VScrollBar, Delta.Y);
  1214.           end
  1215.           else
  1216.             Exit;
  1217.       else
  1218.         Exit;
  1219.       end;
  1220.   end;
  1221.   ClearEvent(Event);
  1222. end;
  1223.  
  1224. function TEditor.HasSelection: Boolean;
  1225. begin
  1226.   HasSelection := SelStart <> SelEnd;
  1227. end;
  1228.  
  1229. procedure TEditor.HideSelect;
  1230. begin
  1231.   Selecting := False;
  1232.   SetSelect(CurPtr, CurPtr, False);
  1233. end;
  1234.  
  1235. procedure TEditor.InitBuffer;
  1236. begin
  1237.   Buffer := MemAlloc(BufSize);
  1238. end;
  1239.  
  1240. function TEditor.InsertBuffer(var P: PEditBuffer; Offset, Length: Word;
  1241.   AllowUndo, SelectText: Boolean): Boolean;
  1242. var
  1243.   SelLen, DelLen, SelLines, Lines: Word;
  1244.   NewSize: Longint;
  1245. begin
  1246.   InsertBuffer := True;
  1247.   Selecting := False;
  1248.   SelLen := SelEnd - SelStart;
  1249.   if (SelLen = 0) and (Length = 0) then Exit;
  1250.   DelLen := 0;
  1251.   if AllowUndo then
  1252.     if CurPtr = SelStart then DelLen := SelLen else
  1253.       if SelLen > InsCount then DelLen := SelLen - InsCount;
  1254.   NewSize := Longint(BufLen + DelCount - SelLen + DelLen) + Length;
  1255.   if NewSize > BufLen + DelCount then
  1256.     if (NewSize > $FFF0) or not SetBufSize(NewSize) then
  1257.     begin
  1258.       EditorDialog(edOutOfMemory, nil);
  1259.       InsertBuffer := False;
  1260.       SelEnd := SelStart;
  1261.       Exit;
  1262.     end;
  1263.   SelLines := CountLines(Buffer^[BufPtr(SelStart)], SelLen);
  1264.   if CurPtr = SelEnd then
  1265.   begin
  1266.     if AllowUndo then
  1267.     begin
  1268.       if DelLen > 0 then Move(Buffer^[SelStart],
  1269.         Buffer^[CurPtr + GapLen - DelCount - DelLen], DelLen);
  1270.       Dec(InsCount, SelLen - DelLen);
  1271.     end;
  1272.     CurPtr := SelStart;
  1273.     Dec(CurPos.Y, SelLines);
  1274.   end;
  1275.   if Delta.Y > CurPos.Y then
  1276.   begin
  1277.     Dec(Delta.Y, SelLines);
  1278.     if Delta.Y < CurPos.Y then Delta.Y := CurPos.Y;
  1279.   end;
  1280.   if Length > 0 then Move(P^[Offset], Buffer^[CurPtr], Length);
  1281.   Lines := CountLines(Buffer^[CurPtr], Length);
  1282.   Inc(CurPtr, Length);
  1283.   Inc(CurPos.Y, Lines);
  1284.   DrawLine := CurPos.Y;
  1285.   DrawPtr := LineStart(CurPtr);
  1286.   CurPos.X := CharPos(DrawPtr, CurPtr);
  1287.   if not SelectText then SelStart := CurPtr;
  1288.   SelEnd := CurPtr;
  1289.   Inc(BufLen, Length - SelLen);
  1290.   Dec(GapLen, Length - SelLen);
  1291.   if AllowUndo then
  1292.   begin
  1293.     Inc(DelCount, DelLen);
  1294.     Inc(InsCount, Length);
  1295.   end;
  1296.   Inc(Limit.Y, Lines - SelLines);
  1297.   Delta.Y := Max(0, Min(Delta.Y, Limit.Y - Size.Y));
  1298.   if not IsClipboard then Modified := True;
  1299.   SetBufSize(BufLen + DelCount);
  1300.   if (SelLines = 0) and (Lines = 0) then Update(ufLine) else Update(ufView);
  1301. end;
  1302.  
  1303. function TEditor.InsertFrom(Editor: PEditor): Boolean;
  1304. begin
  1305.   InsertFrom := InsertBuffer(Editor^.Buffer,
  1306.     Editor^.BufPtr(Editor^.SelStart),
  1307.     Editor^.SelEnd - Editor^.SelStart, CanUndo, IsClipboard);
  1308. end;
  1309.  
  1310. function TEditor.InsertText(Text: Pointer; Length: Word;
  1311.   SelectText: Boolean): Boolean;
  1312. begin
  1313.   InsertText := InsertBuffer(PEditBuffer(Text),
  1314.     0, Length, CanUndo, SelectText);
  1315. end;
  1316.  
  1317. function TEditor.IsClipboard: Boolean;
  1318. begin
  1319.   IsClipboard := Clipboard = @Self;
  1320. end;
  1321.  
  1322. function TEditor.LineEnd(P: Word): Word; assembler;
  1323. asm
  1324.         PUSH    DS
  1325.         LDS     SI,Self
  1326.         LES     BX,DS:[SI].TEditor.Buffer
  1327.         MOV     DI,P
  1328.         MOV     AL,0DH
  1329.         CLD
  1330.         MOV     CX,DS:[SI].TEditor.CurPtr
  1331.         SUB     CX,DI
  1332.         JBE     @@1
  1333.         ADD     DI,BX
  1334.         REPNE   SCASB
  1335.         JE      @@2
  1336.         MOV     DI,DS:[SI].TEditor.CurPtr
  1337. @@1:    MOV     CX,DS:[SI].TEditor.BufLen
  1338.         SUB     CX,DI
  1339.         JCXZ    @@4
  1340.         ADD     BX,DS:[SI].TEditor.GapLen
  1341.         ADD     DI,BX
  1342.         REPNE   SCASB
  1343.         JNE     @@3
  1344. @@2:    DEC     DI
  1345. @@3:    SUB     DI,BX
  1346. @@4:    MOV     AX,DI
  1347.         POP     DS
  1348. end;
  1349.  
  1350. function TEditor.LineMove(P: Word; Count: Integer): Word;
  1351. var
  1352.   Pos: Integer;
  1353.   I: Word;
  1354. begin
  1355.   I := P;
  1356.   P := LineStart(P);
  1357.   Pos := CharPos(P, I);
  1358.   while Count <> 0 do
  1359.   begin
  1360.     I := P;
  1361.     if Count < 0 then
  1362.     begin
  1363.       P := PrevLine(P);
  1364.       Inc(Count);
  1365.     end else
  1366.     begin
  1367.       P := NextLine(P);
  1368.       Dec(Count);
  1369.     end;
  1370.   end;
  1371.   if P <> I then P := CharPtr(P, Pos);
  1372.   LineMove := P;
  1373. end;
  1374.  
  1375. function TEditor.LineStart(P: Word): Word; assembler;
  1376. asm
  1377.         PUSH    DS
  1378.         LDS     SI,Self
  1379.         LES     BX,DS:[SI].TEditor.Buffer
  1380.         MOV     DI,P
  1381.         MOV     AL,0DH
  1382.         STD
  1383.         MOV     CX,DI
  1384.         SUB     CX,DS:[SI].TEditor.CurPtr
  1385.         JBE     @@1
  1386.         ADD     BX,DS:[SI].TEditor.GapLen
  1387.         ADD     DI,BX
  1388.         DEC     DI
  1389.         REPNE   SCASB
  1390.         JE      @@2
  1391.         SUB     BX,DS:[SI].TEditor.GapLen
  1392.         MOV     DI,DS:[SI].TEditor.CurPtr
  1393. @@1:    MOV     CX,DI
  1394.         JCXZ    @@4
  1395.         ADD     DI,BX
  1396.         DEC     DI
  1397.         REPNE   SCASB
  1398.         JNE     @@3
  1399. @@2:    INC     DI
  1400.         INC     DI
  1401.         SUB     DI,BX
  1402.         CMP     DI,DS:[SI].TEditor.CurPtr
  1403.         JE      @@4
  1404.         CMP     DI,DS:[SI].TEditor.BufLen
  1405.         JE      @@4
  1406.         CMP     ES:[BX+DI].Byte,0AH
  1407.         JNE     @@4
  1408.         INC     DI
  1409.         JMP     @@4
  1410. @@3:    XOR     DI,DI
  1411. @@4:    MOV     AX,DI
  1412.         POP     DS
  1413. end;
  1414.  
  1415. procedure TEditor.Lock;
  1416. begin
  1417.   Inc(LockCount);
  1418. end;
  1419.  
  1420. procedure TEditor.NewLine;
  1421. const
  1422.   CrLf: array[1..2] of Char = #13#10;
  1423. var
  1424.   I, P: Word;
  1425. begin
  1426.   P := LineStart(CurPtr);
  1427.   I := P;
  1428.   while (I < CurPtr) and ((Buffer^[I] = ' ') or (Buffer^[I] = #9)) do Inc(I);
  1429.   InsertText(@CrLf, 2, False);
  1430.   if AutoIndent then InsertText(@Buffer^[P], I - P, False);
  1431. end;
  1432.  
  1433. function TEditor.NextChar(P: Word): Word; assembler;
  1434. asm
  1435.         PUSH    DS
  1436.         LDS     SI,Self
  1437.         MOV     DI,P
  1438.         CMP     DI,DS:[SI].TEditor.BufLen
  1439.         JE      @@2
  1440.         INC     DI
  1441.         CMP     DI,DS:[SI].TEditor.BufLen
  1442.         JE      @@2
  1443.         LES     BX,DS:[SI].TEditor.Buffer
  1444.         CMP     DI,DS:[SI].TEditor.CurPtr
  1445.         JB      @@1
  1446.         ADD     BX,DS:[SI].TEditor.GapLen
  1447. @@1:    CMP     ES:[BX+DI-1].Word,0A0DH
  1448.         JNE     @@2
  1449.         INC     DI
  1450. @@2:    MOV     AX,DI
  1451.         POP     DS
  1452. end;
  1453.  
  1454. function TEditor.NextLine(P: Word): Word;
  1455. begin
  1456.   NextLine := NextChar(LineEnd(P));
  1457. end;
  1458.  
  1459. function TEditor.NextWord(P: Word): Word;
  1460. begin
  1461.   while (P < BufLen) and (BufChar(P) in WordChars) do
  1462.     P := NextChar(P);
  1463.   while (P < BufLen) and not (BufChar(P) in WordChars) do
  1464.     P := NextChar(P);
  1465.   NextWord := P;
  1466. end;
  1467.  
  1468. function TEditor.PrevChar(P: Word): Word; assembler;
  1469. asm
  1470.         PUSH    DS
  1471.         LDS     SI,Self
  1472.         MOV     DI,P
  1473.         OR      DI,DI
  1474.         JE      @@2
  1475.         DEC     DI
  1476.         JE      @@2
  1477.         LES     BX,DS:[SI].TEditor.Buffer
  1478.         CMP     DI,DS:[SI].TEditor.CurPtr
  1479.         JB      @@1
  1480.         ADD     BX,DS:[SI].TEditor.GapLen
  1481. @@1:    CMP     ES:[BX+DI-1].Word,0A0DH
  1482.         JNE     @@2
  1483.         DEC     DI
  1484. @@2:    MOV     AX,DI
  1485.         POP     DS
  1486. end;
  1487.  
  1488. function TEditor.PrevLine(P: Word): Word;
  1489. begin
  1490.   PrevLine := LineStart(PrevChar(P));
  1491. end;
  1492.  
  1493. function TEditor.PrevWord(P: Word): Word;
  1494. begin
  1495.   while (P > 0) and not (BufChar(PrevChar(P)) in WordChars) do
  1496.     P := PrevChar(P);
  1497.   while (P > 0) and (BufChar(PrevChar(P)) in WordChars) do
  1498.     P := PrevChar(P);
  1499.   PrevWord := P;
  1500. end;
  1501.  
  1502. procedure TEditor.Replace;
  1503. var
  1504.   ReplaceRec: TReplaceDialogRec;
  1505. begin
  1506.   with ReplaceRec do
  1507.   begin
  1508.     Find := FindStr;
  1509.     Replace := ReplaceStr;
  1510.     Options := EditorFlags;
  1511.     if EditorDialog(edReplace, @ReplaceRec) <> cmCancel then
  1512.     begin
  1513.       FindStr := Find;
  1514.       ReplaceStr := Replace;
  1515.       EditorFlags := Options or efDoReplace;
  1516.       DoSearchReplace;
  1517.     end;
  1518.   end;
  1519. end;
  1520.  
  1521. procedure TEditor.ScrollTo(X, Y: Integer);
  1522. begin
  1523.   X := Max(0, Min(X, Limit.X - Size.X));
  1524.   Y := Max(0, Min(Y, Limit.Y - Size.Y));
  1525.   if (X <> Delta.X) or (Y <> Delta.Y) then
  1526.   begin
  1527.     Delta.X := X;
  1528.     Delta.Y := Y;
  1529.     Update(ufView);
  1530.   end;
  1531. end;
  1532.  
  1533. function TEditor.Search(const FindStr: String; Opts: Word): Boolean;
  1534. var
  1535.   I, Pos: Word;
  1536. begin
  1537.   Search := False;
  1538.   Pos := CurPtr;
  1539.   repeat
  1540.     if Opts and efCaseSensitive <> 0 then
  1541.       I := Scan(Buffer^[BufPtr(Pos)], BufLen - Pos, FindStr)
  1542.     else I := IScan(Buffer^[BufPtr(Pos)], BufLen - Pos, FindStr);
  1543.     if (I <> sfSearchFailed) then
  1544.     begin
  1545.       Inc(I, Pos);
  1546.       if (Opts and efWholeWordsOnly = 0) or
  1547.          not (((I <> 0) and (BufChar(I - 1) in WordChars)) or
  1548.               ((I + Length(FindStr) <> BufLen) and
  1549.                (BufChar(I + Length(FindStr)) in WordChars))) then
  1550.       begin
  1551.         Lock;
  1552.         SetSelect(I, I + Length(FindStr), False);
  1553.         TrackCursor(not CursorVisible);
  1554.         Unlock;
  1555.         Search := True;
  1556.         Exit;
  1557.       end else Pos := I + 1;
  1558.     end;
  1559.   until I = sfSearchFailed;
  1560. end;
  1561.  
  1562. procedure TEditor.SetBufLen(Length: Word);
  1563. begin
  1564.   BufLen := Length;
  1565.   GapLen := BufSize - Length;
  1566.   SelStart := 0;
  1567.   SelEnd := 0;
  1568.   CurPtr := 0;
  1569.   Longint(CurPos) := 0;
  1570.   Longint(Delta) := 0;
  1571.   Limit.X := MaxLineLength;
  1572.   Limit.Y := CountLines(Buffer^[GapLen], BufLen) + 1;
  1573.   DrawLine := 0;
  1574.   DrawPtr := 0;
  1575.   DelCount := 0;
  1576.   InsCount := 0;
  1577.   Modified := False;
  1578.   Update(ufView);
  1579. end;
  1580.  
  1581. function TEditor.SetBufSize(NewSize: Word): Boolean;
  1582. begin
  1583.   SetBufSize := NewSize <= BufSize;
  1584. end;
  1585.  
  1586. procedure TEditor.SetCmdState(Command: Word; Enable: Boolean);
  1587. var
  1588.   S: TCommandSet;
  1589. begin
  1590.   S := [Command];
  1591.   if Enable and (State and sfActive <> 0) then
  1592.     EnableCommands(S) else DisableCommands(S);
  1593. end;
  1594.  
  1595. procedure TEditor.SetCurPtr(P: Word; SelectMode: Byte);
  1596. var
  1597.   Anchor: Word;
  1598. begin
  1599.   if SelectMode and smExtend = 0 then Anchor := P else
  1600.     if CurPtr = SelStart then Anchor := SelEnd else Anchor := SelStart;
  1601.   if P < Anchor then
  1602.   begin
  1603.     if SelectMode and smDouble <> 0 then
  1604.     begin
  1605.       P := PrevLine(NextLine(P));
  1606.       Anchor := NextLine(PrevLine(Anchor));
  1607.     end;
  1608.     SetSelect(P, Anchor, True);
  1609.   end else
  1610.   begin
  1611.     if SelectMode and smDouble <> 0 then
  1612.     begin
  1613.       P := NextLine(P);
  1614.       Anchor := PrevLine(NextLine(Anchor));
  1615.     end;
  1616.     SetSelect(Anchor, P, False);
  1617.   end;
  1618. end;
  1619.  
  1620. procedure TEditor.SetSelect(NewStart, NewEnd: Word; CurStart: Boolean);
  1621. var
  1622.   Flags: Byte;
  1623.   P, L: Word;
  1624. begin
  1625.   if CurStart then P := NewStart else P := NewEnd;
  1626.   Flags := ufUpdate;
  1627.   if (NewStart <> SelStart) or (NewEnd <> SelEnd) then
  1628.     if (NewStart <> NewEnd) or (SelStart <> SelEnd) then
  1629.       Flags := ufView;
  1630.   if P <> CurPtr then
  1631.   begin
  1632.     if P > CurPtr then
  1633.     begin
  1634.       L := P - CurPtr;
  1635.       Move(Buffer^[CurPtr + GapLen], Buffer^[CurPtr], L);
  1636.       Inc(CurPos.Y, CountLines(Buffer^[CurPtr], L));
  1637.       CurPtr := P;
  1638.     end else
  1639.     begin
  1640.       L := CurPtr - P;
  1641.       CurPtr := P;
  1642.       Dec(CurPos.Y, CountLines(Buffer^[CurPtr], L));
  1643.       Move(Buffer^[CurPtr], Buffer^[CurPtr + GapLen], L);
  1644.     end;
  1645.     DrawLine := CurPos.Y;
  1646.     DrawPtr := LineStart(P);
  1647.     CurPos.X := CharPos(DrawPtr, P);
  1648.     DelCount := 0;
  1649.     InsCount := 0;
  1650.     SetBufSize(BufLen);
  1651.   end;
  1652.   SelStart := NewStart;
  1653.   SelEnd := NewEnd;
  1654.   Update(Flags);
  1655. end;
  1656.  
  1657. procedure TEditor.SetState(AState: Word; Enable: Boolean);
  1658. begin
  1659.   inherited SetState(AState, Enable);
  1660.   case AState of
  1661.     sfActive:
  1662.       begin
  1663.         if HScrollBar <> nil then HScrollBar^.SetState(sfVisible, Enable);
  1664.         if VScrollBar <> nil then VScrollBar^.SetState(sfVisible, Enable);
  1665.         if Indicator <> nil then Indicator^.SetState(sfVisible, Enable);
  1666.         UpdateCommands;
  1667.       end;
  1668.     sfExposed:
  1669.       if Enable then Unlock;
  1670.   end;
  1671. end;
  1672.  
  1673. procedure TEditor.StartSelect;
  1674. begin
  1675.   HideSelect;
  1676.   Selecting := True;
  1677. end;
  1678.  
  1679. procedure TEditor.Store(var S: TStream);
  1680. begin
  1681.   inherited Store(S);
  1682.   PutPeerViewPtr(S, HScrollBar);
  1683.   PutPeerViewPtr(S, VScrollBar);
  1684.   PutPeerViewPtr(S, Indicator);
  1685.   S.Write(BufSize, SizeOf(Word));
  1686.   S.Write(CanUndo, SizeOf(Boolean));
  1687. end;
  1688.  
  1689. procedure TEditor.ToggleInsMode;
  1690. begin
  1691.   Overwrite := not Overwrite;
  1692.   SetState(sfCursorIns, not GetState(sfCursorIns));
  1693. end;
  1694.  
  1695. procedure TEditor.TrackCursor(Center: Boolean);
  1696. begin
  1697.   if Center then
  1698.     ScrollTo(CurPos.X - Size.X + 1, CurPos.Y - Size.Y div 2) else
  1699.     ScrollTo(Max(CurPos.X - Size.X + 1, Min(Delta.X, CurPos.X)),
  1700.       Max(CurPos.Y - Size.Y + 1, Min(Delta.Y, CurPos.Y)));
  1701. end;
  1702.  
  1703. procedure TEditor.Undo;
  1704. var
  1705.   Length: Word;
  1706. begin
  1707.   if (DelCount <> 0) or (InsCount <> 0) then
  1708.   begin
  1709.     SelStart := CurPtr - InsCount;
  1710.     SelEnd := CurPtr;
  1711.     Length := DelCount;
  1712.     DelCount := 0;
  1713.     InsCount := 0;
  1714.     InsertBuffer(Buffer, CurPtr + GapLen - Length, Length, False, True);
  1715.   end;
  1716. end;
  1717.  
  1718. procedure TEditor.Unlock;
  1719. begin
  1720.   if LockCount > 0 then
  1721.   begin
  1722.     Dec(LockCount);
  1723.     if LockCount = 0 then DoUpdate;
  1724.   end;
  1725. end;
  1726.  
  1727. procedure TEditor.Update(AFlags: Byte);
  1728. begin
  1729.   UpdateFlags := UpdateFlags or AFlags;
  1730.   if LockCount = 0 then DoUpdate;
  1731. end;
  1732.  
  1733. procedure TEditor.UpdateCommands;
  1734. begin
  1735.   SetCmdState(cmUndo, (DelCount <> 0) or (InsCount <> 0));
  1736.   if not IsClipboard then
  1737.   begin
  1738.     SetCmdState(cmCut, HasSelection);
  1739.     SetCmdState(cmCopy, HasSelection);
  1740.     SetCmdState(cmPaste, (Clipboard <> nil) and (Clipboard^.HasSelection));
  1741.   end;
  1742.   SetCmdState(cmClear, HasSelection);
  1743.   SetCmdState(cmFind, True);
  1744.   SetCmdState(cmReplace, True);
  1745.   SetCmdState(cmSearchAgain, True);
  1746. end;
  1747.  
  1748. function TEditor.Valid(Command: Word): Boolean;
  1749. begin
  1750.   Valid := IsValid;
  1751. end;
  1752.  
  1753. { TMemo }
  1754.  
  1755. constructor TMemo.Load(var S: TStream);
  1756. var
  1757.   Length: Word;
  1758. begin
  1759.   inherited Load(S);
  1760.   S.Read(Length, SizeOf(Word));
  1761.   if IsValid then
  1762.   begin
  1763.     S.Read(Buffer^[BufSize - Length], Length);
  1764.     SetBufLen(Length);
  1765.   end
  1766.   else S.Seek(S.GetPos + Length);
  1767. end;
  1768.  
  1769. function TMemo.DataSize: Word;
  1770. begin
  1771.   DataSize := BufSize + SizeOf(Word);
  1772. end;
  1773.  
  1774. procedure TMemo.GetData(var Rec);
  1775. var
  1776.   Data: TMemoData absolute Rec;
  1777. begin
  1778.   Data.Length := BufLen;
  1779.   Move(Buffer^, Data.Buffer, CurPtr);
  1780.   Move(Buffer^[CurPtr + GapLen], Data.Buffer[CurPtr], BufLen - CurPtr);
  1781.   FillChar(Data.Buffer[BufLen], BufSize - BufLen, 0);
  1782. end;
  1783.  
  1784. function TMemo.GetPalette: PPalette;
  1785. const
  1786.   P: String[Length(CMemo)] = CMemo;
  1787. begin
  1788.   GetPalette := @P;
  1789. end;
  1790.  
  1791. procedure TMemo.HandleEvent(var Event: TEvent);
  1792. begin
  1793.   if (Event.What <> evKeyDown) or (Event.KeyCode <> kbTab) then
  1794.     inherited HandleEvent(Event);
  1795. end;
  1796.  
  1797. procedure TMemo.SetData(var Rec);
  1798. var
  1799.   Data: TMemoData absolute Rec;
  1800. begin
  1801.   Move(Data.Buffer, Buffer^[BufSize - Data.Length], Data.Length);
  1802.   SetBufLen(Data.Length);
  1803. end;
  1804.  
  1805. procedure TMemo.Store(var S: TStream);
  1806. begin
  1807.   inherited Store(S);
  1808.   S.Write(BufLen, SizeOf(Word));
  1809.   S.Write(Buffer^, CurPtr);
  1810.   S.Write(Buffer^[CurPtr + GapLen], BufLen - CurPtr);
  1811. end;
  1812.  
  1813. { TFileEditor }
  1814.  
  1815. constructor TFileEditor.Init(var Bounds: TRect;
  1816.   AHScrollBar, AVScrollBar: PScrollBar;
  1817.   AIndicator: PIndicator; AFileName: FNameStr);
  1818. begin
  1819.   inherited Init(Bounds, AHScrollBar, AVScrollBar, AIndicator, 0);
  1820.   if AFileName <> '' then
  1821.   begin
  1822.     FileName := FExpand(AFileName);
  1823.     if IsValid then IsValid := LoadFile;
  1824.   end;
  1825. end;
  1826.  
  1827. constructor TFileEditor.Load(var S: TStream);
  1828. var
  1829.   SStart, SEnd, Curs: Word;
  1830. begin
  1831.   inherited Load(S);
  1832.   BufSize := 0;
  1833.   S.Read(FileName[0], SizeOf(Char));
  1834.   S.Read(Filename[1], Length(FileName));
  1835.   if IsValid then IsValid := LoadFile;
  1836.   S.Read(SStart, SizeOf(Word));
  1837.   S.Read(SEnd, SizeOf(Word));
  1838.   S.Read(Curs, SizeOf(Word));
  1839.   if IsValid and (SEnd <= BufLen) then
  1840.   begin
  1841.     SetSelect(SStart, SEnd, Curs = SStart);
  1842.     TrackCursor(True);
  1843.   end;
  1844. end;
  1845.  
  1846. procedure TFileEditor.DoneBuffer;
  1847. begin
  1848.   if Buffer <> nil then DisposeBuffer(Buffer);
  1849. end;
  1850.  
  1851. procedure TFileEditor.HandleEvent(var Event: TEvent);
  1852. begin
  1853.   inherited HandleEvent(Event);
  1854.   case Event.What of
  1855.     evCommand:
  1856.       case Event.Command of
  1857.         cmSave: Save;
  1858.         cmSaveAs: SaveAs;
  1859.       else
  1860.         Exit;
  1861.       end;
  1862.   else
  1863.     Exit;
  1864.   end;
  1865.   ClearEvent(Event);
  1866. end;
  1867.  
  1868. procedure TFileEditor.InitBuffer;
  1869. begin
  1870.   NewBuffer(Pointer(Buffer), $1000);
  1871. end;
  1872.  
  1873. function TFileEditor.LoadFile: Boolean;
  1874. var
  1875.   Length: Word;
  1876.   FSize: Longint;
  1877.   F: File;
  1878. begin
  1879.   LoadFile := False;
  1880.   Length := 0;
  1881.   Assign(F, FileName);
  1882.   Reset(F, 1);
  1883.   if IOResult <> 0 then EditorDialog(edReadError, @FileName)
  1884.   else
  1885.   begin
  1886.     FSize := FileSize(F);
  1887.     if (FSize > $FFF0) or not SetBufSize(Word(FSize)) then
  1888.       EditorDialog(edOutOfMemory, nil) else
  1889.     begin
  1890.       BlockRead(F, Buffer^[BufSize - Word(FSize)], FSize);
  1891.       if IOResult <> 0 then EditorDialog(edReadError, @FileName) else
  1892.       begin
  1893.         LoadFile := True;
  1894.         Length := FSize;
  1895.       end;
  1896.     end;
  1897.     Close(F);
  1898.   end;
  1899.   SetBufLen(Length);
  1900. end;
  1901.  
  1902. function TFileEditor.Save: Boolean;
  1903. begin
  1904.   if FileName = '' then Save := SaveAs else Save := SaveFile;
  1905. end;
  1906.  
  1907. function TFileEditor.SaveAs: Boolean;
  1908. begin
  1909.   SaveAs := False;
  1910.   if EditorDialog(edSaveAs, @FileName) <> cmCancel then
  1911.   begin
  1912.     FileName := FExpand(FileName);
  1913.     Message(Owner, evBroadcast, cmUpdateTitle, nil);
  1914.     SaveAs := SaveFile;
  1915.     if IsClipboard then FileName := '';
  1916.   end;
  1917. end;
  1918.  
  1919. function TFileEditor.SaveFile: Boolean;
  1920. var
  1921.   F: File;
  1922.   BackupName: FNameStr;
  1923.   D: DirStr;
  1924.   N: NameStr;
  1925.   E: ExtStr;
  1926. begin
  1927.   SaveFile := False;
  1928.   if EditorFlags and efBackupFiles <> 0 then
  1929.   begin
  1930.     FSplit(FileName, D, N, E);
  1931.     BackupName := D + N + '.BAK';
  1932.     Assign(F, BackupName);
  1933.     Erase(F);
  1934.     Assign(F, FileName);
  1935.     Rename(F, BackupName);
  1936.     InOutRes := 0;
  1937.   end;
  1938.   Assign(F, FileName);
  1939.   Rewrite(F, 1);
  1940.   if IOResult <> 0 then EditorDialog(edCreateError, @FileName) else
  1941.   begin
  1942.     BlockWrite(F, Buffer^, CurPtr);
  1943.     BlockWrite(F, Buffer^[CurPtr + GapLen], BufLen - CurPtr);
  1944.     if IOResult <> 0 then EditorDialog(edWriteError, @FileName) else
  1945.     begin
  1946.       Modified := False;
  1947.       Update(ufUpdate);
  1948.       SaveFile := True;
  1949.     end;
  1950.     Close(F);
  1951.   end;
  1952. end;
  1953.  
  1954. function TFileEditor.SetBufSize(NewSize: Word): Boolean;
  1955. var
  1956.   N: Word;
  1957. begin
  1958.   SetBufSize := False;
  1959.   if NewSize = 0 then NewSize := $1000 else
  1960.     if NewSize > $F000 then NewSize := $FFF0 else
  1961.       NewSize := (NewSize + $0FFF) and $F000;
  1962.   if NewSize <> BufSize then
  1963.   begin
  1964.     if NewSize > BufSize then
  1965.       if not SetBufferSize(Buffer, NewSize) then Exit;
  1966.     N := BufLen - CurPtr + DelCount;
  1967.     Move(Buffer^[BufSize - N], Buffer^[NewSize - N], N);
  1968.     if NewSize < BufSize then SetBufferSize(Buffer, NewSize);
  1969.     BufSize := NewSize;
  1970.     GapLen := BufSize - BufLen;
  1971.   end;
  1972.   SetBufSize := True;
  1973. end;
  1974.  
  1975. procedure TFileEditor.Store(var S: TStream);
  1976. begin
  1977.   inherited Store(S);
  1978.   S.Write(FileName, Length(FileName) + 1);
  1979.   S.Write(SelStart, SizeOf(Word) * 3);
  1980. end;
  1981.  
  1982. procedure TFileEditor.UpdateCommands;
  1983. begin
  1984.   inherited UpdateCommands;
  1985.   SetCmdState(cmSave, True);
  1986.   SetCmdState(cmSaveAs, True);
  1987. end;
  1988.  
  1989. function TFileEditor.Valid(Command: Word): Boolean;
  1990. var
  1991.   D: Integer;
  1992. begin
  1993.   if Command = cmValid then Valid := IsValid else
  1994.   begin
  1995.     Valid := True;
  1996.     if Modified then
  1997.     begin
  1998.       if FileName = '' then D := edSaveUntitled else D := edSaveModify;
  1999.       case EditorDialog(D, @FileName) of
  2000.         cmYes: Valid := Save;
  2001.         cmNo: Modified := False;
  2002.         cmCancel: Valid := False;
  2003.       end;
  2004.     end;
  2005.   end;
  2006. end;
  2007.  
  2008. { TEditWindow }
  2009.  
  2010. constructor TEditWindow.Init(var Bounds: TRect;
  2011.   FileName: FNameStr; ANumber: Integer);
  2012. var
  2013.   HScrollBar, VScrollBar: PScrollBar;
  2014.   Indicator: PIndicator;
  2015.   R: TRect;
  2016. begin
  2017.   inherited Init(Bounds, '', ANumber);
  2018.   Options := Options or ofTileable;
  2019.   R.Assign(18, Size.Y - 1, Size.X - 2, Size.Y);
  2020.   HScrollBar := New(PScrollBar, Init(R));
  2021.   HScrollBar^.Hide;
  2022.   Insert(HScrollBar);
  2023.   R.Assign(Size.X - 1, 1, Size.X, Size.Y - 1);
  2024.   VScrollBar := New(PScrollBar, Init(R));
  2025.   VScrollBar^.Hide;
  2026.   Insert(VScrollBar);
  2027.   R.Assign(2, Size.Y - 1, 16, Size.Y);
  2028.   Indicator := New(PIndicator, Init(R));
  2029.   Indicator^.Hide;
  2030.   Insert(Indicator);
  2031.   GetExtent(R);
  2032.   R.Grow(-1, -1);
  2033.   Editor := New(PFileEditor, Init(
  2034.     R, HScrollBar, VScrollBar, Indicator, FileName));
  2035.   Insert(Editor);
  2036. end;
  2037.  
  2038. constructor TEditWindow.Load(var S: TStream);
  2039. begin
  2040.   inherited Load(S);
  2041.   GetSubViewPtr(S, Editor);
  2042. end;
  2043.  
  2044. procedure TEditWindow.Close;
  2045. begin
  2046.   if Editor^.IsClipboard then Hide else inherited Close;
  2047. end;
  2048.  
  2049. function TEditWindow.GetTitle(MaxSize: Integer): TTitleStr;
  2050. begin
  2051.   if Editor^.IsClipboard then GetTitle := 'Clipboard' else
  2052.     if Editor^.FileName = '' then GetTitle := 'Untitled' else
  2053.       GetTitle := Editor^.FileName;
  2054. end;
  2055.  
  2056. procedure TEditWindow.HandleEvent(var Event: TEvent);
  2057. begin
  2058.   inherited HandleEvent(Event);
  2059.   if (Event.What = evBroadcast) and (Event.Command = cmUpdateTitle) then
  2060.   begin
  2061.     Frame^.DrawView;
  2062.     ClearEvent(Event);
  2063.   end;
  2064. end;
  2065.  
  2066. procedure TEditWindow.SizeLimits(var Min, Max: TPoint);
  2067. begin
  2068.   inherited SizeLimits(Min, Max);
  2069.   Min.X := 23;
  2070. end;
  2071.  
  2072. procedure TEditWindow.Store(var S: TStream);
  2073. begin
  2074.   inherited Store(S);
  2075.   PutSubViewPtr(S, Editor);
  2076. end;
  2077.  
  2078. procedure RegisterEditors;
  2079. begin
  2080.   RegisterType(REditor);
  2081.   RegisterType(RMemo);
  2082.   RegisterType(RFileEditor);
  2083.   RegisterType(RIndicator);
  2084.   RegisterType(REditWindow);
  2085. end;
  2086.  
  2087. end.
  2088.