пятница, 31 октября 2014 г.

Ссылка. Организация памяти в текстовом редакторе

Организация памяти в текстовом редакторе

Gap buffer

Прикольно. Я ровно такое делал в начале 90-х.

Когда писал один из редакторов под DOS.

Вот тут может быть даже доступны исходники:

http://everesteditor.chat.ru/Resources/Editors/DOS/my/index.htm
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/

Прикольно,что это всплыло в 2013-м году.

Видимо - "есть потребности".

"Я уже не первый раз сталкиваюсь с тем, что некоторые технологии, разработанные для старых компьютеров с ограниченными возможностями, оказываются в наше время забытыми, что приводит к распространению неэффективных решений. Надеюсь, что описанное в этой статье «тайное знание предков» поможет появлению на свет хороших текстовых редакторов."

Ну и это перекликается вот с чем - Рефакторинг. Преодоление "алгоритма маляра".

Всё так или иначе возвращается.

Из старых исходников особенно интересно вот это:

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/OLD/SSW.PAS

{$O+}
{$I-}

Unit SSw;

{$DEFINE FROM_SSW}

Interface

{$I DEFINES.INC} { - текущие директивы условной компиляции }
{$I USES.INC}

Const
 swOk              =  0;
 swBoxInvalid      = -1;
 swBoxNoBlock      = -2;
 swNonCorrectBlock = -3;
 swReadError       = -4;
 swWriteError      = -5;
 swCreateError     = -6;
 swStreamError     = -7;
 swMemoryError     = -8;

Type
 TBlockHeader = record
  NextPtr,PrevPtr : LongInt;
  LenContext : Word;
 end;

 TBoxHeader = record
  CurBlock,FirstBlock : LongInt;
 end;

 PBoxCollection = ^TBoxCollection;
 TBoxCollection = object(TCollection)
  Procedure FreeItem(Item : Pointer); virtual;
  Function DataSize : Integer; virtual;
 end;

const
 HdSize = SizeOf(TBlockHeader);

type
 PSwapper = ^TSwapper;
 TSwapper = object(TObject)

  BoxList    : PBoxCollection;
  {$IFDEF ENCRYPT}
  SwFile     : PEncryptFilter;
  {$ELSE}
  SwFile     : PLoFile;
  {$ENDIF}
  {$IFNDEF TEMP}
  FName      : String;
  {$ENDIF}
  HeadSwFile : PLoFile;
  MaxLenCount: Word;
  BlockHd    : TBlockHeader;
  FreeBox    : Integer;
  Status     : Integer;

  Constructor Init({$IFNDEF TEMP}Const aFName: PathStr;{$ENDIF}
                    aMaxLenCount: Word
   {$IFDEF ENCRYPT} ;aKey: LongInt {$ENDIF});
  Destructor Done; virtual;
  Procedure NewBlockBox(Var BoxNumber: Integer);
  Procedure FreeBlockBox(BoxNumber: Integer);
  Function  GetBlock(BoxNumber:Integer; Var Count: Word; Var Block) : Integer;
  Function  PutBlock(BoxNumber:Integer; Count: Word; Var Block) : Integer;
  Function  MoveBlock(SourceBoxNum,DestBoxNum:Integer) : Integer;
  Function  BoxValid(BoxNumber:Word) : Boolean;
  Function  EmptyBox(BoxNumber:Word) : Boolean;
  function  HasError : Boolean;
 end;

Implementation
var
 TmpCount : Word;
Function TBoxCollection.DataSize: Integer;
 Begin
  DataSize:=SizeOf(TBoxHeader);
 end;

Procedure TBoxCollection.FreeItem(Item : Pointer);
 Begin
  if Item <> Nil Then FreeMem(Item,DataSize);
 end;

 Constructor TSwapper.Init({$IFNDEF TEMP}Const aFName: PathStr;{$ENDIF}
  aMaxLenCount: Word
  {$IFDEF ENCRYPT} ;aKey: LongInt {$ENDIF});
  Begin
   inherited Init;
   Status := swOk;
   MaxLenCount:=aMaxLenCount;
   {$IFNDEF TEMP}
   FName := aFName;
   {$ENDIF}
   {$IFNDEF TEMP}
    {$IFDEF ENCRYPT}
     SwFile:=New(PEncryptFilter,Init(aKey,
                 New(PLoFile,Init(FName+'.sw',stCreate,cBlockSize))));
    {$ELSE}
     SwFile:=New(PLoFile,Init(FName+'.sw',stCreate,cBlockSize));
    {$ENDIF ENCRYPT}
    HeadSwFile:=New(PLoFile,Init(FName+'.hsw',stCreate,512));
   {$ELSE}
    {$IFDEF ENCRYPT}
     SwFile:=New(PEncryptFilter,Init(aKey,
                New(PTempBufStream,Init(cBlockSize))));
    {$ELSE}
     SwFile:=New(PTempBufStream,Init(cBlockSize));
    {$ENDIF ENCRYPT}
    HeadSwFile:=New(PTempBufStream,Init(512));
   {$ENDIF TEMP}
   if (SwFile = nil) or (HeadSwFile = nil) then
    begin
     Status := swStreamError;
     Exit;
    end;
   if (SwFile^.Status <> stOk) or (HeadSwFile^.Status <> stOk) then
    begin
     Status := swCreateError;
     Exit;
    end;
   BoxList:=New(PBoxCollection,Init(5,2));
   NewBlockBox(FreeBox);
  end;

 Destructor TSwapper.Done;
  {$IFNDEF TEMP}
   var F:File;
  {$ENDIF TEMP}
  Begin
   if (SwFile<>nil) then Dispose(SwFile,Done);
   if (HeadSwFile<>nil) then Dispose(HeadSwFile,Done);
   if (BoxList<>nil) Then Dispose(BoxList,Done);
   {$IFNDEF TEMP}
    Assign(F,FName+'.sw');
    Erase(F);
    Assign(F,FName+'.hsw');
    Erase(F);
   {$ENDIF}
   inherited Done;
  end;

 Procedure TSwapper.NewBlockBox(Var BoxNumber : Integer);
  Var
    P : Pointer;
    It : Integer;

  Function GetFreeElem(Item: Pointer) : Boolean; far;
   Begin
    GetFreeElem:=(Item=Nil);
   end;

  Begin
   P:=MemAlloc(BoxList^.DataSize);
   If P<>Nil
    then
     Begin
      With TBoxHeader(P^) do
       Begin
        CurBlock:=-1;
        FirstBlock:=-1;
       end;
     end
    else
     Begin
      BoxNumber:=-1;
      Status := swMemoryError;
      Exit;
     end;
   It:=BoxList^.IndexOf(Nil);
   If It>=0
    then
     Begin
      BoxList^.AtPut(It,P);
      BoxNumber:=It;
     end
    else
     Begin
      BoxNumber:=BoxList^.Count;
      BoxList^.Insert(P);
     end;
  end;

 Function  TSwapper.EmptyBox(BoxNumber:Word) : Boolean;
   Begin
    EmptyBox := not(TBoxHeader(BoxList^.At(BoxNumber)^).CurBlock>0);
   end; {TSwapper.EmptyBox}

 Procedure TSwapper.FreeBlockBox(BoxNumber : Integer);
   Begin
    if BoxValid(BoxNumber) Then
     begin
      if (BoxList^.At(BoxNumber)) <> Nil Then
       begin
        while(not EmptyBox(BoxNumber)) do
          MoveBlock(BoxNumber,FreeBox);
        BoxList^.FreeItem(BoxList^.At(BoxNumber));
        BoxList^.AtPut(BoxNumber,Nil);
        {Связать с пустыми (?!): }
       end;
     end
    else
     Status := swBoxInvalid;
   end;

 Function TSwapper.BoxValid(BoxNumber:Word) : Boolean;
   Begin
    If (BoxNumber>=0) and (BoxNumber<BoxList^.Count)
     then BoxValid:=True
     else BoxValid:=False;
   end;

 Function TSwapper.GetBlock(BoxNumber:Integer;
                             Var Count: Word;Var Block) : Integer;
   Var
    CurPtr   : LongInt;

   Begin
     If Not BoxValid(BoxNumber)
      then
       Begin
        Status := swBoxInvalid;
        GetBlock:=swBoxInvalid;
        Exit;
       end;
     CurPtr:=TBoxHeader(BoxList^.At(BoxNumber)^).CurBlock;

     If CurPtr=-1
      then
       Begin
        Status := swBoxNoBlock;
        GetBlock:=swBoxNoBlock;
        Exit;
       end;

     HeadSwFile^.Seek(CurPtr*HdSize);
     HeadSwFile^.Read(BlockHd,HdSize);
     Count:=BlockHd.LenContext;
     SwFile^.Seek(CurPtr*MaxLenCount);
     SwFile^.Read(Block,{MaxLen}Count);

     if (HeadSwFile^.Status <> stOk) or (SwFile^.Status <> stOk) then
      begin
       Status := swReadError;
       GetBlock:=swReadError;
       Exit;
      end;

     MoveBlock(BoxNumber,FreeBox);
     GetBlock:=swOk;
   end;

 Function TSwapper.PutBlock(BoxNumber:Integer;
                             Count: Word; Var Block) : Integer;
   Var
    SwLog  : Integer;
    CurPtr : LongInt;
    i      : Word;
   const
    NULL   : Byte = 0;
   Begin
    If Not BoxValid(BoxNumber)
      then
       Begin
        Status := swBoxInvalid;
        PutBlock:=swBoxInvalid;
        Exit;
       end;

    if (Count > MaxLenCount) then Count := MaxLenCount;
    BlockHd.LenContext:=Count;
    SwLog:=MoveBlock(FreeBox,BoxNumber);
    If (SwLog=swOk)
     then
      Begin
       CurPtr := TBoxHeader(BoxList^.At(BoxNumber)^).CurBlock;
       HeadSwFile^.Seek(CurPtr*HdSize);
       HeadSwFile^.Read(BlockHd,HdSize);
       if (HeadSwFile^.Status <> stOk) then
        begin
         Status := swReadError;
         PutBlock:=swReadError;
         Exit;
        end;
       BlockHd.LenContext:=Count;
       HeadSwFile^.Seek(CurPtr*HdSize);
       HeadSwFile^.Write(BlockHd,HdSize);
       if (HeadSwFile^.Status <> stOk) then
        begin
         Status := swWriteError;
         PutBlock:=swWriteError;
         Exit;
        end;
       SwFile^.Seek(CurPtr*MaxLenCount);
       SwFile^.Write(Block,{MaxLen}Count);
       if (MaxLenCount > Count) then
        for i := 1 to MaxLenCount - Count do
         SwFile^.Write(NULL,1);
       if (SwFile^.Status <> stOk) then
        begin
         Status := swWriteError;
         PutBlock:=swWriteError;
         Exit;
        end;
       PutBlock:=swOk;
      end
     else PutBlock:=SwLog;
   end;

 Function TSwapper.MoveBlock(SourceBoxNum,DestBoxNum:Integer) : Integer;
   Var
    BlockPtr : LongInt;

   Begin
     If Not (BoxValid(SourceBoxNum) and
             BoxValid(DestBoxNum))
      then
       Begin
        Status:=swBoxInvalid;
        MoveBlock:=swBoxInvalid;
        Exit;
       end;

     BlockPtr:=TBoxHeader(BoxList^.At(SourceBoxNum)^).CurBlock;
     If BlockPtr=-1
      then
       If SourceBoxNum=FreeBox
        then
         BlockPtr:=HeadSwFile^.GetSize div HdSize
        else
         Begin
          Status := swBoxNoBlock;
          MoveBlock:=swBoxNoBlock;
          Exit;
         end
      else
       Begin
        HeadSwFile^.Seek(BlockPtr*HdSize);
        HeadSwFile^.Read(BlockHd,HdSize);
        if (HeadSwFile^.Status <> stOk) then
         begin
          Status := swReadError;
          MoveBlock:=swReadError;
          Exit;
         end;
        TBoxHeader(BoxList^.At(SourceBoxNum)^).CurBlock:=BlockHd.NextPtr;
       end;

     {Пpисоед. блока BlockPtr к Dest}
     BlockHd.NextPtr:=TBoxHeader(BoxList^.At(DestBoxNum)^).CurBlock;
     HeadSwFile^.Seek(BlockPtr*HdSize);
     HeadSwFile^.Write(BlockHd,HdSize);
     if (HeadSwFile^.Status <> stOk) then
      begin
       Status := swWriteError;
       MoveBlock:=swWriteError;
       Exit;
      end;

     If TBoxHeader(BoxList^.At(DestBoxNum)^).CurBlock>=0
      then
       Begin
        HeadSwFile^.Seek(TBoxHeader(BoxList^.At(DestBoxNum)^).CurBlock*HdSize);
        HeadSwFile^.Read(BlockHd,HdSize);
        if (HeadSwFile^.Status <> stOk) then
         begin
          Status := swReadError;
          MoveBlock:=swReadError;
          Exit;
         end;
        BlockHd.PrevPtr:=BlockPtr;
        HeadSwFile^.Seek(TBoxHeader(BoxList^.At(DestBoxNum)^).CurBlock*HdSize);
        HeadSwFile^.Write(BlockHd,HdSize);
        if (HeadSwFile^.Status <> stOk) then
         begin
          Status := swWriteError;
          MoveBlock:=swWriteError;
          Exit;
         end;
       end
      else {Пеpвый блок};
     TBoxHeader(BoxList^.At(DestBoxNum)^).CurBlock:=BlockPtr;
     MoveBlock:=swOk;
   end;

