356 lines
7.6 KiB
PHP
356 lines
7.6 KiB
PHP
{$ifdef none}begin end;{$endif}
|
|
|
|
procedure TATStrings.TextInsert(AX, AY: integer; const AText: atString; AOverwrite: boolean; out AShift, APosAfter: TPoint);
|
|
var
|
|
Str, StrLead, StrTail: atString;
|
|
List: TATStrings;
|
|
bWithEol, bInsertAtEnd: boolean;
|
|
begin
|
|
AShift.X:= 0;
|
|
AShift.Y:= 0;
|
|
APosAfter.X:= AX;
|
|
APosAfter.Y:= AY;
|
|
|
|
if not IsIndexValid(AY) then Exit;
|
|
if AX<0 then Exit;
|
|
if AText='' then Exit;
|
|
|
|
Str:= Lines[AY];
|
|
bInsertAtEnd:= AX>=Length(Str);
|
|
|
|
if not bInsertAtEnd then
|
|
begin
|
|
StrLead:= Copy(Str, 1, AX);
|
|
StrTail:= Copy(Str, AX+1, MaxInt);
|
|
end
|
|
else
|
|
begin
|
|
StrLead:= Str+StringOfChar(' ', AX-Length(Str));
|
|
StrTail:= '';
|
|
end;
|
|
|
|
if AOverwrite then
|
|
Delete(StrTail, 1, Length(AText));
|
|
|
|
//------------------
|
|
//Insert single line
|
|
|
|
if not SWithBreaks(AText) then
|
|
begin
|
|
Lines[AY]:= StrLead+AText+StrTail;
|
|
if not AOverwrite then
|
|
AShift.X:= Length(AText);
|
|
APosAfter.X:= AX+Length(AText);
|
|
Exit
|
|
end;
|
|
|
|
//----------------------
|
|
//Insert multi-line text
|
|
|
|
List:= TATStrings.Create;
|
|
BeginUndoGroup;
|
|
try
|
|
List.LoadFromString(StrLead+AText);
|
|
List.ActionDeleteFakeLine;
|
|
if List.Count=0 then Exit;
|
|
|
|
if StrTail<>'' then
|
|
Lines[AY]:= StrTail
|
|
else
|
|
if Lines[AY]<>'' then
|
|
LineDelete(AY);
|
|
|
|
bWithEol:= SEndsWith(AText, #10) or
|
|
SEndsWith(AText, #13) or
|
|
bInsertAtEnd //need for (paste N lines, no final eol, at end of line)
|
|
;
|
|
LineInsertStrings(AY, List, bWithEol);
|
|
|
|
if bWithEol then
|
|
begin
|
|
APosAfter.X:= 0;
|
|
APosAfter.Y:= AY+List.Count;
|
|
end
|
|
else
|
|
begin
|
|
APosAfter.X:= Length(List.Lines[List.Count-1]);
|
|
APosAfter.Y:= AY+List.Count-1;
|
|
end;
|
|
AShift.Y:= APosAfter.Y-AY;
|
|
|
|
finally
|
|
FreeAndNil(List);
|
|
EndUndoGroup;
|
|
end;
|
|
end;
|
|
|
|
procedure TATStrings.TextDeleteLeft(AX, AY: integer; ALen: integer; out AShift,
|
|
APosAfter: TPoint);
|
|
var
|
|
Str, StrPrev: atString;
|
|
begin
|
|
AShift.X:= 0;
|
|
AShift.Y:= 0;
|
|
APosAfter.X:= AX;
|
|
APosAfter.Y:= AY;
|
|
if not IsIndexValid(AY) then Exit;
|
|
Str:= Lines[AY];
|
|
|
|
//handle spec case: caret on last fake line, BkSp pressed:
|
|
//delete fake line,
|
|
//delete EOL at prev line
|
|
if (AX=0) and (AY=Count-1) and (AY>0) and IsLastLineFake then
|
|
begin
|
|
LineDelete(AY, false);
|
|
LinesEnds[AY-1]:= cEndNone;
|
|
AShift.Y:= -1;
|
|
APosAfter.X:= Length(Lines[AY-1]);
|
|
APosAfter.Y:= AY-1;
|
|
exit
|
|
end;
|
|
|
|
if AX>0 then
|
|
begin
|
|
if AX<=Length(Str) then
|
|
begin
|
|
System.Delete(Str, Max(1, AX+1-ALen), ALen);
|
|
Lines[AY]:= Str;
|
|
end;
|
|
AShift.X:= -Min(AX, ALen);
|
|
APosAfter.X:= Max(0, AX-ALen);
|
|
end
|
|
else
|
|
if AY>0 then
|
|
begin
|
|
StrPrev:= Lines[AY-1];
|
|
Lines[AY-1]:= StrPrev+Str;
|
|
LineDelete(AY);
|
|
AShift.Y:= -1;
|
|
APosAfter.X:= Length(StrPrev);
|
|
APosAfter.Y:= AY-1;
|
|
end;
|
|
end;
|
|
|
|
procedure TATStrings.TextDeleteRight(AX, AY: integer; ALen: integer; out AShift,
|
|
APosAfter: TPoint; ACanDelEol: boolean = true);
|
|
var
|
|
Str: atString;
|
|
DelEol: boolean;
|
|
begin
|
|
AShift.X:= 0;
|
|
AShift.Y:= 0;
|
|
APosAfter.X:= AX;
|
|
APosAfter.Y:= AY;
|
|
if not IsIndexValid(AY) then Exit;
|
|
Str:= Lines[AY];
|
|
|
|
//special case: last fake line
|
|
if (AY=Count-1) and (Str='') and (LinesEnds[AY]=cEndNone) then
|
|
Exit;
|
|
|
|
DelEol:= false;
|
|
if AX<Length(Str) then
|
|
begin
|
|
System.Delete(Str, AX+1, ALen);
|
|
Lines[AY]:= Str;
|
|
AShift.X:= -ALen;
|
|
end
|
|
else
|
|
DelEol:= ACanDelEol;
|
|
|
|
if DelEol then
|
|
if Str='' then //handle for simpler line-states
|
|
begin
|
|
AShift.Y:= -1;
|
|
if (AY>0) and (AY=Count-1) then
|
|
begin
|
|
APosAfter.X:= 0;
|
|
APosAfter.Y:= AY-1;
|
|
end;
|
|
LineDelete(AY);
|
|
end
|
|
else
|
|
begin
|
|
//add spaces if we are after eol
|
|
if AX>=Length(Str) then
|
|
Str:= Str+StringOfChar(' ', AX-Length(Str));
|
|
|
|
//not last: del next line
|
|
if AY+1<Count then
|
|
begin
|
|
Lines[AY]:= Str+Lines[AY+1];
|
|
LineDelete(AY+1, false{not force});
|
|
//maybe also eol
|
|
if AY=Count-1 then
|
|
LinesEnds[AY]:= cEndNone;
|
|
end
|
|
else
|
|
//last line: del eol
|
|
LinesEnds[AY]:= cEndNone;
|
|
|
|
AShift.Y:= -1;
|
|
end;
|
|
end;
|
|
|
|
procedure TATStrings.TextDeleteRange(AFromX, AFromY, AToX, AToY: integer;
|
|
out AShift, APosAfter: TPoint);
|
|
var
|
|
Str: atString;
|
|
bDelEmpty: boolean;
|
|
bNoEol: boolean;
|
|
i: integer;
|
|
begin
|
|
AShift.X:= 0;
|
|
AShift.Y:= 0;
|
|
APosAfter.X:= AFromX;
|
|
APosAfter.Y:= AFromY;
|
|
if not IsIndexValid(AFromY) then Exit;
|
|
if not IsIndexValid(AToY) then Exit;
|
|
if (AFromX=AToX) and (AFromY=AToY) then Exit;
|
|
if (AFromY>AToY) then Exit;
|
|
|
|
if AFromY=AToY then
|
|
begin
|
|
//delete range in one line
|
|
Str:= Lines[AFromY];
|
|
Delete(Str, AFromX+1, AToX-AFromX);
|
|
Lines[AFromY]:= Str;
|
|
|
|
AShift.X:= -(AToX-AFromX);
|
|
end
|
|
else
|
|
begin
|
|
bDelEmpty:= false;
|
|
//correct AToX/AToY to not del extra empty line
|
|
if (AToX=0) and (Lines[AToY]='') then //for empty last line
|
|
begin
|
|
AToY:= Max(0, AToY-1);
|
|
AToX:= Length(Lines[AToY]);
|
|
bDelEmpty:= true;
|
|
end;
|
|
|
|
//remember no final Eol
|
|
bNoEol:= (AToY=Count-1) and (LinesEnds[AToY]=cEndNone);
|
|
|
|
//place ramaining parts of 1st+last lines
|
|
Str:= Copy(Lines[AFromY], 1, AFromX) + Copy(Lines[AToY], AToX+1, MaxInt);
|
|
Lines[AFromY]:= Str;
|
|
|
|
//del middle lines
|
|
for i:= AToY downto AFromY+1 do
|
|
LineDelete(i);
|
|
|
|
//del empty line?
|
|
if bDelEmpty then
|
|
if Str='' then
|
|
LineDelete(AFromY);
|
|
|
|
if bNoEol then
|
|
begin
|
|
ActionDeleteFakeLine;
|
|
if Count>0 then
|
|
LinesEnds[Count-1]:= cEndNone;
|
|
end;
|
|
|
|
AShift.Y:= -(AToY-AFromY);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TATStrings.TextInsertColumnBlock(AX, AY: integer; ABlock: TATStrings; AOverwrite: boolean);
|
|
var
|
|
Shift, PosAfter: TPoint;
|
|
i: integer;
|
|
begin
|
|
for i:= 0 to ABlock.Count-1 do
|
|
begin
|
|
TextInsert(AX, AY+i, ABlock.Lines[i], AOverwrite, Shift, PosAfter);
|
|
LinesEnds[AY+i]:= Endings; //force eol
|
|
if not IsIndexValid(AY+i+1) then
|
|
LineAddRaw('', cEndNone);
|
|
end;
|
|
end;
|
|
|
|
procedure TATStrings.TextInsertEol(AX, AY: integer; AKeepCaret: boolean; const AStrIndent: atString; out AShift, APosAfter: TPoint);
|
|
var
|
|
Str, StrMove: atString;
|
|
NewEnd: TATLineEnds;
|
|
begin
|
|
AShift.X:= 0;
|
|
AShift.Y:= 0;
|
|
APosAfter.X:= AX;
|
|
APosAfter.Y:= AY;
|
|
if not IsIndexValid(AY) then Exit;
|
|
|
|
Str:= Lines[AY];
|
|
StrMove:= '';
|
|
|
|
//special case AX=0: just insert empty line
|
|
//(less changes in undo)
|
|
if AX=0 then
|
|
begin
|
|
LineInsertRaw(AY, '', Endings);
|
|
end
|
|
else
|
|
begin
|
|
BeginUndoGroup;
|
|
|
|
if (AX<Length(Str)) then
|
|
begin
|
|
StrMove:= Copy(Str, AX+1, MaxInt);
|
|
Delete(Str, AX+1, MaxInt);
|
|
Lines[AY]:= Str;
|
|
end;
|
|
|
|
//handle situation when we at non-eol line, this must give
|
|
//inserted line also w/o eol
|
|
NewEnd:= LinesEnds[AY];
|
|
LinesEnds[AY]:= Endings; //force eol to cur line
|
|
LineInsertRaw(AY+1, AStrIndent+StrMove, NewEnd);
|
|
|
|
EndUndoGroup;
|
|
end;
|
|
|
|
if not AKeepCaret then
|
|
begin
|
|
APosAfter.X:= Length(AStrIndent);
|
|
APosAfter.Y:= AY+1;
|
|
AShift.Y:= 1;
|
|
end;
|
|
end;
|
|
|
|
procedure TATStrings.TextDeleteLine(AX, AY: integer; out AShift, APosAfter: TPoint);
|
|
begin
|
|
AShift.X:= 0;
|
|
AShift.Y:= 0;
|
|
APosAfter.X:= AX;
|
|
APosAfter.Y:= AY;
|
|
if not IsIndexValid(AY) then Exit;
|
|
|
|
AShift.Y:= -1;
|
|
APosAfter.X:= 0;
|
|
|
|
LineDelete(AY);
|
|
if AY>=Count then
|
|
LineAddEx('', cEndNone);
|
|
end;
|
|
|
|
procedure TATStrings.TextDuplicateLine(AX, AY: integer; out AShift, APosAfter: TPoint);
|
|
begin
|
|
AShift.X:= 0;
|
|
AShift.Y:= 0;
|
|
APosAfter.X:= AX;
|
|
APosAfter.Y:= AY;
|
|
if not IsIndexValid(AY) then Exit;
|
|
|
|
LineInsert(AY+1, Lines[AY]);
|
|
|
|
if LinesEnds[AY]<>Endings then
|
|
LinesEnds[AY]:= Endings;
|
|
LinesEnds[AY+1]:= Endings;
|
|
|
|
AShift.Y:= 1;
|
|
end;
|
|
|
|
|