function TSwapper.HasError : Boolean;
var Msg : String;
begin
 if (Status=swOk) then
  HasError := False
 else
  begin
   HasError := True;
   Case Status of
    swBoxInvalid: Msg := 'Неправильный номер коробки временного файла.';
    swBoxNoBlock: Msg := 'Коробка временного файла пустая.';
    swNonCorrectBlock: Msg := 'Неверный блок временного файла.';
    swReadError: Msg := 'Ошибка чтения временного файла.';
    swWriteError: Msg := 'Ошибка записи временного файла.';
    swCreateError: Msg := 'Ошибка создания временного файла.';
    swStreamError: Msg := 'Ошибка открытия временного потока.';
    swMemoryError: Msg := 'Нехватка памяти для работы с временным файлом.';
   else
    Msg := 'Неизвестная ошибка временного файла.';
   end;
   MessageBox(^C+Msg,Nil,mfError+mfOkButton);
  end;
end;

{$UNDEF FROM_SSW}

end.

Это собственная реализация своппинга.

А используется своппинг тут:

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/OLD/LONGEDIT.PAS

Ну а GapBuffer используется например тут:

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/OLD/EDITOR.PAS

{$O+}

{$DEFINE FROM_EDITOR}

Unit Editor;

interface

{$I DEFINES.INC} { - текущие директивы условной компиляции }
{$I USES.INC}

{ интерфейс обьекта TEditor - простого редактора коротких файлов }
type
  PEditor = ^TEditor;
  {$IFNDEF SVISION}
  TEditor = object(TView)
  {$ELSE}
  TEditor = object(TView)
  {$ENDIF}
    UndoMode: Boolean;
    HScrollBar: PLongScrollBar;
    VScrollBar: PLongScrollBar;
    Indicator: PIndicator;
    Buffer: PEditBuffer;
    BufSize: Word;
    BufLen: Word;
    GapLen: Word;
    SelStart: Word;
    SelEnd: Word;
    OSelectMode : Byte;    { предыдущий режим выделения блока   }
    OSelStart   : Word;    { старое начало блока (до подкачки из коробки) }
    OSelEnd     : Word;    { старый конец блока  (до подкачки из коробки) }
    CurPtr: Word;
    CurPos: TLongPoint;
    Delta: TLongPoint;
    Limit: TLongPoint;
    edDrawLine: LongInt;
    DrawPtr: Word;
    IsValid: Boolean;
    Modified: Boolean;
    Selecting: Boolean;
    Overwrite: Boolean;
    LockCount: Byte;
    UpdateFlags: Byte;
    KeyState: Integer;
    {$IFDEF SVISION}
    ScrollNum   : LongInt; { количество строк скроллинга в графическом реж}
    VertScroll  : Word;    { шаг вертикального скроллирования }
    SCY         : Word;    { высота привязанного шрифта }
    OvSelectMode: Byte;    { предыдущий режим выделения блока   }
    {$ENDIF}

    constructor Init(var Bounds: TRect;
      AHScrollBar, AVScrollBar: PLongScrollBar;
      AIndicator: PIndicator; ABufSize: Word);
    constructor Load(var S: TStream);
    procedure   Store(var S: TStream);
    destructor  Done; virtual;

    { "стандартные" процедуры поддержки видимого обьекта }
    procedure ChangeBounds(var Bounds: TRect); virtual;
    function  GetPalette: Views.PPalette; virtual;
    function  CursorVisible: Boolean;
    function  Valid(Command: Word): Boolean; virtual;
    procedure HandleEvent(var Event: TEvent); virtual;
    procedure ConvertEvent(var Event: TEvent); virtual;
    procedure SetCmdState(Command: Word; Enable: Boolean);
    procedure UpdateCommands; virtual;
    function  GetMousePtr(Mouse: TPoint): Word;
    procedure SetState(AState: Word; Enable: Boolean); virtual;

    { работа с буфером редактора }
    procedure InitBuffer;                         virtual;
    procedure DoneBuffer;                         virtual;
    function  SetBufSize(NewSize: Word): Boolean; virtual;
    procedure SetBufLen(Length: Word);            virtual;

    { процедуры перемещения по тексту }
    procedure SetCurPtr(P: Word; SelectMode: Byte);virtual;
    procedure ScrollTo(X, Y: LongInt);             virtual;
    function  GoToBlockStart : Boolean;            virtual;
    function  GoToBlockEnd   : Boolean;            virtual;
    procedure GoToPos(P:TLongPoint);


    { базовые процедуры вставки/удаления }
    function  InsertBuffer(var P: PEditBuffer; Offset, Length: Word;
              AllowUndo, SelectText: Boolean): Boolean; virtual;
    function  InsertFrom(Editor: PEditor): Boolean;     virtual;
    function  InsertText(Text: Pointer; Length: Word;
                         SelectText: Boolean): Boolean; virtual;
    procedure DeleteRange(StartPtr, EndPtr: Word;
         DelSelect: Boolean);          virtual;

    { базовые процедуры вставки/удаления для Undo файла}
    procedure uInsertText(AText: Pointer; ALen: Integer;
              StartPtr: TLongPoint);
    procedure uOverwriteText(AText: Pointer; ALen: Integer;
              StartPtr: TLongPoint);
    procedure uDeleteRange(StartPtr, EndPtr: TLongPoint);
    function  PointLine(ALine: Integer; var ALineLen: Integer) : Pointer;

    { начало/конец транзакций для Undo-файла }
    procedure StartTransaction;
    procedure StopTransaction;

    { иммитация нажатия клавиш }
    procedure NewLine;
    procedure Tab;

    procedure Undo; virtual;
    function  IsClipboard: Boolean;
    procedure ToggleInsMode;

    { процедуры низкого уровня для работы с буфером }
    function  CharPos(P, Target: Word): Integer;       virtual;
    function  CharPtr(P: Word; Target: Integer): Word; virtual;
    function  BufChar(P: Word): Char;
    function  BufPtr(P: Word) : Word;
    function  LineMove(P: Word; Count: Integer): Word;
    function  LineStart(P: Word): Word;                virtual;
    function  LineEnd(P: Word):   Word;                virtual;
    function  NextChar(P: Word): Word;                 virtual;
    function  NextLine(P: Word): Word;
    function  NextWord(P: Word): Word;
    function  PrevChar(P: Word): Word;                 virtual;
    function  PrevLine(P: Word): Word;
    function  PrevWord(P: Word): Word;

    { процедуры работы с блоками }
    procedure SetSelect(NewStart, NewEnd: Word; CurStart: Boolean); virtual;
    function  ClipCopy: Boolean;                       virtual;
    procedure ClipCut;                                 virtual;
    procedure ClipPaste;                         virtual;
    procedure DeleteSelect;                            virtual;
    procedure DeleteBlock;                             virtual;
    function  HasSelection: Boolean;
    procedure HideSelect;                              virtual;
    procedure StartSelect(P : Word);                   virtual;
    procedure EndSelect(P : Word);                     virtual;

    { процедуры отрисовки редактора }
    procedure Draw; virtual;
    procedure DrawLines(Y, Count: Integer; LinePtr: Word); virtual;
    procedure FormatLine(var DrawBuf; LinePtr: Word;
                Width: Integer; Colors: Word);
    procedure TrackCursor(Center: Boolean);
    procedure Lock;
    procedure Unlock;
    procedure Update(AFlags: Byte);
    procedure DoUpdate; virtual;
    {$IFDEF SVISION}
    procedure DoScroll;
    function  CheckKeys(Key : Word) : Word;
    {$ENDIF}

    { процедуры поиска/замены       }
    function  Search(const FindStr: String; Opts: Word): Boolean;
    procedure DoSearchReplace; {!!!} virtual;
    procedure Find;
    procedure Replace;

    { Для форматирования блоков: }
    function  CorrectSpecial(P, Len : Word) : Integer; virtual;
    procedure FormatParagraph(cm : Integer);           virtual;
    { сжимает абзац в строку }
    function  ParagraphToLine(cm : Integer;Ind : Integer) : Boolean;
    Procedure LineToParagraph;
    Function  NextSpace(P : Word) : Word;
    { определяет левый отступ строки }
    Function  GetIndent(P:Word)   : Integer;
    { переносит слово }
    Procedure SplitWord(Const FullWord : String; Var SplitPos:Word); virtual;
    Procedure SplitStr;
    function  GetThisWord(P: Word): String;
    function  ThisWord(P: Word): Word;

  end;

function DefEditorDialog(Dialog: Integer; Info: Pointer): Word;
{$IFNDEF SVISION}
function CreateFindDialog: PDialog;
function CreateReplaceDialog: PDialog;
{$ELSE}
function CreateFindDialog: PsDialog;
function CreateReplaceDialog: PsDialog;
{$ENDIF}
function StdEditorDialog(Dialog: Integer; Info: Pointer): Word;

const LongEditWindowNum  : Word = 0;

const
    SetterInited : Boolean  = False;
var
    TheSetter  : TSetter;  { "настройщик" редактора }

{$IFDEF USE_UNDO}
const
    UndoFile   : PsUndoFile = nil;
{$ENDIF USE_UNDO}

const
    Clipboard  : PEditor    = nil;

const
  EditorDialog: TEditorDialog = StdEditorDialog;
{$Ifdef MOST }
  EditorFlags: Word = efPromptOnReplace;
{$Else }
  EditorFlags: Word = efBackupFiles + efPromptOnReplace;
{$EndIf}

const
  FindStr: String[80] = '';
  ReplaceStr: String[80] = '';

type
  TFindDialogRec = record
    Find: String[80];
    Options: Word;
  end;

type
  TReplaceDialogRec = record
    Find: String[80];
    Replace: String[80];
    Options: Word;
  end;

implementation

{$I MINMAX.INC}
{$I SCAN.INC  }
{$I COUNT.INC }

{ TEditor }

procedure TEditor.StartTransaction;
begin
{$IFDEF USE_UNDO}
  if not IsClipboard and (UndoFile <> nil) then
    UndoFile^.StartTransaction;
{$ENDIF USE_UNDO}
end;

procedure TEditor.StopTransaction;
begin
{$IFDEF USE_UNDO}
  if not IsClipboard and (UndoFile <> nil) then
    UndoFile^.StopTransaction;
{$ENDIF USE_UNDO}
end;

procedure TEditor.uInsertText(AText: Pointer; ALen: Integer;
                              StartPtr: TLongPoint);
begin
 UndoMode := True;
 GoToPos(StartPtr);
 InsertText(AText,ALen,False);
 UndoMode := False;
end;

procedure TEditor.uOverwriteText(AText: Pointer; ALen: Integer;
                              StartPtr: TLongPoint);
begin
 UndoMode := True;
 GoToPos(StartPtr);
 DeleteRange(CurPtr,CurPtr+ALen,False);
 GoToPos(StartPtr);
 InsertText(AText,ALen,False);
 UndoMode := False;
end;

procedure TEditor.uDeleteRange(StartPtr, EndPtr: TLongPoint);
var
 OB, OE, OP : TLongPoint;
begin
 UndoMode := True;

 HideSelect;

 GoToPos(StartPtr);
 StartSelect(CurPtr);
 GoToPos(EndPtr);
 EndSelect(CurPtr);
 DeleteBlock;

 UndoMode := False;
end;

function  TEditor.PointLine(ALine: Integer; var ALineLen: Integer) : Pointer;
var
 P : TLongPoint;
begin
 P.X := 0;  P.Y := ALine;
 GoToPos(P);
 SetCurPtr(LineEnd(CurPtr),0);
 ALineLen := LineEnd(CurPtr) - LineStart(CurPtr);
 PointLine := @(Buffer^[LineStart(CurPtr)]);
end;

constructor TEditor.Init(var Bounds: TRect;
  AHScrollBar, AVScrollBar: PLongScrollBar;
  AIndicator: PIndicator; ABufSize: Word);
{$IFDEF SVISION}
var
 SV : TsView absolute Self;
{$ENDIF}
begin
  inherited Init(Bounds);
  GrowMode := gfGrowHiX + gfGrowHiY;
  Options := Options or ofSelectable;
  {$IFDEF SVISION}
  Options := Options or ofClipping;
  {$ENDIF}
  EventMask := evMouseDown + evKeyDown + evCommand + evBroadcast;
  ShowCursor;
  HScrollBar := AHScrollBar;
  VScrollBar := AVScrollBar;
  Indicator := AIndicator;
  BufSize := ABufSize;
  {$IFDEF SVISION}
  VertScroll := 1;
  SCY := SV.LinkedFont^.CharHeight;
  OvSelectMode := 0;
  {$ENDIF}
  InitBuffer;
  if Buffer <> nil
   then IsValid := True
   else
    begin
     IsValid := False;
     EditorDialog(edOutOfMemory, nil);
     BufSize := 0;
     {!!!}
     SetBufLen(0);
     Exit;
    end;
  SetBufLen(0);
  if (LongEditWindowNum = 0) then
   if not(SetterInited) then
    begin
     SetterInited := True;
     if (SetterFileName='') then
      TheSetter.Init('')
     else
      TheSetter.Init(GetPathString+SetterFileName);
    end;
end;

constructor TEditor.Load(var S: TStream);
begin
  inherited Load(S);
  GetPeerViewPtr(S, HScrollBar);
  GetPeerViewPtr(S, VScrollBar);
  GetPeerViewPtr(S, Indicator);
  S.Read(BufSize, SizeOf(Word));
  S.Read(SCY,SizeOf(SCY));
  InitBuffer;
  if Buffer <> nil
   then IsValid := True
   else
    begin
     IsValid := False;
     EditorDialog(edOutOfMemory, nil);
     BufSize := 0;
    end;
  Lock;
  {$IFDEF SVISION}
  VertScroll := 1;
  {$ENDIF}
  UndoMode := False;
  SetBufLen(0);
end;

destructor TEditor.Done;
begin
  DoneBuffer;
  if (LongEditWindowNum = 0)
   then
     TheSetter.Done;
  inherited Done;
end;

function TEditor.BufChar(P: Word): Char; assembler;
asm
        LES     DI,Self
        MOV     BX,P
        CMP     BX,ES:[DI].TEditor.CurPtr
        JB      @@1
        ADD     BX,ES:[DI].TEditor.GapLen
@@1:
        CMP     BX,ES:[DI].TEditor.BufSize
        JB      @@2
        MOV     BX,ES:[DI].TEditor.BufSize
        DEC     BX

@@2:    LES     DI,ES:[DI].TEditor.Buffer
        MOV     AL,ES:[DI+BX]
end;

function TEditor.BufPtr(P: Word): Word; assembler;
asm
        LES     DI,Self
        MOV     AX,P
        CMP     AX,ES:[DI].TEditor.CurPtr
        JB      @@1
        ADD     AX,ES:[DI].TEditor.GapLen
@@1:
end;

procedure TEditor.ChangeBounds(var Bounds: TRect);
begin
  SetBounds(Bounds);
  Delta.X := MaxLong(0, MinLong(Delta.X, Limit.X - Size.X div SCX));
  Delta.Y := MaxLong(0, MinLong(Delta.Y, Limit.Y - Size.Y div SCY));
  Update(ufView);
end;

function TEditor.CharPos(P, Target: Word): Integer;
var
  Pos: 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;

function TEditor.CharPtr(P: Word; Target: Integer): Word;
var
  Pos: Integer;
begin
  Pos := 0;
  while (Pos < Target) and (P < BufLen) and (BufChar(P) <> #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;

function TEditor.ClipCopy: Boolean;
begin
  ClipCopy := False;
  if (Clipboard <> nil) and (Clipboard <> @Self) then
  begin
    ClipCopy := Clipboard^.InsertFrom(@Self);
    Selecting := False;
    Update(ufUpdate);
    {$IFDEF SVISION}
    OvSelectMode := smExtend
    {$ENDIF SVISION}
  end;
end;

procedure TEditor.ClipCut;
begin
  if ClipCopy then DeleteSelect;
end;

procedure TEditor.ClipPaste;
begin
  if (Clipboard <> nil) and (Clipboard <> @Self) then
    InsertFrom(Clipboard);
end;

procedure TEditor.ConvertEvent(var Event: TEvent);
var
  ShiftState: Byte absolute $40:$17;
  Key: Word;
begin
  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;

function TEditor.CursorVisible: Boolean;
begin
  CursorVisible := (CurPos.Y >= Delta.Y) and (CurPos.Y < Delta.Y + Size.Y div SCY);
end;

procedure TEditor.DeleteRange(StartPtr, EndPtr: Word; DelSelect: Boolean);
begin
  if HasSelection and DelSelect then DeleteSelect else
  begin
    SetSelect(StartPtr, EndPtr, True);
    DeleteSelect;
  end;
end;

procedure TEditor.DeleteSelect;
var
 OPos  : TLongPoint;
 SPos  : TLongPoint;
 EPos    : TLongPoint;
 OSS,OSE : Word;
 SS,SE   : Word;
begin
  {$IFDEF USE_UNDO}
  if not(IsClipBoard) AND (UndoFile<>nil) AND not(UndoMode) then
   begin
    OSS := OSelStart; OSE := OSelEnd;
    SS := SelStart; SE := SelEnd;
    OPos := CurPos;
    SetCurPtr(SelStart,0); SPos := CurPos;
    SetCurPtr(SelEnd,0);   EPos := CurPos;
    UndoFile^.InsertText(@Self,SPos,EPos);
    if (OPos.X<>SPos.X) OR (OPos.Y<>SPos.Y) then
     begin
      GoToPos(OPos);
      SelStart := SS; SelEnd := SE;
     end
    else
     begin
      SetSelect(SS,SE,True);
     end;
    OSelStart := OSS; OSelEnd := OSE;
   end;
  {$ENDIF USE_UNDO}
  InsertText(nil, 0, False);
end;

procedure TEditor.DeleteBlock;
begin
 DeleteSelect;
end;

procedure TEditor.DoneBuffer;
begin
  if Buffer <> nil then
  begin
    FreeMem(Buffer, BufSize);
    Buffer := nil;
  end;
end;

procedure TEditor.DoSearchReplace;
var
  I: Word;
  C: TPoint;
begin
  repeat
    I := cmCancel;
    if not Search(FindStr, EditorFlags) then
    begin
      if EditorFlags and (efReplaceAll + efDoReplace) <>
          (efReplaceAll + efDoReplace) then
        EditorDialog(edSearchFailed, nil)
    end
    else if EditorFlags and efDoReplace <> 0 then
    begin
      I := cmYes;
      if EditorFlags and efPromptOnReplace <> 0 then
      begin
        MakeGlobal(Cursor, C);
        I := EditorDialog(edReplacePrompt, Pointer(C));
      end;
      if I = cmYes then
      begin
        Lock;
        InsertText(@ReplaceStr[1], Length(ReplaceStr), False);
        TrackCursor(False);
        Unlock;
      end;
    end;
  until (I = cmCancel) or (EditorFlags and efReplaceAll = 0);
end;

{$IFNDEF SVISION}
procedure TEditor.DoUpdate;
begin
  if UpdateFlags <> 0 then
  begin
    if CursorVisible then 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 HScrollBar <> nil then
      HScrollBar^.SetParams(Delta.X, 0, Limit.X - Size.X, Size.X div 2, 1);
    if VScrollBar <> nil then
       VScrollBar^.SetParams(Delta.Y, 0, Limit.Y - Size.Y, Size.Y - 1, 1);
    if Indicator <> nil then Indicator^.SetValue(CurPos, Modified);
    if State and sfActive <> 0 then UpdateCommands;
    UpdateFlags := 0;
  end;
end;
{$ELSE}
procedure TEditor.DoUpdate;
var R : TRect;
    P : TLongPoint;
    OSM : Word;
begin
  if UpdateFlags <> 0 then
  begin
    if (UpdateFlags and ufScroll <> 0)
       AND (UpdateFlags and ufBlock = 0)
       AND (abs(ScrollNum)<(Size.Y div SCY)-1)
    then
     DoScroll
    else
     if (UpdateFlags and ufScroll <> 0)
        and (ScrollNum <> 0)
        {AND (UpdateFlags and ufBlock = 0)} then
     begin
      OSM := OSelectMode;
      OSelectMode := 0;
      P := CurPos; Dec(P.Y,ScrollNum); ScrollNum := 0;
      GotoPos(P);
      OSelectMode := OSM;
     end;
    ScrollNum := 0;
    if CursorVisible then SetCursor((CurPos.X - Delta.X)*SCX,(CurPos.Y - Delta.Y)*SCY);
    if (UpdateFlags and ufView <> 0) or (UpdateFlags and ufBlock <> 0)
     then DrawView else
      if UpdateFlags and ufLine <> 0 then
        begin
         R.Assign(0,(CurPos.Y-Delta.Y)*SCY,Size.X,(CurPos.Y-Delta.Y+1)*SCY);
         DrawLines((CurPos.Y - Delta.Y)*SCY, SCY, LineStart(CurPtr));
        end;
    if HScrollBar <> nil then
      HScrollBar^.SetParams(Delta.X, 0, Limit.X - Size.X div SCX, (Size.X div SCX) div 2, 1);
    if VScrollBar <> nil then
       VScrollBar^.SetParams(Delta.Y, 0, Limit.Y - Size.Y div SCY, (Size.Y div SCY) - 1, 1);
    if Indicator <> nil then Indicator^.SetValue(CurPos, Modified);
    if State and sfActive <> 0 then UpdateCommands;
    UpdateFlags := 0;
  end;
end;
{$ENDIF SVISION}

{$IFDEF SVISION}
procedure TEditor.DoScroll;
var R  : TRect;
    DY : Integer;
    OPos,P : TLongPoint;
    SV : TsView absolute Self;
    CursorOn : Boolean;

 procedure DrawProc(var R1 : TRect); far;
  begin
   OPos := CurPos;
   if (ScrollNum > 0) then
    begin
     Inc(R1.B.Y,DY);
     GoToPos(Delta);
    end
   else
    begin
     Inc(R1.A.Y,DY);
     P:=Delta; Inc(P.Y,(Size.Y div SCY)); Inc(P.Y,ScrollNum);
     GoToPos(P);
    end;
   CopyImage(ActivePage,
            R1.A.X, R1.A.Y - DY,
            R1.B.X-1, R1.B.Y-DY-1,
            ActivePage,
            R1.A.X, R1.A.Y);
  end;

var
 OSM : Word;

begin
 OSM := OSelectMode;
 OSelectMode := 0;
 CursorOn := State and sfCursorVis <> 0;
 if CursorOn then HideCursor;
 HideMouse;
 DY := ScrollNum * SCY;
 GetExtent(R);
 if (ScrollNum > 0) then
  R.A.Y := R.A.Y + SCY * ScrollNum
 else
  R.B.Y := R.A.Y + ((Size.Y div SCY) + ScrollNum) * SCY;
 SV.DrawFigure(R,@DrawProc);
 DrawLines((CurPos.Y - Delta.Y)*SCY, abs(DY), LineStart(CurPtr));
 GetExtent(R);
 R.A.Y := R.B.Y - (Size.Y - (Size.Y div SCY) * SCY);
 SV.DrawRect(R,True,Lo(GetColor($0301)));
 if (OPos.Y >= Delta.Y) and (OPos.Y < Delta.Y + Size.Y div SCY)
  then GoToPos(OPos);
 UpdateFlags := ufScroll;
 ShowMouse;
 if CursorOn then ShowCursor;
 OSelectMode := OSM;
end;

function TEditor.CheckKeys(Key : Word) : Word; assembler;
asm
        PUSH    CX
        XOR     CX,CX
@@LP: MOV AH,1
 INT 16H
        JE      @@1
        CMP     AX,Key
        JNE     @@1
        MOV     AH,0
        INT     16H
        INC     CX
        JMP     @@LP
@@1:    MOV     AX,CX
        POP     CX
end;

{$ENDIF SVISION}

{$IFNDEF SVISION}
procedure TEditor.Draw;
begin
  if edDrawLine <> Delta.Y then
  begin
    DrawPtr := LineMove(DrawPtr, Delta.Y - edDrawLine);
    edDrawLine := Delta.Y;
  end;
  DrawLines(0, Size.Y, DrawPtr);
end;
{$ELSE}
procedure TEditor.Draw;
var
 R : Trect;
 SV : TsView absolute Self;
begin
  if edDrawLine <> Delta.Y then
  begin
    DrawPtr := LineMove(DrawPtr, Delta.Y - edDrawLine);
    edDrawLine := Delta.Y;
  end;
  DrawLines(0, Size.Y, DrawPtr);
  GetExtent(R);
  R.A.Y := R.B.Y - (Size.Y - (Size.Y div SCY) * SCY);
  SV.DrawRect(R,True,Lo(GetColor($0301)));
end;
{$ENDIF SVISION}

procedure TEditor.DrawLines(Y, Count: Integer; LinePtr: Word);
var
  Color: Word;
{$IFDEF SVISION}
  bkColor  : Word;
  SV       : TsView absolute Self;
  B        : array [0..MaxLineLength-1] of Word;
  SzX      : Word;
  S        : String;
  P        : TPoint;
  R        : TRect;
  S1,S2,S3 : String;
  C1,C2,C3 : Byte;
  B1,B2,B3 : Byte;

procedure BufferToStr(var Buf; var S1, S2, S3: String;
                      var C1, C2, C3 : Byte;
                      var B1, B2, B3 : Byte;
                      Len: Word); near;{assembler;}
var     C : Byte;
        B : array [0..(MaxLineLength-1)*2] of Byte absolute Buf;
        i : Word;
        L : Byte;
begin

 C := B[1];
 L := 0;
 S1 := ''; S2 := ''; S3 := '';

 for i:=0 to Len*2 do
  begin
   if (C <> B[i+1]) then
     begin
      C1 := C;
      if (C1 = Lo(Color)) then B1:=Lo(bkColor) else B1:=Hi(bkColor);
      C  := B[i+1];
      break;
     end;
   Inc(i);
   Inc(L);
   S1[L] := Char(B[i-1]);
  end;
  S1[0] := Char(L);

 L := 0;

 if (i < Len*2) then
 for i:=i to Len*2 do
  begin
   if (C <> B[i+1]) then
     begin
      C2 := C;
      if (C2 = Lo(Color)) then B2:=Lo(bkColor) else B2:=Hi(bkColor);
      C  := B[i+1];
      break;
     end;
   Inc(i);
   Inc(L);
   S2[L] := Char(B[i-1]);
  end;
  S2[0] := Char(L);

 L := 0;

 if (i < Len*2) then
   for i:=i to Len*2 do
    begin
     if (C <> B[i+1]) then
       begin
        C3 := C;
        if (C3 = Lo(Color)) then B3:=Lo(bkColor) else B3:=Hi(bkColor);
        C  := B[i+1];
        break;
       end;
     Inc(i);
     Inc(L);
     S3[L] := Char(B[i-1]);
    end;
    S3[0] := Char(L);
end;

{asm
        PUSH    DS
        LDS     SI,Buf
        LES     DI,S1
        CLD
        MOV     CX,Len
        INC     DI
        XOR     BL,BL
        MOV     C,0

@@1:    LODSB
        CMP     C,0
        JE      @@2
        CMP     C,AL
        JNE     @@3

@@2:    INC     BL
        LODSB
        STOSB
        LOOP    @@1

@@3:
        LES     DI,S1
        MOV     AL,BL
        STOSB

        POP     DS
end;}

{$ELSE}
  B: array[0..MaxLineLength - 1] of Word;
{$ENDIF}
begin
  {$IFDEF SVISION}
   SzX := Size.X div SCX + 1;
   P.X := 0;
   P.Y := Y;
   Color := GetColor($0402);
   bkColor:= GetColor($0301);
  {$ELSE}
   Color := GetColor($0201);
  {$ENDIF}
  while Count {$IFNDEF SVISION} >0 {$ELSE} >=SCY {$ENDIF} do
  begin
   {$IFNDEF SVISION}
    FormatLine(B, LinePtr, Delta.X + Size.X, Color);
    WriteBuf(0, Y, Size.X, 1, B[Delta.X]);
    LinePtr := NextLine(LinePtr);
    Inc(Y);
    Dec(Count);
   {$ELSE}
    FormatLine(B, LinePtr, Delta.X+SzX, Color);

    BufferToStr(B[Delta.X],S1,S2,S3,C1,C2,C3,B1,B2,B3,SzX);
    R.A.X := P.X; R.A.Y := P.Y;
    R.B := R.A; Inc(R.B.X,Size.X); Inc(R.B.Y,SCY);

      SV.DrawSysStr(R,S1,False,C1,B1);

    if (S2<>'') then
     begin
      Inc(R.A.X,Length(S1)*SCX); Inc(R.B.X,Length(S1)*SCX);
      SV.DrawSysStr(R,S2,False,C2,B2);
     end;

     if (S3<>'') then
     begin
      Inc(R.A.X,Length(S2)*SCX); Inc(R.B.X,Length(S2)*SCX);
      SV.DrawSysStr(R,S3,False,C3,B3);
     end;

    LinePtr := NextLine(LinePtr);
    Inc(P.Y,SCY);
    Dec(Count,SCY);
   {$ENDIF}
  end;
end;

procedure TEditor.Find;
var
  FindRec: TFindDialogRec;
begin
  with FindRec do
  begin
    Find := FindStr;
    Options := EditorFlags;
    if EditorDialog(edFind, @FindRec) <> cmCancel then
    begin
      FindStr := Find;
      EditorFlags := Options and not efDoReplace;
      DoSearchReplace;
    end;
  end;
end;

procedure TEditor.FormatLine(var DrawBuf; LinePtr: Word;
  Width: Integer; Colors: Word); assembler;
asm
{!!! - Изменена для работы с "нормальными" блоками }
        PUSH    DS
        LDS     BX,Self
        LES     DI,DrawBuf
        MOV     SI,LinePtr
        XOR     DX,DX
        CLD
        MOV     AH,Colors.Byte[0]
        MOV CX,DS:[BX].TEditor.CurPtr
        CMP CX,DS:[BX].TEditor.SelStart
        JA @@100
        CALL @@10
        JMP @@200
@@100: MOV CX,DS:[BX].TEditor.SelStart
 CALL @@10
        MOV AH,Colors.Byte[1]
        MOV CX,DS:[BX].TEditor.CurPtr
        CMP CX,DS:[BX].TEditor.SelEnd
        JA @@300
        CALL @@10
        ADD SI,DS:[BX].TEditor.GapLen
        JMP @@400
@@300: MOV CX,DS:[BX].TEditor.SelEnd
 CALL @@10
        MOV AH,Colors.Byte[0]
        MOV CX,DS:[BX].TEditor.CurPtr
        CALL @@10
        ADD SI,DS:[BX].TEditor.GapLen
        JMP @@500
@@200: ADD SI,DS:[BX].TEditor.GapLen
 MOV CX,DS:[BX].TEditor.SelStart
        ADD CX,DS:[BX].TEditor.GapLen
        CALL @@10
@@400: MOV AH,Colors.Byte[1]
 MOV CX,DS:[BX].TEditor.SelEnd
        ADD CX,DS:[BX].TEditor.GapLen
        CALL @@10
@@500:  MOV     AH,Colors.Byte[0]
        MOV     CX,DS:[BX].TEditor.BufSize
        CALL    @@10
        JMP     @@31
@@10:   SUB     CX,SI
        JA      @@11
        RETN
@@11:   LDS     BX,DS:[BX].TEditor.Buffer
        ADD     SI,BX
        MOV     BX,Width
@@12:   LODSB
        CMP     AL,' '
        JB      @@20
{ $IFNDEF SVISION}
@@13:   STOSW
(*{$ELSE}
@@13:   STOSB
{$ENDIF}*)
        INC     DX
@@14:   CMP     DX,BX
        JAE     @@30
        LOOP    @@12
        LDS     BX,Self
        SUB     SI,DS:[BX].TEditor.Buffer.Word[0]
        RETN
@@20:   CMP     AL,0DH
        JE      @@30
        CMP     AL,09H
        JNE     @@13
        MOV     AL,' '
{ $IFNDEF SVISION}
@@21:   STOSW
(*{$ELSE}
@@21:   STOSB
{$ENDIF}*)
        INC     DX
        TEST    DL,7
        JNE     @@21
        JMP     @@14
@@30:   POP     CX
{$IFNDEF SVISION}
@@31:   MOV     AL,' '
{$ELSE}
@@31:   MOV     AL,00
{$ENDIF}
        MOV     CX,Width
        SUB     CX,DX
        JBE     @@32
        REP     STOSW
@@32:   POP     DS
end;

function TEditor.GetMousePtr(Mouse: TPoint): Word;
begin
  MakeLocal(Mouse, Mouse);
  Mouse.X:=Mouse.X div SCX; Mouse.Y:=Mouse.Y div SCY;
  Mouse.X := Max(0, Min(Mouse.X, Size.X div SCX - 1));
  Mouse.Y := Max(0, Min(Mouse.Y, Size.Y div SCY - 1));
  GetMousePtr := CharPtr(LineMove(DrawPtr, Mouse.Y + Delta.Y - edDrawLine),
    Mouse.X + Delta.X);
end;

function TEditor.GetPalette:Views.PPalette;
const
  P: String[Length(CEditor)] = CEditor;
begin
  GetPalette := @P;
end;

function TEditor.ThisWord(P: Word): Word;
 Begin
  If (P > 0) then
   While (P > 0) and (BufChar(PrevChar(P)) in WordChars)
    do P := PrevChar(P);
  ThisWord:=P;
 end;

function TEditor.GetThisWord(P: Word): String;
 Var
  AThisWord : String;
 Begin
  AThisWord:='';
  P:=ThisWord(P);
  While (P < BufLen) and (BufChar(P) in WordChars) do
   Begin
    AThisWord:=AThisWord+BufChar(P);
    P := NextChar(P);
   end;
  GetThisWord:=AThisWord;
 end;

function  TEditor.GoToBlockStart : Boolean;
begin
 GoToBlockStart := True;
 SetCurPtr(SelStart,0);
end;

function  TEditor.GoToBlockEnd : Boolean;
begin
 GoToBlockEnd := True;
 SetCurPtr(SelEnd,0);
end;

procedure TEditor.GoToPos(P:TLongPoint);
var
 i   : LongInt;
 Lim : LongInt;
begin
 if (P.X=CurPos.X) AND (P.Y=CurPos.Y) then Exit;
 {$IFDEF SVISION} Inc(LockCount); {$ENDIF}
 if P.Y <> CurPos.Y then
  begin
   Lim := P.Y - CurPos.Y;
   for i:=1 To abs(Lim) do
    SetCurPtr(LineMove(CurPtr,Lim div abs(Lim)),0);
  end;
 SetCurPtr(LineStart(CurPtr),0);
 if (P.X<=CharPos(CurPtr,LineEnd(CurPtr))) then
  for i:=1 To P.X do
   SetCurPtr(NextChar(CurPtr),0);
 {$IFDEF SVISION} Dec(LockCount); {$ENDIF}
end;

procedure TEditor.HandleEvent(var Event: TEvent);

var
  ShiftState  : Byte absolute $40:$17;
  CenterCursor: Boolean;
  SelectMode  : Byte;
  i           : Integer;
  NewPtr      : Word;
  D           : TLongPoint;
  Mouse       : TPoint;
{$IFDEF SVISION}
  ODelta      : TLongPoint;
  SV          : TsView absolute Self;
{$ENDIF}

procedure CheckScrollBar(P: PLongScrollBar; var D: {$IFNDEF SVISION}
                                                    LongInt
                                                   {$ELSE}
                                                    Integer
                                                   {$ENDIF});
{$IFDEF SVISION}
var ODelta : TLongPoint;
{$ENDIF}
begin
  if (Event.InfoPtr = P) and (P^.Value <> D) then
  begin
    {$IFDEF SVISION}
    ODelta := Delta;
    {$ENDIF}
    D := P^.Value;
    {$IFDEF SVISION}
     if (ODelta.Y<>Delta.Y) then
      Message(@Self,evCommand,cmSetScroll,Pointer(ODelta.Y-Delta.Y))
     else
      Update(ufView);
    {$ELSE}
     Update(ufView);
    {$ENDIF}
  end;
end;

begin
  {$IFDEF SVISION}
   ScrollNum := 0;
   SCY := SV.LinkedFont^.CharHeight;
  {$ENDIF}
  inherited HandleEvent(Event);
  ConvertEvent(Event);
  CenterCursor := not CursorVisible;
  SelectMode := 0;
  if Selecting or (ShiftState and $03 <> 0) then SelectMode := smExtend;
  {$IFDEF SVISION}
  if (SelectMode = smExtend) then UpdateFlags := ufView or ufBlock
   else if (OvSelectMode = smExtend) OR (OvSelectMode = smDrag)
    then UpdateFlags := ufView or ufBlock;
  OvSelectMode := SelectMode;
  {$ENDIF}
  case Event.What of
    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;
          UpdateFlags := ufView {$IFDEF SVISION} {or ufBlock} {$ENDIF};
          {$IFDEF SVISION}
          OvSelectMode := smDrag;
          {$ENDIF}
          Unlock;
        until not MouseEvent(Event, evMouseMove + evMouseAuto);
      end;
    evKeyDown:
      case Event.CharCode of
        #32..#255:
          begin
            Lock;
            if Overwrite
               {and not HasSelection}
               and (CurPtr <> LineEnd(CurPtr))
             then
              begin
               DeleteRange(CurPtr,CurPtr+1,False);
              end; {SelEnd := NextChar(CurPtr)};
            If (Event.CharCode in [#32])
               and (CurPtr >= LineStart(CurPtr)+TheSetter._R.StrLen-1)
             then NewLine
             else InsertText(@Event.CharCode, 1, False);
            TrackCursor(False);
            Unlock;
          end;
      else
        Exit;
      end;
    evCommand:
      case Event.Command of
        cmFind: Find;
        cmReplace: Replace;
        cmSearchAgain: DoSearchReplace;
      else
        begin
          Lock;
          case Event.Command of
            cmTab: Tab;
            cmCut: ClipCut;
            cmCopy: ClipCopy;
            cmPaste: ClipPaste;
            cmUndo: Undo;
            cmClear: DeleteSelect;
            cmCharLeft: {$IFNDEF SVISION}
                         SetCurPtr(PrevChar(CurPtr), SelectMode);
                        {$ELSE}
                         for i:= 1 to CheckKeys(kbLeft)+1 do
                          begin
                           SetCurPtr(PrevChar(CurPtr), SelectMode);
                          end;
                        {$ENDIF SVISION}
            cmCharRight:{$IFNDEF SVISION}
                         SetCurPtr(NextChar(CurPtr), SelectMode);
                        {$ELSE}
                         for i:= 1 to CheckKeys(kbRight)+1 do
                          begin
                           SetCurPtr(NextChar(CurPtr), SelectMode);
                          end;
                        {$ENDIF}
            cmWordLeft: SetCurPtr(PrevWord(CurPtr), SelectMode);
            cmWordRight: SetCurPtr(NextWord(CurPtr), SelectMode);
            cmLineStart: SetCurPtr(LineStart(CurPtr), SelectMode);
            cmLineEnd: SetCurPtr(LineEnd(CurPtr), SelectMode);
            cmLineUp:  {$IFNDEF SVISION}
                        SetCurPtr(LineMove(CurPtr, -1), SelectMode);
                       {$ELSE}
                        begin
                         ScrollNum := VertScroll * (CheckKeys(kbUp)+1);
                         if (ScrollNum > (CurPos.Y)) then
                          ScrollNum := CurPos.Y;
                         SetCurPtr(LineMove(CurPtr, -ScrollNum), SelectMode);
                        end;
                       {$ENDIF}
            cmLineDown:{$IFNDEF SVISION}
                        SetCurPtr(LineMove(CurPtr, 1), SelectMode);
                       {$ELSE}
                        begin
                         ScrollNum := -VertScroll * (CheckKeys(kbDown)+1);
                         if (-ScrollNum > (Limit.Y-CurPos.Y)) then
                          ScrollNum := CurPos.Y - Limit.Y;
                         SetCurPtr(LineMove(CurPtr, -ScrollNum), SelectMode);
                        end;
                       {$ENDIF}
            cmPageUp: {$IFNDEF SVISION}
                      SetCurPtr(LineMove(CurPtr, -(Size.Y div SCY - 1)), SelectMode);
                      {$ELSE}
                      SetCurPtr(LineMove(CurPtr, -(Size.Y div SCY - 1)*(CheckKeys(kbPgUp)+1)), SelectMode);
                      {$ENDIF}
            cmPageDown:{$IFNDEF SVISION}
                       SetCurPtr(LineMove(CurPtr, (Size.Y div SCY - 1)), SelectMode);
                       {$ELSE}
                       SetCurPtr(LineMove(CurPtr, (Size.Y div SCY - 1)*(CheckKeys(kbPgDn)+1)), SelectMode);
                       {$ENDIF}
            cmTextStart: SetCurPtr(0, SelectMode);
            cmTextEnd:   SetCurPtr(BufLen, SelectMode);
            cmNewLine: NewLine;
            cmBackSpace: DeleteRange(PrevChar(CurPtr), CurPtr, False);
            cmDelChar: DeleteRange(CurPtr, NextChar(CurPtr), False);
            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(CurPtr);
            cmHideSelect: HideSelect;
            cmIndentMode: TheSetter._R.AutoIndent := not TheSetter._R.AutoIndent;
            {$IFDEF SVISION}
            cmSetScroll     : begin
                               ScrollNum := Integer(Event.InfoPtr);
                               if (ScrollNum <> 0) then
                                UpdateFlags := (UpdateFlags and ufBlock)
                                               or ufView or ufScroll;
                               UnLock;
                               ClearEvent(Event);
                               Exit;
                              end;
            {$ENDIF}
          else
            Unlock;
            Exit;
          end;
          {$IFDEF SVISION}
           ODelta := Delta;
           TrackCursor(False);
           if (ScrollNum <> 0) AND (ODelta.Y<>Delta.Y) then
            begin
             Dec(LockCount);
             Message(@Self,evCommand,cmSetScroll,Pointer(ScrollNum));
             ClearEvent(Event);
             Exit;
            end;
          {$ELSE}
           TrackCursor(False);
          {$ENDIF SVISION}
          Unlock;
        end;
      end;
    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;
  end;
  ClearEvent(Event);
end;

function TEditor.HasSelection: Boolean;
begin
  HasSelection := SelStart <> SelEnd;
end;

procedure TEditor.HideSelect;
begin
  Selecting := False;
  SetSelect(CurPtr, CurPtr, False);
  {$IFDEF SVISION}
  UpdateFlags := ufView or ufBlock;
  {$ENDIF SVISION}
end;

procedure TEditor.InitBuffer;
begin
  Buffer := MemAlloc(BufSize);
end;

function TEditor.InsertBuffer(var P: PEditBuffer; Offset, Length: Word;
  AllowUndo, SelectText: Boolean): Boolean;
var
  SelLen, DelLen, SelLines, Lines: Word;
  NewSize,Dum: Longint;
  OCurPos : TLongPoint;
begin
  OCurPos := CurPos;
  InsertBuffer := True;
  Selecting := False;
  SelLen := SelEnd - SelStart;
  if (SelLen = 0) and (Length = 0) then Exit;
  DelLen := 0;
  if (SelLen > 0) then DelLen := SelLen;
  NewSize := Longint(BufLen - SelLen + DelLen) + Length;
  if (NewSize > $FFF0) then
   NewSize := $FFF1;
  if (NewSize > BufSize) then
    if not SetBufSize(NewSize) then
    begin
      EditorDialog(edOutOfMemory, nil);
      InsertBuffer := False;
      SelEnd := SelStart;
      Exit;
    end;
  if (SelLen > 0) then
   SelLines := CountLines(Buffer^[BufPtr(SelStart)], SelLen)
  else
   SelLines := 0;
  if (CurPtr = SelEnd) AND (SelLen > 0) then
  begin
    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);
  edDrawLine := CurPos.Y;
  DrawPtr := LineStart(CurPtr);
  CurPos.X := CharPos(DrawPtr, CurPtr);
  if not SelectText then SelStart := CurPtr;
  SelEnd := CurPtr;
  Dum:=Length; Dum:=Dum-SelLen;
  Inc(BufLen,Dum);
  Dec(GapLen,Dum);
  Dum:=Lines; Dum:=Dum-SelLines;
  Inc(Limit.Y,Dum);
  Delta.Y := MaxLong(0, MinLong(Delta.Y, Limit.Y - Size.Y div SCY));
  if not(IsClipboard) then Modified := True;
  {$IFDEF USE_UNDO}
  if (UndoFile<>nil) AND not(IsClipBoard) AND not(UndoMode) then
   UndoFile^.DeleteRange(OCurPos,CurPos);
  {$ENDIF USE_UNDO}
  SetBufSize(BufLen);
  if (SelLines = 0) and (Lines = 0) then Update(ufLine) else Update(ufView);
end;

function TEditor.InsertFrom(Editor: PEditor): Boolean;
begin
  InsertFrom := InsertBuffer(Editor^.Buffer,
    Editor^.BufPtr(Editor^.SelStart),
    Editor^.SelEnd - Editor^.SelStart, False, IsClipboard);
end;

function TEditor.InsertText(Text: Pointer; Length: Word;
  SelectText: Boolean): Boolean;
begin
  InsertText:=InsertBuffer(PEditBuffer(Text),
                           0, Length, False, SelectText);
end;

function TEditor.IsClipboard: Boolean;
begin
  IsClipboard := (Clipboard = @Self);
end;

function TEditor.LineEnd(P: Word): Word; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        LES     BX,DS:[SI].TEditor.Buffer
        MOV     DI,P
        MOV     AL,0DH
        CLD
        MOV     CX,DS:[SI].TEditor.CurPtr
        SUB     CX,DI
        JBE     @@1
        ADD     DI,BX
        REPNE   SCASB
        JE      @@2
        MOV     DI,DS:[SI].TEditor.CurPtr
@@1:    MOV     CX,DS:[SI].TEditor.BufLen
        SUB     CX,DI
        JCXZ    @@4
        ADD     BX,DS:[SI].TEditor.GapLen
        ADD     DI,BX
        REPNE   SCASB
        JNE     @@3
@@2:    DEC     DI
@@3:    SUB     DI,BX
@@4:    MOV     AX,DI
        POP     DS
end;

function TEditor.LineMove(P: Word; Count: Integer): Word;
var
  Pos: Integer;
  I: 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;

function TEditor.LineStart(P: Word): Word; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        LES     BX,DS:[SI].TEditor.Buffer
        MOV     DI,P
        MOV     AL,0DH
        STD
        MOV     CX,DI
        SUB     CX,DS:[SI].TEditor.CurPtr
        JBE     @@1
        ADD     BX,DS:[SI].TEditor.GapLen
        ADD     DI,BX
        DEC     DI
        REPNE   SCASB
        JE      @@2
        SUB     BX,DS:[SI].TEditor.GapLen
        MOV     DI,DS:[SI].TEditor.CurPtr
@@1:    MOV     CX,DI
        JCXZ    @@4
        ADD     DI,BX
        DEC     DI
        REPNE   SCASB
        JNE     @@3
@@2:    INC     DI
        INC     DI
        SUB     DI,BX
        CMP     DI,DS:[SI].TEditor.CurPtr
        JE      @@4
        CMP     DI,DS:[SI].TEditor.BufLen
        JE      @@4
        CMP     ES:[BX+DI].Byte,0AH
        JNE     @@4
        INC     DI
        JMP     @@4
@@3:    XOR     DI,DI
@@4:    MOV     AX,DI
        POP     DS
end;

procedure TEditor.Lock;
begin
  Inc(LockCount);
end;

procedure TEditor.NewLine;
const
  CrLf: array[1..2] of Char = #13#10;
  InChar : Char =' ';
var
  I,B,ILen: Word;

begin
  B := LineStart(CurPtr);
  InsertText(@CrLf, 2, False);
  if TheSetter._R.AutoIndent
   then
    If TheSetter._R.IndentLen<0 data-blogger-escaped-:="B;" data-blogger-escaped-and="" data-blogger-escaped-begin="" data-blogger-escaped-curptr="" data-blogger-escaped-do="" data-blogger-escaped-else="" data-blogger-escaped-end="" data-blogger-escaped-false="" data-blogger-escaped-i="" data-blogger-escaped-if="" data-blogger-escaped-inc="" data-blogger-escaped-inserttext="" data-blogger-escaped-or="" data-blogger-escaped-then="" data-blogger-escaped-thesetter._r.indentlen="" data-blogger-escaped-uffer="" data-blogger-escaped-while="">0 then
       For i:=0 to TheSetter._R.IndentLen-1 do
        InsertText(@InChar,1, False);
end;

function TEditor.NextChar(P: Word): Word; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        MOV     DI,P
        CMP     DI,DS:[SI].TEditor.BufLen
        JE      @@3
        INC     DI
        CMP     DI,DS:[SI].TEditor.BufLen
        JE      @@3
        LES     BX,DS:[SI].TEditor.Buffer
        CMP     DI,DS:[SI].TEditor.CurPtr
        JB      @@1
        ADD     BX,DS:[SI].TEditor.GapLen
@@1:    CMP     ES:[BX+DI-1].Word,0A0DH
        JNE     @@2
        INC     DI
@@2:    {!!!}
@@3:
        CMP     DI,DS:[SI].TEditor.BufSize
        JB      @@4
        MOV     DI,DS:[SI].TEditor.BufSize
        DEC     DI
@@4:
 MOV     AX,DI
        POP     DS
end;

function TEditor.NextLine(P: Word): Word;
begin
  NextLine := NextChar(LineEnd(P));
end;

function TEditor.NextWord(P: Word): Word;
begin
  while (P < BufLen) and (BufChar(P) in WordChars) do
    P := NextChar(P);
  while (P < BufLen) and not (BufChar(P) in WordChars) do
    P := NextChar(P);
  NextWord := P;
end;

function TEditor.PrevChar(P: Word): Word; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        MOV     DI,P
        OR      DI,DI
        JE      @@3
        DEC     DI
        JE      @@3
        LES     BX,DS:[SI].TEditor.Buffer
        CMP     DI,DS:[SI].TEditor.CurPtr
        JB      @@1
        ADD     BX,DS:[SI].TEditor.GapLen
@@1:    CMP     ES:[BX+DI-1].Word,0A0DH
        JNE     @@2
        DEC     DI
@@2:    {!!!}
@@3:
 MOV     AX,DI
        POP     DS
end;

function TEditor.PrevLine(P: Word): Word;
begin
  PrevLine := LineStart(PrevChar(P));
end;

function TEditor.PrevWord(P: Word): Word;
begin
  while (P > 0) and not (BufChar(PrevChar(P)) in WordChars) do
    P := PrevChar(P);
  while (P > 0) and (BufChar(PrevChar(P)) in WordChars) do
    P := PrevChar(P);
  PrevWord := P;
end;

procedure TEditor.Replace;
var
  ReplaceRec: TReplaceDialogRec;
begin
  with ReplaceRec do
  begin
    Find := FindStr;
    Replace := ReplaceStr;
    Options := EditorFlags;
    if EditorDialog(edReplace, @ReplaceRec) <> cmCancel then
    begin
      FindStr := Find;
      ReplaceStr := Replace;
      EditorFlags := Options or efDoReplace;
      DoSearchReplace;
    end;
  end;
end;

procedure TEditor.ScrollTo(X, Y: LongInt);
begin
  X := MaxLong(0, MinLong(X, Limit.X - Size.X div SCX));
  Y := MaxLong(0, MinLong(Y, Limit.Y - Size.Y div SCY));
{!!!}
  if (X <> Delta.X) or (Y <> Delta.Y) then
  begin
    Delta.X := X;
    Delta.Y := Y;
    Update(ufView);
  end;
end;

function TEditor.Search(const FindStr: String; Opts: Word): Boolean;
var
  I, Pos: Word;
begin
  Search := False;
  Pos := CurPtr;
  repeat
    if Opts and efCaseSensitive <> 0 then
      I := Scan(Buffer^[BufPtr(Pos)], BufLen - Pos, FindStr,0)
    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;
        SetCurPtr(I,0);
        StartSelect(CurPtr);
        EndSelect(CurPtr + Length(FindStr));
        Selecting := False;
        {$IFDEF SVISION}
        OvSelectMode := smExtend;
        {$ENDIF}
        TrackCursor(not CursorVisible);
        Unlock;
        Search := True;
        Exit;
      end else Pos := I + 1;
    end;
  until I = sfSearchFailed;
end;

procedure TEditor.SetBufLen(Length: 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;
  Limit.X := MaxLineLength;
  Limit.Y := CountLines(Buffer^[GapLen], BufLen) + 1;
  edDrawLine := 0;
  DrawPtr := 0;
  Modified := False;
  Update(ufView);
end;

function TEditor.SetBufSize(NewSize: Word): Boolean;
begin
  SetBufSize := NewSize <= BufSize;
end;

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;

procedure TEditor.SetCurPtr(P: Word; SelectMode: Byte);
var
  Anchor: 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;

procedure TEditor.SetSelect(NewStart, NewEnd: Word; CurStart: Boolean);
var
  Flags: Byte;
  P, L: Word;
begin
  if CurStart then P := NewStart else P := NewEnd;
  Flags := ufUpdate;
  {$IFNDEF SVISION}
  if (NewStart <> SelStart) or (NewEnd <> SelEnd) then
    if (NewStart <> NewEnd) or (SelStart <> SelEnd) then
      Flags := ufView;
  {$ENDIF}
  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;
    edDrawLine := CurPos.Y;
    DrawPtr := LineStart(P);
    CurPos.X := CharPos(DrawPtr, P);
    SetBufSize(BufLen);
  end;
  SelStart := NewStart;
  SelEnd := NewEnd;
  Update(Flags);
end;

procedure TEditor.SetState(AState: Word; Enable: Boolean);
begin
  inherited SetState(AState, Enable);
  case AState of
    sfActive:
      begin
        if HScrollBar <> nil then HScrollBar^.SetState(sfVisible, Enable);
        if VScrollBar <> nil then VScrollBar^.SetState(sfVisible, Enable);
        if Indicator  <> nil then Indicator^.SetState(sfVisible, Enable);
        {$IFDEF USE_UNDO}
        if Enable AND (UndoFile <> nil) then UndoFile^.Clear;
        {$ENDIF USE_UNDO}
        UpdateCommands;
      end;
    sfExposed:
      if Enable then Unlock;
  end;
end;

Procedure TEditor.SplitWord(Const FullWord : String; Var SplitPos:Word);
 Var
  I   : Byte;
  Len : Byte;

 Function GlasnPresent(Const Slog : String) : Boolean;
  Var
   I : Byte;
  Begin
   GlasnPresent:=False;
   For I:=1 to Length(Slog) do
    If Slog[I] in Glasn
     then
      Begin
       GlasnPresent:=True;
       Break;
      end;
  end;

 Function ValidSlog(Const Slog : String;SlogNum : Byte) : Boolean;
  Begin
   If (Length(Slog)>1) and
      GlasnPresent(Slog) and
      ((SlogNum=1) or Not (Slog[1] in Znaks))
    then ValidSlog:=True
    else ValidSlog:=False;
  end;

Begin
  if TheSetter._R.MaySplit then
   begin
    Len := Length(FullWord);
    While SplitPos>1 do
     Begin
      If ValidSlog(Copy(FullWord,1,SplitPos),1) and
         ValidSlog(Copy(FullWord,SplitPos+1,Len),2) and
         ((FullWord[SplitPos] in Glasn+Znaks) or
          Not (FullWord[SplitPos+1] in Glasn))
       then Exit
       else Dec(SplitPos);
     end;
     SplitPos:=0;
   end
  else
   SplitPos := 0;
end;

Function TEditor.GetIndent(P:Word) : Integer;
 Var
  I : Integer;
 Begin
  P:=LineStart(P);
  I:=0;
  if (BufChar(P)=#13) then I:=-1
   else
    While (P<BufLen) AND (BufChar(P)=#32) do
      Begin
       Inc(I);
{       Inc(P);}
       P:=NextChar(P);
      end;
  GetIndent:=I;
 end;

Function TEditor.NextSpace(P : Word) : Word;
 const SP : Char = #32;
 Begin
  Repeat
   P:=NextChar(P);
   if BufChar(P)=#9 then
    begin
     DeleteRange(P,NextChar(P),False);
     InsertText(@SP,1,False);
    end;
  until (BufChar(P)=#32) or (P+1 >= BufLen);
  NextSpace:=P;
 end;


function TEditor.ParagraphToLine(cm : Integer; Ind : Integer) : Boolean;
 Var
  P    : Word;
  OP   : TLongPoint;
  Cnt  : LongInt;
  SCnt : String;
 Begin
  Cnt := 0; SCnt := '';
  CtrlBreakHit := False;
  ParagraphToLine := True;
  OP := CurPos;
  P  := LineStart(CurPtr);
  {!!!}
  if ((cm = cmFormatInt) AND (GetIndent(P)<>TheSetter._R.FirstLineIndent)) then
   Ind:=GetIndent(P);
  If (BufChar(LineStart(CurPtr)) = #32)
   then
    If (NextWord(P)<=LineEnd(CurPtr))
     then P:=NextWord(P)
     else
      Begin
       SetCurPtr(LineStart(NextLine(P)),0);
       EXIT;
      end;

  Repeat
   Inc(Cnt);
   Str(Cnt,SCnt);
   SCnt := 'Просмотрено: ' + SCnt + ' строк.';
   DecStatusCount;
   SetNewStatus(SCnt);
   While (NextSpace(P)<LineEnd(P)) do
    Begin
     P:=NextSpace(P);
     SetCurPtr(P,0);
     If (BufChar(NextChar(P))=#32)
      then
       Begin
        While (P<BufLen) AND (BufChar(NextChar(P))=#32) do P:=NextChar(P);
        DeleteRange(CurPtr,P,False);
       end;
     P:=CurPtr;
    end;
   If (P >= BufLen) then exit;
   {!!!}
   If (Ind = -1) then Ind:=GetIndent(NextLine(P));
   If (Ind >=0) AND
      ((cm = cmFormatInt) AND (GetIndent(NextLine(P))=Ind)) XOR
      ((cm = cmFormatNew) AND (GetIndent(NextLine(P))=Ind)) XOR
      ((cm = cmFormat) AND (GetIndent(NextLine(P))=TheSetter._R.IndentLen))
    then
     Begin
      {!!!}
      SetCurPtr(LineEnd(P),0);
      P:=NextLine(P);
      If P >= BufLen then exit;
      P:=LineStart(P);
      While (P<BufLen) AND (BufChar(P)=#32) do P:=NextChar(P);
      DeleteRange(CurPtr,P,False);
      P:=PrevChar(CurPtr);
      If (BufChar(P)=CarrySign) and
         (BufChar(PrevChar(P)) in WordChars) and
         (BufChar(NextChar(P)) in WordChars)
       then
        DeleteRange(P,NextChar(P),False)
       else
        If (BufChar(P)<>#32)
         then
          Begin
           SetCurPtr(NextChar(P),0);
           InsertText(@SpaceCh,1,False);
          end;
     end
    else
     Begin
      Break;
     end;
  until CtrlBreakHit {и не конец файла};
  if CtrlBreakHit then
   begin
    CtrlBreakHit := False;
    ParagraphToLine := False;
   end;
  GoToPos(OP);
  CtrlBreakHit := False;
 end;

Procedure TEditor.LineToParagraph;
var
 Cnt  : LongInt;
 SCnt : String;
 Begin
  Cnt := 0; SCnt := '';
  While (LineEnd(CurPtr)-LineStart(CurPtr)>TheSetter._R.StrLen) do
   begin
    Inc(Cnt);
    Str(Cnt,SCnt);
    SCnt := 'Отформатировано: ' + SCnt + ' строк.';
    DecStatusCount;
    SetNewStatus(SCnt);
    Splitstr;
   end;
 end;

procedure TEditor.FormatParagraph(cm : Integer);
 var
  LS : Word;
  Ind: Integer;
  S  : String;
  i  : integer;
 Begin
  SetNewStatus('Форматирование абзаца...');
  StartTransaction;
  Ind := -1;
  if (cm = cmFormatNew) then
   begin
     LS := LineStart(CurPtr);
     Ind := GetIndent(CurPtr);
     if (Ind>0) then DeleteRange(LS,LS+Ind,False);
     if (TheSetter._R.FirstLineIndent > 0) then
      begin
       S := '';
       for i:=1 to TheSetter._R.FirstLineIndent do
        S := S + #32;
       SetCurPtr(LineStart(CurPtr),0);
       InsertText(@S[1],Length(S),False);
       SetCurPtr(LineStart(CurPtr),0);
      end;
   end;
  if ParagraphToLine(cm,-1) then
   LineToParagraph;
  SetCurPtr(NextLine(CurPtr),0);
  StopTransaction;
  SetOldStatus;
 end;

function  TEditor.CorrectSpecial(P, Len : Word) : Integer;
begin
 CorrectSpecial := 0;
end;

Procedure TEditor.SplitStr;
  Var
   SpWord       : String;
   SplitNum : Word;
   DecSpNum : Integer;
   SaveCurPtr : Word;
   P   : Word;
   IncrCh : Integer;
   SLen         : Word;
  Begin
   IncrCh:=0;
   DecSpNum:=0;
   SaveCurPtr:=CurPtr;
   SLen := TheSetter._R.StrLen;
   SLen := SLen+CorrectSpecial(LineStart(CurPtr),SLen);
   P:=LineStart(CurPtr)+SLen;
   SpWord:=GetThisWord(P);
   If SpWord=''
    then While BufChar(PrevChar(P))=#32 do P:=PrevChar(P)
    else
     If (ThisWord(P)>=P-1)
      then
       Begin
        P:=ThisWord(P);
        While BufChar(PrevChar(P))=#32 do P:=PrevChar(P)
       end
      else
       Begin
        P:=ThisWord(P);
        SplitNum:=LineStart(P)+SLen-1;
        SplitNum:=SplitNum-P;
        SplitWord(SpWord,SplitNum);
        If SplitNum>0
         then
          Begin
           SetCurPtr(P+SplitNum,0);
           InsertText(@CarrySign, 1, False);
           P:=CurPtr;
          end
         else
           While BufChar(PrevChar(P))=' ' do P:=PrevChar(P);
       end;

   if TheSetter._R.MaySmooth then
    begin
     IncrCh:=LineStart(CurPtr)+SLen-P;
     If (IncrCh>0)
        and (LineStart(P)<PrevWord(P))
        and (LineStart(P)<PrevWord(PrevWord(P)))
      then
       Begin
        While IncrCh>0 do
         Begin
          While (LineStart(P)<PrevWord(PrevWord(P)))
           and (IncrCh>0) do
           Begin
            P:=PrevWord(P);
            SetCurPtr(P,0);
            InsertText(@SpaceCh, 1, False);
            Dec(IncrCh);
           end;
          P:=LineStart(P)+SLen-IncrCh;
         end;
       P:=LineStart(P)+SLen;
       end;
    end;{if MaySmooth ...}
   SetCurPtr(P,0);
   NewLine;
   P:=LineStart(CurPtr)+TheSetter._R.IndentLen;
   If BufChar(P)=#32
    then DeleteRange(P,NextChar(P),False);
 end;

procedure TEditor.StartSelect(P : Word);
begin
  Selecting := True;
  SetSelect(P,SelEnd,True);
  {$IFDEF SVISION}
  UpdateFlags := UpdateFlags or ufBlock;
  {$ENDIF}
end;

procedure TEditor.EndSelect(P : Word);
begin
  Selecting := True;
  SetSelect(SelStart,P,False);
  {$IFDEF SVISION}
  UpdateFlags := UpdateFlags or ufBlock;
  {$ENDIF}
end;

procedure TEditor.Store(var S: TStream);
begin
  inherited Store(S);
  PutPeerViewPtr(S, HScrollBar);
  PutPeerViewPtr(S, VScrollBar);
  PutPeerViewPtr(S, Indicator);
  S.Write(BufSize, SizeOf(Word));
  S.Write(SCY,SizeOf(SCY));
end;

procedure TEditor.ToggleInsMode;
begin
  Overwrite := not Overwrite;
  SetState(sfCursorIns, not GetState(sfCursorIns));
end;

procedure TEditor.Tab;
var i : Byte;
    S : String;
Begin
 Lock;
 S := '';
 for i := 1 to (TheSetter._R.TabLen-(CurPos.X mod TheSetter._R.TabLen)) do
  S := S + #32;
 if (Ord(S[0])<>0) then
  InsertText(@S[1],i,False);
 TrackCursor(not CursorVisible);
 UnLock;
end;

procedure TEditor.TrackCursor(Center: Boolean);
begin
  if Center then
    ScrollTo(CurPos.X - Size.X div SCX + 1, CurPos.Y - (Size.Y div SCY) div 2) else
    ScrollTo(MaxLong(CurPos.X - Size.X  div SCX + 1, MinLong(Delta.X, CurPos.X)),
      MaxLong(CurPos.Y - Size.Y div SCY + 1, MinLong(Delta.Y, CurPos.Y)));
end;

procedure TEditor.Undo;
begin
{$IFDEF USE_UNDO}
 SetNewStatus('Идет откат предыдущей операции...');
 if UndoFile <> nil then begin
   UndoFile^.Undo(@Self);
   UpdateCommands;
 end;
 SetOldStatus;
{$ENDIF USE_UNDO}
end;

procedure TEditor.Unlock;
begin
  if LockCount > 0 then
  begin
    Dec(LockCount);
    if LockCount = 0 then DoUpdate;
  end;
end;

procedure TEditor.Update(AFlags: Byte);
begin
  UpdateFlags := UpdateFlags or AFlags;
  if LockCount = 0 then DoUpdate;
end;

procedure TEditor.UpdateCommands;
begin
  if not(IsClipboard) then
  begin
    SetCmdState(cmCut, HasSelection);
    SetCmdState(cmCopy, HasSelection);
    SetCmdState(cmPaste, (Clipboard <> nil) and (Clipboard^.HasSelection));
  end;
  SetCmdState(cmClear, HasSelection);
  SetCmdState(cmPrint, True);
  SetCmdState(cmFind, True);
  SetCmdState(cmReplace, True);
  SetCmdState(cmSearchAgain, True);
  SetCmdState(cmSettings, True);
  SetCmdState(cmFormat, True);
  SetCmdState(cmFormatNew, True);
  SetCmdState(cmFormatInt, True);
end;

function TEditor.Valid(Command: Word): Boolean;
begin
  Valid := IsValid;
end;

{$I DIALOG.INC}

{$UNDEF FROM_EDITOR}

end. {Editor}

Там кстати и про SuperVision упоминания есть.

А ещё была "штука" -"редактор шаблонных документов". До сих пор мной не повторённая.

Это когда часть текста - ReadOnly а часть - "поля для заполнения".

Типа "формы ввода".

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/OLD/LONGEDIT.PAS

Ну и "длинный буфер":
https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/NEW/LONGBUF.PAS

Исходники иногда "рвут крышу" - типа "данные обращаются к View". Зато они сильно документированы. Я сейчас столько комментариев - не пишу. Даже удивительно, что уогда-то писал.

А вот тут:

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/wEditor/Editor/OE/ALTABLER.PAS

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/wEditor/Editor/OE/ALTABLER.INC

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/wEditor/Editor/OE/ALTABLEC.PAS

https://sourceforge.net/p/rumtmarc/code-0/HEAD/tree/trunk/DOSEditors/wEditor/Editor/OE/ALTABLEC.INC

Ещё смешнее - реализация таблиц для Plain-Text редактора.


unit alTableR;

interface

uses
  OvcBase,
  alEditN,
  alPara,
  alUndo,
  alTableC,
  alEditCo,
  cChars;

type
  TColumnArray = array [1..MaxColumnCount] of TTableColumn;

{=========================================================================}

type
  TTableRow = class(TParaNode)

   protected
    {---------------------------------------------------------------------}
    { добавленные поля }
    FColumnArray  : TColumnArray;
    FColumnCount  : Integer;
    FTableWidth   : Integer;
    FLeftDelta    : Integer;
    FTree         : TParaList;

   protected
    {---------------------------------------------------------------------}
    { методы для работы со свойствами }
    function  GetColumnWidth(i: Integer) : Integer;
      { - возвращает ширину i-й колонки }
    procedure SetColumnWidth(i: Integer; Value: Integer);
      { - устанавливает ширину i-й колонки }
    procedure SetLeftDelta(Value: Integer);
      { - устанавливает отступ таблицы }
    function  GetColumn(i : Integer) : TTableColumn;
      { - возвращает i-ю колонку }
    procedure SetColumn(i: Integer; Value: TTableColumn);
      { - присваивает i-ю колонку }

   public
    {---------------------------------------------------------------------}
    { переопределенные методы }
    function  NthLine(N : Integer; var Len : Word) : PChar;
      override;
      {-return a pointer to the Nth line of this paragraph; Len is its length}
    procedure Recalc;
      override;
      {-calculate the number of lines in this paragraph given the specified
        wrap column}
    function  PosToLine(Pos : Integer; var Col : Integer) : Integer;
      override;
      {-given a position in the paragraph, return the line and Col}
    function PEQ(PN: TParaNode): Boolean;
      override;
      {-сравнивает параметры двух параграфов на идентичность}
    function GetHIndent(L : Integer) : Integer;
      override;
      {-}

    {---------------------------------------------------------------------}
    { переопределенные методы для записи/чтения строки таблицы }
    procedure StoreParas(OnWrite: TalEditorReadWriteEvent; var Res: LongInt);
      override;
      {-сохраняет параметры строки таблицы}
    constructor LoadParas(OnRead: TalEditorReadWriteEvent; var Res: LongInt);
      override;
      {-читает параметры строки таблицы}
    procedure StoreText(Editor: TOvcBase; OnWrite: TalEditorReadWriteEvent; var Res : LongInt);
      override;
      {-сохраняет текст параграфа}
    procedure LoadText(Editor: TOvcBase; OnRead: TalEditorReadWriteEvent; Count: LongInt;
                       var Res: LongInt);
      override;
      {-читает текст параграфа}

    {---------------------------------------------------------------------}
    { добавленные методы }
    constructor Create(aTree: TParaList; aColumnCount,
                       aLeftDelta, aTableWidth,aColumnWidth: Integer);
      virtual;
      {-создает новую строку таблицы не заполняя ее ячейками}
    constructor CreateSame(aTree: TParaList; TR: TTableRow);
      virtual;
      {-создает строку таблицы с параметрами как у TR}
    constructor CreateEmpty(aTree: TParaList; aColumnCount, aTableWidth: Integer);
      virtual;
      {-создает пустую строку таблицы заполнив ее ячейками}
    destructor  Destroy;
      override;
      {-}
    function    ScreenPosToTable(P : Integer; var TP: TTablePoint): TTableColumn;
      {-из позиции на экране находит позицию в таблице }
    function    GlobalPosToTable(C: Integer; P : Integer) : Integer;
      { - из позиции на экране находит позицию в таблице }
    procedure   CorrectScreenPos(var CurLine : LongInt;
                                 var CurCol  : Integer;
                                 var LinePos : Integer);
      {-поправляет позицию на экране так
        чтобы она правильно попадала в ячейку таблицы }
    procedure   MoveCaretToPP(C: Integer;
                              Para: LongInt; var Pos: Integer);
      { - из позиции в таблице - делает позицию на экране }
    procedure   MoveCaretTo(C: Integer;
                            Line: LongInt; var Col: Integer);
      {-из позиции в таблице - делает позицию на экране }
    function    GetHighlightBorder(BC, EC: Integer;
                                   var HBC, HEC: Integer) : Boolean;
      {-определяет границы колонок которыми ограничено выделение }
    procedure   GetHighlightColumns(Left,Right: Integer;
                                    var LeftColumn, RightColumn: Integer);
      {-возвращает номера колонок выделенных в данной строке таблицы}
    procedure   InsertColumn(C: Integer);
      {-вставляет новую колонку по номеру C}
    procedure   PlaceColumn(C: Integer; TC: TTableColumn);
      {-вставляет новую колонку TC по номеру C}
    procedure   DeleteColumn(C: Integer);
      {-удаляет колонку по номеру C}
    procedure   SwapColumns(C1,C2: Integer);
      {-меняет колонки местами}

    {---------------------------------------------------------------------}
    { свойства }
    property Tree : TParaList
      read FTree
      write FTree;
      {-дерево параграфов-владелец данной строки таблицы}

    property Column [i : Integer] : TTableColumn
      read GetColumn
      write SetColumn;
      default;
      {- i-я ячеека }

    property ColumnWidth [i : Integer] : Integer
      read GetColumnWidth
      write SetColumnWidth;
      {-ширина i-й колонки }

    property ColumnCount : Integer
      read FColumnCount
      write FColumnCount;
      {-число колонок }

    property TableWidth : Integer
      read FTableWidth
      write FTableWidth;
      {-ширина таблицы }

    property LeftDelta : Integer
      read FLeftDelta
      write SetLeftDelta;
      {-левый отступ таблицы }

  end; { TTableRow }

{=========================================================================}

implementation

uses
  alEdit,
  alParaT,
  MinMax;

{$I altableR.inc }

end.

Я уж и забыл, что "такое было".

 Под каким-нибудь Lazarus'ом или Delphi1 это даже наверное соберётся.

Комментариев нет:

Отправить комментарий