mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-03 17:43:51 +02:00
SynEdit: Refactor the Undo/Redo system. Should fix some issues with column selection too; and enables group-undo for all kind of edit-actions; also fixes bug #13298
git-svn-id: trunk@19001 -
This commit is contained in:
parent
b8e7143c97
commit
a26d1805de
@ -84,7 +84,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
function TSynBeautifier.UnIndentLine(const Editor: TSynEditBase;
|
function TSynBeautifier.UnIndentLine(const Editor: TSynEditBase;
|
||||||
// XXXXX viewed
|
|
||||||
const Line: string; const Lines: TSynEditStrings; const PhysCaret: TPoint;
|
const Line: string; const Lines: TSynEditStrings; const PhysCaret: TPoint;
|
||||||
out DelChars, InsChars: String; out CaretNewX: Integer): String;
|
out DelChars, InsChars: String; out CaretNewX: Integer): String;
|
||||||
var
|
var
|
||||||
@ -109,13 +108,13 @@ begin
|
|||||||
LogSpacePos := TSynEdit(Editor).PhysicalToLogicalCol(Line, PhysCaret.y-1, SpaceCount2 + 1);
|
LogSpacePos := TSynEdit(Editor).PhysicalToLogicalCol(Line, PhysCaret.y-1, SpaceCount2 + 1);
|
||||||
LogCaret := TSynEdit(Editor).PhysicalToLogicalPos(PhysCaret);
|
LogCaret := TSynEdit(Editor).PhysicalToLogicalPos(PhysCaret);
|
||||||
CaretNewX := SpaceCount2 + 1;
|
CaretNewX := SpaceCount2 + 1;
|
||||||
|
Lines.EditDelete(LogSpacePos, PhysCaret.Y, LogCaret.X - LogSpacePos);
|
||||||
DelChars := copy(Line, LogSpacePos, LogCaret.X - LogSpacePos);
|
DelChars := copy(Line, LogSpacePos, LogCaret.X - LogSpacePos);
|
||||||
InsChars := ''; // TODO: if tabs were removed, maybe fill-up with spaces
|
InsChars := ''; // TODO: if tabs were removed, maybe fill-up with spaces
|
||||||
Result :=copy(Line, 1, LogSpacePos-1) + copy(Line, LogCaret.X, MaxInt);
|
Result :=copy(Line, 1, LogSpacePos-1) + copy(Line, LogCaret.X, MaxInt);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSynBeautifier.IndentLine(const Editor: TSynEditBase; Line: string;
|
function TSynBeautifier.IndentLine(const Editor: TSynEditBase; Line: string;
|
||||||
// XXXXX viewed
|
|
||||||
const Lines: TSynEditStrings; const PhysCaret: TPoint; out DelChars,
|
const Lines: TSynEditStrings; const PhysCaret: TPoint; out DelChars,
|
||||||
InsChars: String; out CaretNewX: Integer; RemoveCurrentIndent: Boolean): String;
|
InsChars: String; out CaretNewX: Integer; RemoveCurrentIndent: Boolean): String;
|
||||||
var
|
var
|
||||||
@ -151,8 +150,8 @@ begin
|
|||||||
else
|
else
|
||||||
InsChars := '';
|
InsChars := '';
|
||||||
end;
|
end;
|
||||||
Result := InsChars + Line;
|
Result := InsChars;
|
||||||
CaretNewX := TSynEdit(Editor).LogicalToPhysicalCol(Result, PhysCaret.y - 1, SpaceCount2+1);
|
CaretNewX := TSynEdit(Editor).LogicalToPhysicalCol(Result + Line, PhysCaret.y - 1, SpaceCount2+1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSynBeautifier.GetIndentForLine(Editor: TSynEditBase;
|
function TSynBeautifier.GetIndentForLine(Editor: TSynEditBase;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -38,7 +38,7 @@ uses
|
|||||||
{$IFDEF SYN_MBCSSUPPORT}
|
{$IFDEF SYN_MBCSSUPPORT}
|
||||||
Imm,
|
Imm,
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
SynEditTextBase, SynEditTypes, SynEditMiscProcs, SynEditTextBuffer;
|
SynEditTextBase, SynEditTypes, SynEditMiscProcs;//, SynEditTextBuffer;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -78,7 +78,6 @@ type
|
|||||||
FLinesDeletedMethod: TLinesCountChanged;
|
FLinesDeletedMethod: TLinesCountChanged;
|
||||||
FLinesInsertedMethod: TLinesCountChanged;
|
FLinesInsertedMethod: TLinesCountChanged;
|
||||||
FEnabled: Boolean;
|
FEnabled: Boolean;
|
||||||
FSpacesToTabs: Boolean;
|
|
||||||
FTabWidth: Integer;
|
FTabWidth: Integer;
|
||||||
FActiveSelectionMode: TSynSelectionMode;
|
FActiveSelectionMode: TSynSelectionMode;
|
||||||
FSelectionMode: TSynSelectionMode;
|
FSelectionMode: TSynSelectionMode;
|
||||||
@ -103,13 +102,11 @@ type
|
|||||||
constructor Create(ALines: TSynEditStrings);
|
constructor Create(ALines: TSynEditStrings);
|
||||||
//destructor Destroy; override;
|
//destructor Destroy; override;
|
||||||
procedure AdjustAfterTrimming; // TODO: Move into TrimView
|
procedure AdjustAfterTrimming; // TODO: Move into TrimView
|
||||||
procedure SetSelTextPrimitive(PasteMode: TSynSelectionMode; Value: PChar;
|
procedure SetSelTextPrimitive(PasteMode: TSynSelectionMode; Value: PChar);
|
||||||
AddToUndoList: Boolean = false; ChangeReason: TSynChangeReason = crInsert);
|
|
||||||
function SelAvail: Boolean;
|
function SelAvail: Boolean;
|
||||||
function SelCanContinue(ACaret: TSynEditCaret): Boolean;
|
function SelCanContinue(ACaret: TSynEditCaret): Boolean;
|
||||||
function IsBackwardSel: Boolean; // SelStart < SelEnd ?
|
function IsBackwardSel: Boolean; // SelStart < SelEnd ?
|
||||||
property Enabled: Boolean read FEnabled write SetEnabled;
|
property Enabled: Boolean read FEnabled write SetEnabled;
|
||||||
property SpacesToTabs: Boolean read FSpacesToTabs write FSpacesToTabs;
|
|
||||||
property ActiveSelectionMode: TSynSelectionMode
|
property ActiveSelectionMode: TSynSelectionMode
|
||||||
read FActiveSelectionMode write SetActiveSelectionMode;
|
read FActiveSelectionMode write SetActiveSelectionMode;
|
||||||
property SelectionMode: TSynSelectionMode
|
property SelectionMode: TSynSelectionMode
|
||||||
@ -142,7 +139,7 @@ type
|
|||||||
TSynEditCaret = class(TSynEditPointBase)
|
TSynEditCaret = class(TSynEditPointBase)
|
||||||
private
|
private
|
||||||
FAllowPastEOL: Boolean;
|
FAllowPastEOL: Boolean;
|
||||||
FForcePastEOL: Boolean;
|
FForcePastEOL: Integer;
|
||||||
FLinePos: Integer; // 1 based
|
FLinePos: Integer; // 1 based
|
||||||
FCharPos: Integer; // 1 based
|
FCharPos: Integer; // 1 based
|
||||||
FOldLinePos: Integer; // 1 based
|
FOldLinePos: Integer; // 1 based
|
||||||
@ -166,6 +163,8 @@ type
|
|||||||
Procedure DoUnlock; override;
|
Procedure DoUnlock; override;
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
|
procedure IncForcePastEOL;
|
||||||
|
procedure DecForcePastEOL;
|
||||||
property OldLinePos: Integer read FOldLinePos;
|
property OldLinePos: Integer read FOldLinePos;
|
||||||
property OldCharPos: Integer read FOldCharPos;
|
property OldCharPos: Integer read FOldCharPos;
|
||||||
property LinePos: Integer read fLinePos write setLinePos;
|
property LinePos: Integer read fLinePos write setLinePos;
|
||||||
@ -176,7 +175,6 @@ type
|
|||||||
property LineText: string read GetLineText write SetLineText;
|
property LineText: string read GetLineText write SetLineText;
|
||||||
property AdjustToNextChar: Boolean read FAdjustToNextChar write FAdjustToNextChar;
|
property AdjustToNextChar: Boolean read FAdjustToNextChar write FAdjustToNextChar;
|
||||||
property AllowPastEOL: Boolean read FAllowPastEOL write SetAllowPastEOL;
|
property AllowPastEOL: Boolean read FAllowPastEOL write SetAllowPastEOL;
|
||||||
property ForcePastEOL: Boolean read FForcePastEOL write FForcePastEOL;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
@ -240,7 +238,17 @@ begin
|
|||||||
fLinePos:= 1;
|
fLinePos:= 1;
|
||||||
fCharPos:= 1;
|
fCharPos:= 1;
|
||||||
FAllowPastEOL := True;
|
FAllowPastEOL := True;
|
||||||
FForcePastEOL := False;
|
FForcePastEOL := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditCaret.IncForcePastEOL;
|
||||||
|
begin
|
||||||
|
inc(FForcePastEOL);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditCaret.DecForcePastEOL;
|
||||||
|
begin
|
||||||
|
dec(FForcePastEOL);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSynEditCaret.setLinePos(const AValue : Integer);
|
procedure TSynEditCaret.setLinePos(const AValue : Integer);
|
||||||
@ -316,10 +324,10 @@ begin
|
|||||||
if NewLine < 1 then begin
|
if NewLine < 1 then begin
|
||||||
// this is just to make sure if Lines stringlist should be empty
|
// this is just to make sure if Lines stringlist should be empty
|
||||||
NewLine := 1;
|
NewLine := 1;
|
||||||
if not (FAllowPastEOL or FForcePastEOL) then
|
if (not FAllowPastEOL) and (FForcePastEOL = 0) then
|
||||||
nMaxX := 1;
|
nMaxX := 1;
|
||||||
end else begin
|
end else begin
|
||||||
if not (FAllowPastEOL or FForcePastEOL) then begin
|
if (not FAllowPastEOL) and (FForcePastEOL = 0) then begin
|
||||||
Line := Lines[NewLine - 1];
|
Line := Lines[NewLine - 1];
|
||||||
nMaxX := Lines.LogicalToPhysicalCol(Line, NewLine - 1, length(Line)+1);
|
nMaxX := Lines.LogicalToPhysicalCol(Line, NewLine - 1, length(Line)+1);
|
||||||
end;
|
end;
|
||||||
@ -581,23 +589,18 @@ end;
|
|||||||
|
|
||||||
procedure TSynEditSelection.SetSelText(const Value : string);
|
procedure TSynEditSelection.SetSelText(const Value : string);
|
||||||
begin
|
begin
|
||||||
SetSelTextPrimitive(smNormal, PChar(Value), true);
|
SetSelTextPrimitive(smNormal, PChar(Value));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSynEditSelection.SetSelTextPrimitive(PasteMode : TSynSelectionMode;
|
procedure TSynEditSelection.SetSelTextPrimitive(PasteMode : TSynSelectionMode;
|
||||||
Value : PChar; AddToUndoList: Boolean = false;
|
Value : PChar);
|
||||||
ChangeReason: TSynChangeReason = crInsert);
|
|
||||||
var
|
var
|
||||||
BB, BE: TPoint;
|
BB, BE: TPoint;
|
||||||
TempString: string;
|
|
||||||
|
|
||||||
procedure DeleteSelection;
|
procedure DeleteSelection;
|
||||||
var
|
var
|
||||||
x, MarkOffset: Integer;
|
y, l, r, xb, xe, MarkOffset: Integer;
|
||||||
UpdateMarks: boolean;
|
UpdateMarks: boolean;
|
||||||
{$IFDEF SYN_MBCSSUPPORT}
|
|
||||||
l, r: Integer;
|
|
||||||
{$ENDIF}
|
|
||||||
begin
|
begin
|
||||||
UpdateMarks := FALSE;
|
UpdateMarks := FALSE;
|
||||||
MarkOffset := 0;
|
MarkOffset := 0;
|
||||||
@ -605,62 +608,59 @@ var
|
|||||||
smNormal:
|
smNormal:
|
||||||
begin
|
begin
|
||||||
if FLines.Count > 0 then begin
|
if FLines.Count > 0 then begin
|
||||||
// Create a string that contains everything on the first line up
|
if BE.Y > BB.Y + 1 then begin
|
||||||
// to the selection mark, and everything on the last line after
|
FLines.EditLinesDelete(BB.Y + 1, BE.Y - BB.Y - 1);
|
||||||
// the selection mark.
|
BE.Y := BB.Y + 1;
|
||||||
TempString := Copy(FLines[BB.Y - 1], 1, BB.X - 1) +
|
end;
|
||||||
Copy(FLines[BE.Y - 1], BE.X, MaxInt);
|
if BE.Y > BB.Y then begin
|
||||||
// Delete all FLines in the selection range.
|
BE.X := BE.X + length(FLines[BB.Y - 1]);
|
||||||
FLines.DeleteLines(BB.Y-1, BE.Y - BB.Y);
|
FLines.EditLineJoin(BB.Y);
|
||||||
FLines[BB.Y - 1] := TempString;
|
BE.Y := BB.Y;
|
||||||
|
end;
|
||||||
|
FLines.EditDelete(BB.X, BB.Y, BE.X - BB.X);
|
||||||
end;
|
end;
|
||||||
UpdateMarks := TRUE;
|
UpdateMarks := TRUE;
|
||||||
FCaret.LineBytePos := BB;
|
FCaret.LineBytePos := BB;
|
||||||
end;
|
end;
|
||||||
smColumn:
|
smColumn:
|
||||||
begin
|
begin
|
||||||
// swap X if needed
|
FCaret.LineBytePos := BB;
|
||||||
if BB.X > BE.X then
|
l := FCaret.CharPos;
|
||||||
|
FCaret.LineBytePos := BE;
|
||||||
|
r := FCaret.CharPos;
|
||||||
|
// swap l, r if needed
|
||||||
|
if l > r then
|
||||||
{$IFDEF SYN_COMPILER_3_UP}
|
{$IFDEF SYN_COMPILER_3_UP}
|
||||||
SwapInt(BB.X, BE.X);
|
SwapInt(l, r);
|
||||||
{$ELSE}
|
{$ELSE}
|
||||||
begin
|
begin
|
||||||
x := BB.X;
|
y := l;
|
||||||
BB.X := BE.X;
|
l := r;
|
||||||
BE.X := x;
|
r := y;
|
||||||
end;
|
end;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
for x := BB.Y - 1 to BE.Y - 1 do begin
|
for y := BB.Y to BE.Y do begin
|
||||||
TempString := FLines[x];
|
FCaret.LineCharPos := Point(l, y);
|
||||||
{$IFNDEF SYN_MBCSSUPPORT}
|
xb := FCaret.BytePos;
|
||||||
Delete(TempString, BB.X, BE.X - BB.X);
|
FCaret.LineCharPos := Point(r, y);
|
||||||
{$ELSE}
|
xe := Min(FCaret.BytePos, 1 + length(FCaret.LineText));
|
||||||
l := BB.X;
|
if xe > xb then
|
||||||
r := BE.X;
|
FLines.EditDelete(xb, y, xe - xb);
|
||||||
MBCSGetSelRangeInLineWhenColumnActiveSelectionMode(TempString, l, r);
|
|
||||||
{$IFDEF USE_UTF8BIDI_LCL}
|
|
||||||
VDelete(TempString, l, r - 1);
|
|
||||||
{$ELSE USE_UTF8BIDI_LCL}
|
|
||||||
Delete(TempString, l, r - l);
|
|
||||||
{$ENDIF USE_UTF8BIDI_LCL}
|
|
||||||
{$ENDIF}
|
|
||||||
FLines[x] := TempString;
|
|
||||||
end;
|
end;
|
||||||
// FLines never get deleted completely, so keep caret at end.
|
// FLines never get deleted completely, so keep caret at end.
|
||||||
FCaret.LineBytePos := Point(BB.X, FEndLinePos);
|
FCaret.LineCharPos := Point(l, FEndLinePos);
|
||||||
// Column deletion never removes a line entirely, so no mark
|
// Column deletion never removes a line entirely, so no mark
|
||||||
// updating is needed here.
|
// updating is needed here.
|
||||||
end;
|
end;
|
||||||
smLine:
|
smLine:
|
||||||
begin
|
begin
|
||||||
if BE.Y = FLines.Count then begin
|
if BE.Y = FLines.Count then begin
|
||||||
FLines[BE.Y - 1] := '';
|
// Keep the (CrLf of) last line, since no Line exists to replace it
|
||||||
for x := BE.Y - 2 downto BB.Y - 1 do
|
FLines.EditDelete(1, BE.Y, length(FLines[BE.Y - 1]));
|
||||||
FLines.Delete(x);
|
dec(BE.Y)
|
||||||
end else
|
end;
|
||||||
for x := BE.Y - 1 downto BB.Y - 1 do
|
if BE.Y >= BB.Y then
|
||||||
FLines.Delete(x);
|
FLines.EditLinesDelete(BB.Y, BE.Y - BB.Y + 1);
|
||||||
// smLine deletion always resets to first column.
|
|
||||||
FCaret.LineCharPos := Point(1, BB.Y);
|
FCaret.LineCharPos := Point(1, BB.Y);
|
||||||
UpdateMarks := TRUE;
|
UpdateMarks := TRUE;
|
||||||
MarkOffset := 1;
|
MarkOffset := 1;
|
||||||
@ -688,62 +688,43 @@ var
|
|||||||
|
|
||||||
function InsertNormal: Integer;
|
function InsertNormal: Integer;
|
||||||
var
|
var
|
||||||
sLeftSide: string;
|
|
||||||
sRightSide: string;
|
|
||||||
Str: string;
|
Str: string;
|
||||||
Start: PChar;
|
Start: PChar;
|
||||||
P: PChar;
|
P: PChar;
|
||||||
LogCaretXY: TPoint;
|
LogCaretXY: TPoint;
|
||||||
PhysicalLineEndPos: LongInt;
|
|
||||||
begin
|
begin
|
||||||
Result := 0;
|
Result := 0;
|
||||||
LogCaretXY := FCaret.LineBytePos;
|
LogCaretXY := FCaret.LineBytePos;
|
||||||
sLeftSide := Copy(FCaret.LineText, 1, LogCaretXY.X - 1);
|
|
||||||
if LogCaretXY.X - 1 > Length(sLeftSide) then begin
|
|
||||||
PhysicalLineEndPos:= FLines.LogicalToPhysicalPos
|
|
||||||
(Point(Length(sLeftSide)+1, FCaret.LinePos)).X-1;
|
|
||||||
sLeftSide := sLeftSide
|
|
||||||
+ CreateTabsAndSpaces(FCaret.CharPos,
|
|
||||||
FCaret.CharPos-1-PhysicalLineEndPos,
|
|
||||||
FTabWidth, FSpacesToTabs);
|
|
||||||
end;
|
|
||||||
sRightSide := Copy(FCaret.LineText, LogCaretXY.X,
|
|
||||||
Length(FCaret.LineText) - (LogCaretXY.X - 1));
|
|
||||||
// step1: insert the first line of Value into current line
|
|
||||||
Start := PChar(Value);
|
Start := PChar(Value);
|
||||||
P := GetEOL(Start);
|
P := GetEOL(Start);
|
||||||
if P^ <> #0 then begin
|
if P^ = #0 then begin
|
||||||
SetString(Str, Value, P - Start);
|
FLines.EditInsert(LogCaretXY.X, LogCaretXY.Y, Value);
|
||||||
FLines.InsertLines(FCaret.LinePos - 1, CountLines(P));
|
FCaret.BytePos := FCaret.BytePos + Length(Value);
|
||||||
FLines[FCaret.LinePos - 1] := sLeftSide + Str;
|
|
||||||
end else begin
|
end else begin
|
||||||
FLines[FCaret.LinePos - 1] := sLeftSide + Value + sRightSide;
|
SetString(Str, Value, P - Start);
|
||||||
FCaret.BytePos := 1 + Length(sLeftSide + Value);
|
FLines.EditInsert(LogCaretXY.X, LogCaretXY.Y, Str);
|
||||||
end;
|
FLines.EditLineBreak(LogCaretXY.X + Length(Str), LogCaretXY.Y);
|
||||||
// step2: insert left lines of Value
|
Result := CountLines(P);
|
||||||
while P^ <> #0 do begin
|
if Result > 1 then
|
||||||
if P^ = #13 then
|
FLines.EditLinesInsert(LogCaretXY.Y + 1, Result - 1);
|
||||||
Inc(P);
|
while P^ <> #0 do begin
|
||||||
if P^ = #10 then
|
if P^ = #13 then
|
||||||
Inc(P);
|
Inc(P);
|
||||||
FCaret.LinePos := FCaret.LinePos + 1;
|
if P^ = #10 then
|
||||||
Start := P;
|
Inc(P);
|
||||||
P := GetEOL(Start);
|
LogCaretXY.Y := LogCaretXY.Y + 1;
|
||||||
if P = Start then begin
|
Start := P;
|
||||||
if p^ <> #0 then
|
P := GetEOL(Start);
|
||||||
FLines[FCaret.LinePos - 1] := ''
|
if P <> Start then begin
|
||||||
|
SetString(Str, Start, P - Start);
|
||||||
|
FLines.EditInsert(1, LogCaretXY.Y, Str);
|
||||||
|
end
|
||||||
else
|
else
|
||||||
FLines[FCaret.LinePos - 1] := sRightSide;
|
Str := '';
|
||||||
end else begin
|
|
||||||
SetString(Str, Start, P - Start);
|
|
||||||
if p^ <> #0 then
|
|
||||||
FLines[FCaret.LinePos - 1] := Str
|
|
||||||
else
|
|
||||||
FLines[FCaret.LinePos - 1] := Str + sRightSide
|
|
||||||
end;
|
end;
|
||||||
if p^=#0 then
|
FCaret.LinePos := LogCaretXY.Y;
|
||||||
FCaret.BytePos := 1 + Length(FLines[FCaret.LinePos - 1]) - Length(sRightSide);
|
FCaret.BytePos := 1 + Length(Str);
|
||||||
Inc(Result);
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -752,40 +733,17 @@ var
|
|||||||
Str: string;
|
Str: string;
|
||||||
Start: PChar;
|
Start: PChar;
|
||||||
P: PChar;
|
P: PChar;
|
||||||
Len: Integer;
|
|
||||||
InsertPos: Integer;
|
|
||||||
LogicalInsertPos: Integer;
|
|
||||||
begin
|
begin
|
||||||
// Insert string at current position
|
// Insert string at current position
|
||||||
InsertPos := FCaret.CharPos;
|
Result := 0;
|
||||||
|
FCaret.IncForcePastEOL;
|
||||||
Start := PChar(Value);
|
Start := PChar(Value);
|
||||||
repeat
|
repeat
|
||||||
P := GetEOL(Start);
|
P := GetEOL(Start);
|
||||||
if P <> Start then begin
|
if P <> Start then begin
|
||||||
SetLength(Str, P - Start);
|
SetLength(Str, P - Start);
|
||||||
Move(Start^, Str[1], P - Start);
|
Move(Start^, Str[1], P - Start);
|
||||||
if FCaret.LinePos > FLines.Count then {useless check. FCaret.LinePos cannot exceed FLines.Count}
|
FLines.EditInsert(FCaret.BytePos, FCaret.LinePos, Str);
|
||||||
FLines.Add(StringOfChar(' ', InsertPos - 1) + Str)
|
|
||||||
else begin
|
|
||||||
TempString := FLines[FCaret.LinePos - 1];
|
|
||||||
Len := Length(TempString);
|
|
||||||
LogicalInsertPos := FLines.PhysicalToLogicalCol(TempString,
|
|
||||||
FCaret.LinePos - 1, InsertPos);
|
|
||||||
if Len < LogicalInsertPos
|
|
||||||
then begin
|
|
||||||
TempString :=
|
|
||||||
TempString + StringOfChar(' ', LogicalInsertPos - Len - 1)
|
|
||||||
+ Str
|
|
||||||
end else begin
|
|
||||||
{$IFDEF SYN_MBCSSUPPORT}
|
|
||||||
if mbTrailByte = ByteType(TempString, InsertPos) then
|
|
||||||
Insert(Str, TempString, InsertPos + 1)
|
|
||||||
else
|
|
||||||
{$ENDIF}
|
|
||||||
System.Insert(Str, TempString, LogicalInsertPos);
|
|
||||||
end;
|
|
||||||
FLines[FCaret.LinePos - 1] := TempString;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
if p^ in [#10,#13] then begin
|
if p^ in [#10,#13] then begin
|
||||||
if (p[1] in [#10,#13]) and (p[1]<>p^) then
|
if (p[1] in [#10,#13]) and (p[1]<>p^) then
|
||||||
@ -793,13 +751,14 @@ var
|
|||||||
else
|
else
|
||||||
Inc(P);
|
Inc(P);
|
||||||
if FCaret.LinePos = FLines.Count then
|
if FCaret.LinePos = FLines.Count then
|
||||||
FLines.Add(StringOfChar(' ', InsertPos - 1));
|
FLines.EditLinesInsert(FCaret.LinePos + 1, 1);
|
||||||
|
// No need to inc result => adding at EOF
|
||||||
FCaret.LinePos := FCaret.LinePos + 1;
|
FCaret.LinePos := FCaret.LinePos + 1;
|
||||||
end;
|
end;
|
||||||
Start := P;
|
Start := P;
|
||||||
until P^ = #0;
|
until P^ = #0;
|
||||||
FCaret.BytePos:= FCaret.BytePos + Length(Str);
|
FCaret.BytePos:= FCaret.BytePos + Length(Str);
|
||||||
Result := 0;
|
FCaret.DecForcePastEOL;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function InsertLine: Integer;
|
function InsertLine: Integer;
|
||||||
@ -807,7 +766,6 @@ var
|
|||||||
Start: PChar;
|
Start: PChar;
|
||||||
P: PChar;
|
P: PChar;
|
||||||
Str: string;
|
Str: string;
|
||||||
n: Integer;
|
|
||||||
begin
|
begin
|
||||||
Result := 0;
|
Result := 0;
|
||||||
FCaret.CharPos := 1;
|
FCaret.CharPos := 1;
|
||||||
@ -821,14 +779,10 @@ var
|
|||||||
end else
|
end else
|
||||||
Str := '';
|
Str := '';
|
||||||
if (P^ = #0) then begin // Not a full line?
|
if (P^ = #0) then begin // Not a full line?
|
||||||
n := FLines.Count;
|
FLines.EditInsert(1, FCaret.LinePos, Str);
|
||||||
if (n >= FCaret.LinePos) then
|
FCaret.BytePos := 1 + Length(Str);
|
||||||
FLines[FCaret.LinePos - 1] := Str + FLines[FCaret.LinePos - 1]
|
|
||||||
else
|
|
||||||
FLines.Add(Str);
|
|
||||||
FCaret.CharPos := 1 + Length(Str);
|
|
||||||
end else begin
|
end else begin
|
||||||
FLines.Insert(FCaret.LinePos - 1, Str);
|
FLines.EditLinesInsert(FCaret.LinePos, 1, Str);
|
||||||
FCaret.LinePos := FCaret.LinePos + 1;
|
FCaret.LinePos := FCaret.LinePos + 1;
|
||||||
Inc(Result);
|
Inc(Result);
|
||||||
if P^ = #13 then
|
if P^ = #13 then
|
||||||
@ -876,8 +830,6 @@ var
|
|||||||
FLinesInsertedMethod(StartLine, InsertedLines);
|
FLinesInsertedMethod(StartLine, InsertedLines);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
var
|
|
||||||
StartInsert, EndInsert: TPoint;
|
|
||||||
begin
|
begin
|
||||||
FLines.BeginUpdate;
|
FLines.BeginUpdate;
|
||||||
try
|
try
|
||||||
@ -885,45 +837,11 @@ begin
|
|||||||
BB := FirstLineBytePos;
|
BB := FirstLineBytePos;
|
||||||
BE := LastLineBytePos;
|
BE := LastLineBytePos;
|
||||||
if SelAvail then begin
|
if SelAvail then begin
|
||||||
// todo: better move add-undo past actual delete
|
|
||||||
FLines[BB.Y - 1] := FLines[BB.Y - 1]; // TrimRealSpaces (in case of smNormal or smLine
|
|
||||||
if AddToUndoList then begin
|
|
||||||
if ChangeReason in [crSilentDelete, crSilentDeleteAfterCursor] then begin
|
|
||||||
if IsBackwardSel then
|
|
||||||
fUndoList.AddChange(crSilentDeleteAfterCursor, StartLineBytePos, EndLineBytePos,
|
|
||||||
GetSelText, ActiveSelectionMode)
|
|
||||||
else
|
|
||||||
fUndoList.AddChange(crSilentDelete, StartLineBytePos, EndLineBytePos,
|
|
||||||
GetSelText, ActiveSelectionMode);
|
|
||||||
end else begin
|
|
||||||
if IsBackwardSel then
|
|
||||||
fUndoList.AddChange(crDeleteAfterCursor, StartLineBytePos, EndLineBytePos,
|
|
||||||
GetSelText, ActiveSelectionMode)
|
|
||||||
else
|
|
||||||
fUndoList.AddChange(crDelete, StartLineBytePos, EndLineBytePos,
|
|
||||||
GetSelText, ActiveSelectionMode);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
DeleteSelection;
|
DeleteSelection;
|
||||||
EndLineBytePos := BB; // deletes selection // calls selection changed
|
EndLineBytePos := BB; // deletes selection // calls selection changed
|
||||||
end;
|
end;
|
||||||
if (Value <> nil) and (Value[0] <> #0) then begin
|
if (Value <> nil) and (Value[0] <> #0) then begin
|
||||||
StartInsert := FCaret.LineBytePos;
|
|
||||||
InsertText;
|
InsertText;
|
||||||
if AddToUndoList then begin
|
|
||||||
EndInsert := FCaret.LineBytePos;
|
|
||||||
if ActiveSelectionMode = smLine then begin // The ActiveSelectionMode of the deleted block
|
|
||||||
StartInsert.x := 1;
|
|
||||||
if EndInsert.x = 1 then begin
|
|
||||||
dec(EndInsert.y);
|
|
||||||
EndInsert.x := Length(FLines[EndInsert.y - 1]);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
if ChangeReason in [crSilentDelete, crSilentDeleteAfterCursor, crDelete,
|
|
||||||
crDeleteAfterCursor] then
|
|
||||||
ChangeReason := crInsert;
|
|
||||||
fUndoList.AddChange(ChangeReason, StartInsert, EndInsert, '', PasteMode);
|
|
||||||
end;
|
|
||||||
StartLineBytePos := FCaret.LineBytePos; // reset selection
|
StartLineBytePos := FCaret.LineBytePos; // reset selection
|
||||||
end;
|
end;
|
||||||
finally
|
finally
|
||||||
|
@ -26,7 +26,7 @@ unit SynEditTextBase;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, LCLProc, SynEditTypes;
|
Classes, SysUtils, LCLProc, SynEditTypes, SynEditMiscProcs, SynEditKeyCmds;
|
||||||
|
|
||||||
type
|
type
|
||||||
TSynEditStrings = class;
|
TSynEditStrings = class;
|
||||||
@ -37,6 +37,11 @@ type
|
|||||||
|
|
||||||
TPhysicalCharWidths = Array of Shortint;
|
TPhysicalCharWidths = Array of Shortint;
|
||||||
|
|
||||||
|
TSynEditUndoList = class;
|
||||||
|
TSynEditUndoItem = class;
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
{ TSynEditStrings }
|
{ TSynEditStrings }
|
||||||
|
|
||||||
TSynEditStrings = class(TStrings)
|
TSynEditStrings = class(TStrings)
|
||||||
@ -52,6 +57,12 @@ type
|
|||||||
function GetExpandedString(Index: integer): string; virtual; abstract;
|
function GetExpandedString(Index: integer): string; virtual; abstract;
|
||||||
function GetLengthOfLongestLine: integer; virtual; abstract;
|
function GetLengthOfLongestLine: integer; virtual; abstract;
|
||||||
procedure SetTextStr(const Value: string); override;
|
procedure SetTextStr(const Value: string); override;
|
||||||
|
|
||||||
|
function GetRedoList: TSynEditUndoList; virtual; abstract;
|
||||||
|
function GetUndoList: TSynEditUndoList; virtual; abstract;
|
||||||
|
procedure SetIsUndoing(const AValue: Boolean); virtual; abstract;
|
||||||
|
procedure SendNotification(AReason: TSynEditNotifyReason;
|
||||||
|
ASender: TSynEditStrings; aIndex, aCount: Integer); virtual; abstract;
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
procedure DeleteLines(Index, NumLines: integer); virtual; abstract;
|
procedure DeleteLines(Index, NumLines: integer); virtual; abstract;
|
||||||
@ -77,6 +88,18 @@ type
|
|||||||
function PhysicalToLogicalPos(const p: TPoint): TPoint;
|
function PhysicalToLogicalPos(const p: TPoint): TPoint;
|
||||||
function PhysicalToLogicalCol(const Line: string;
|
function PhysicalToLogicalCol(const Line: string;
|
||||||
Index, PhysicalPos: integer): integer; virtual;
|
Index, PhysicalPos: integer): integer; virtual;
|
||||||
|
public
|
||||||
|
procedure EditInsert(LogX, LogY: Integer; AText: String); virtual; abstract;
|
||||||
|
function EditDelete(LogX, LogY, ByteLen: Integer): String; virtual; abstract;
|
||||||
|
procedure EditLineBreak(LogX, LogY: Integer); virtual; abstract;
|
||||||
|
procedure EditLineJoin(LogY: Integer; FillText: String = ''); virtual; abstract;
|
||||||
|
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); virtual; abstract;
|
||||||
|
procedure EditLinesDelete(LogY, ACount: Integer); virtual; abstract;
|
||||||
|
procedure EditUndo(Item: TSynEditUndoItem); virtual; abstract;
|
||||||
|
procedure EditRedo(Item: TSynEditUndoItem); virtual; abstract;
|
||||||
|
property UndoList: TSynEditUndoList read GetUndoList;
|
||||||
|
property RedoList: TSynEditUndoList read GetRedoList;
|
||||||
|
property IsUndoing: Boolean write SetIsUndoing;
|
||||||
public
|
public
|
||||||
property ExpandedStrings[Index: integer]: string read GetExpandedString;
|
property ExpandedStrings[Index: integer]: string read GetExpandedString;
|
||||||
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
|
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
|
||||||
@ -101,6 +124,12 @@ type
|
|||||||
|
|
||||||
function GetExpandedString(Index: integer): string; override;
|
function GetExpandedString(Index: integer): string; override;
|
||||||
function GetLengthOfLongestLine: integer; override;
|
function GetLengthOfLongestLine: integer; override;
|
||||||
|
|
||||||
|
procedure SendNotification(AReason: TSynEditNotifyReason;
|
||||||
|
ASender: TSynEditStrings; aIndex, aCount: Integer); override;
|
||||||
|
function GetRedoList: TSynEditUndoList; override;
|
||||||
|
function GetUndoList: TSynEditUndoList; override;
|
||||||
|
procedure SetIsUndoing(const AValue: Boolean); override;
|
||||||
protected
|
protected
|
||||||
function GetCount: integer; override;
|
function GetCount: integer; override;
|
||||||
function GetCapacity: integer;
|
function GetCapacity: integer;
|
||||||
@ -135,10 +164,114 @@ type
|
|||||||
AHandler: TStringListLineCountEvent); override;
|
AHandler: TStringListLineCountEvent); override;
|
||||||
|
|
||||||
function GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths; override;
|
function GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths; override;
|
||||||
|
public
|
||||||
|
// LogX, LogY are 1-based
|
||||||
|
procedure EditInsert(LogX, LogY: Integer; AText: String); override;
|
||||||
|
function EditDelete(LogX, LogY, ByteLen: Integer): String; override;
|
||||||
|
procedure EditLineBreak(LogX, LogY: Integer); override;
|
||||||
|
procedure EditLineJoin(LogY: Integer; FillText: String = ''); override;
|
||||||
|
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); override;
|
||||||
|
procedure EditLinesDelete(LogY, ACount: Integer); override;
|
||||||
|
procedure EditUndo(Item: TSynEditUndoItem); override;
|
||||||
|
procedure EditRedo(Item: TSynEditUndoItem); override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoItem }
|
||||||
|
|
||||||
|
TSynEditUndoItem = class(TObject)
|
||||||
|
protected
|
||||||
|
// IsEqual is only needed/implemented for Carets
|
||||||
|
function IsEqualContent(AnItem: TSynEditUndoItem): Boolean; virtual;
|
||||||
|
function IsEqual(AnItem: TSynEditUndoItem): Boolean;
|
||||||
|
public
|
||||||
|
function IsCaretInfo: Boolean; virtual;
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; virtual; abstract;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoGroup }
|
||||||
|
|
||||||
|
TSynEditUndoGroup = class(TObject)
|
||||||
|
private
|
||||||
|
FItems: Array of TSynEditUndoItem;
|
||||||
|
FCount, FCapacity: Integer;
|
||||||
|
FReason: TSynEditorCommand;
|
||||||
|
function GetItem(Index: Integer): TSynEditUndoItem;
|
||||||
|
procedure Grow;
|
||||||
|
protected
|
||||||
|
Function HasUndoInfo: Boolean;
|
||||||
|
procedure Append(AnUndoGroup: TSynEditUndoItem);
|
||||||
|
procedure TranferTo(AnUndoGroup: TSynEditUndoGroup);
|
||||||
|
function CanMergeWith(AnUndoGroup: TSynEditUndoGroup): Boolean;
|
||||||
|
procedure MergeWith(AnUndoGroup: TSynEditUndoGroup);
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
Destructor Destroy; override;
|
||||||
|
|
||||||
|
procedure Assign(AnUndoGroup: TSynEditUndoGroup);
|
||||||
|
procedure Add(AnItem: TSynEditUndoItem);
|
||||||
|
procedure Clear;
|
||||||
|
procedure Insert(AIndex: Integer; AnItem: TSynEditUndoItem);
|
||||||
|
function Pop: TSynEditUndoItem;
|
||||||
|
property Count: Integer read FCount;
|
||||||
|
property Items [Index: Integer]: TSynEditUndoItem read GetItem;
|
||||||
|
property Reason: TSynEditorCommand read FReason write FReason;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
TSynGetCaretUndoProc = function: TSynEditUndoItem of object;
|
||||||
|
|
||||||
|
{ TSynEditUndoList }
|
||||||
|
|
||||||
|
TSynEditUndoList = class(TObject)
|
||||||
|
private
|
||||||
|
FGroupUndo: Boolean;
|
||||||
|
FIsInsideRedo: Boolean;
|
||||||
|
FUndoGroup: TSynEditUndoGroup;
|
||||||
|
FInGroupCount: integer;
|
||||||
|
fFullUndoImposible: boolean;
|
||||||
|
fItems: TList;
|
||||||
|
fLockCount: integer;
|
||||||
|
fMaxUndoActions: integer;
|
||||||
|
fOnAdded: TNotifyEvent;
|
||||||
|
FOnNeedCaretUndo: TSynGetCaretUndoProc;
|
||||||
|
fUnModifiedItem: integer;
|
||||||
|
procedure EnsureMaxEntries;
|
||||||
|
function GetCanUndo: boolean;
|
||||||
|
function GetCurrentReason: TSynEditorCommand;
|
||||||
|
function GetItemCount: integer;
|
||||||
|
procedure SetCurrentReason(const AValue: TSynEditorCommand);
|
||||||
|
procedure SetMaxUndoActions(Value: integer);
|
||||||
|
function RealCount: Integer;
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure AddChange(AChange: TSynEditUndoItem);
|
||||||
|
procedure AppendToLastChange(AChange: TSynEditUndoItem);
|
||||||
|
procedure BeginBlock;
|
||||||
|
procedure EndBlock;
|
||||||
|
procedure Clear;
|
||||||
|
procedure Lock;
|
||||||
|
function PopItem: TSynEditUndoGroup;
|
||||||
|
procedure Unlock;
|
||||||
|
function IsLocked: Boolean;
|
||||||
|
procedure MarkTopAsUnmodified;
|
||||||
|
function IsTopMarkedAsUnmodified: boolean;
|
||||||
|
function UnModifiedMarkerExists: boolean;
|
||||||
|
public
|
||||||
|
property CanUndo: boolean read GetCanUndo;
|
||||||
|
property FullUndoImpossible: boolean read fFullUndoImposible;
|
||||||
|
property ItemCount: integer read GetItemCount;
|
||||||
|
property MaxUndoActions: integer read fMaxUndoActions
|
||||||
|
write SetMaxUndoActions;
|
||||||
|
property IsInsideRedo: Boolean read FIsInsideRedo write FIsInsideRedo;
|
||||||
|
property OnAddedUndo: TNotifyEvent read fOnAdded write fOnAdded;
|
||||||
|
property OnNeedCaretUndo : TSynGetCaretUndoProc
|
||||||
|
read FOnNeedCaretUndo write FOnNeedCaretUndo;
|
||||||
|
property GroupUndo: Boolean read FGroupUndo write FGroupUndo;
|
||||||
|
property CurrentGroup: TSynEditUndoGroup read FUndoGroup;
|
||||||
|
property CurrentReason: TSynEditorCommand read GetCurrentReason
|
||||||
|
write SetCurrentReason;
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
@ -305,6 +438,11 @@ begin
|
|||||||
fSynStrings.InsertStrings(Index, NewStrings);
|
fSynStrings.InsertStrings(Index, NewStrings);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.SetIsUndoing(const AValue: Boolean);
|
||||||
|
begin
|
||||||
|
fSynStrings.IsUndoing := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
function TSynEditStringsLinked.GetIsUtf8: Boolean;
|
function TSynEditStringsLinked.GetIsUtf8: Boolean;
|
||||||
begin
|
begin
|
||||||
Result := FSynStrings.IsUtf8;
|
Result := FSynStrings.IsUtf8;
|
||||||
@ -347,6 +485,16 @@ begin
|
|||||||
Result:= fSynStrings.GetLengthOfLongestLine;
|
Result:= fSynStrings.GetLengthOfLongestLine;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TSynEditStringsLinked.GetRedoList: TSynEditUndoList;
|
||||||
|
begin
|
||||||
|
Result := fSynStrings.GetRedoList;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditStringsLinked.GetUndoList: TSynEditUndoList;
|
||||||
|
begin
|
||||||
|
Result := fSynStrings.GetUndoList;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TSynEditStringsLinked.RegisterAttribute(const Index: TClass; const Size: Word);
|
procedure TSynEditStringsLinked.RegisterAttribute(const Index: TClass; const Size: Word);
|
||||||
begin
|
begin
|
||||||
fSynStrings.RegisterAttribute(Index, Size);
|
fSynStrings.RegisterAttribute(Index, Size);
|
||||||
@ -416,5 +564,433 @@ begin
|
|||||||
fSynStrings.EndUpdate;
|
fSynStrings.EndUpdate;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.EditInsert(LogX, LogY: Integer; AText: String);
|
||||||
|
begin
|
||||||
|
fSynStrings.EditInsert(LogX, LogY, AText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditStringsLinked.EditDelete(LogX, LogY, ByteLen: Integer): String;
|
||||||
|
begin
|
||||||
|
Result := fSynStrings.EditDelete(LogX, LogY, ByteLen);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.EditLineBreak(LogX, LogY: Integer);
|
||||||
|
begin
|
||||||
|
fSynStrings.EditLineBreak(LogX, LogY);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.EditLineJoin(LogY: Integer;
|
||||||
|
FillText: String = '');
|
||||||
|
begin
|
||||||
|
fSynStrings.EditLineJoin(LogY, FillText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.EditLinesInsert(LogY, ACount: Integer; AText: String = '');
|
||||||
|
begin
|
||||||
|
fSynStrings.EditLinesInsert(LogY, ACount, AText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.EditLinesDelete(LogY, ACount: Integer);
|
||||||
|
begin
|
||||||
|
fSynStrings.EditLinesDelete(LogY, ACount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.EditUndo(Item: TSynEditUndoItem);
|
||||||
|
begin
|
||||||
|
fSynStrings.EditUndo(Item);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.EditRedo(Item: TSynEditUndoItem);
|
||||||
|
begin
|
||||||
|
fSynStrings.EditRedo(Item);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringsLinked.SendNotification(AReason: TSynEditNotifyReason;
|
||||||
|
ASender: TSynEditStrings; aIndex, aCount: Integer);
|
||||||
|
begin
|
||||||
|
fSynStrings.SendNotification(AReason, ASender, aIndex, aCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoList }
|
||||||
|
|
||||||
|
constructor TSynEditUndoList.Create;
|
||||||
|
begin
|
||||||
|
inherited Create;
|
||||||
|
// Create and keep one undo group => avoids resizing the FItems list
|
||||||
|
FUndoGroup := TSynEditUndoGroup.Create;
|
||||||
|
FIsInsideRedo := False;
|
||||||
|
fItems := TList.Create;
|
||||||
|
fMaxUndoActions := 1024;
|
||||||
|
fUnModifiedItem:=-1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TSynEditUndoList.Destroy;
|
||||||
|
begin
|
||||||
|
Clear;
|
||||||
|
fItems.Free;
|
||||||
|
FreeAndNil(FUndoGroup);
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.AddChange(AChange: TSynEditUndoItem);
|
||||||
|
var
|
||||||
|
ugroup: TSynEditUndoGroup;
|
||||||
|
begin
|
||||||
|
if fLockCount > 0 then begin
|
||||||
|
AChange.Free;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if FInGroupCount > 0 then
|
||||||
|
FUndoGroup.Add(AChange)
|
||||||
|
else begin
|
||||||
|
ugroup := TSynEditUndoGroup.Create;
|
||||||
|
ugroup.Add(AChange);
|
||||||
|
fItems.Add(ugroup);
|
||||||
|
if Assigned(fOnAdded) then
|
||||||
|
fOnAdded(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
EnsureMaxEntries;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.AppendToLastChange(AChange: TSynEditUndoItem);
|
||||||
|
var
|
||||||
|
cur: Boolean;
|
||||||
|
begin
|
||||||
|
cur := FUndoGroup.HasUndoInfo;
|
||||||
|
if (fLockCount <> 0) or ((fItems.Count = 0) and not cur) then begin
|
||||||
|
AChange.Free;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if cur then
|
||||||
|
FUndoGroup.Append(AChange)
|
||||||
|
else
|
||||||
|
TSynEditUndoGroup(fItems[fItems.Count-1]).Append(AChange);
|
||||||
|
|
||||||
|
// Do not callback to synedit, or Redo Info is lost
|
||||||
|
EnsureMaxEntries;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.BeginBlock;
|
||||||
|
begin
|
||||||
|
Inc(FInGroupCount);
|
||||||
|
if (FInGroupCount = 1) then begin
|
||||||
|
FUndoGroup.Clear;
|
||||||
|
if assigned(FOnNeedCaretUndo) then
|
||||||
|
FUndoGroup.add(FOnNeedCaretUndo());
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.Clear;
|
||||||
|
var
|
||||||
|
i: integer;
|
||||||
|
begin
|
||||||
|
for i := 0 to fItems.Count - 1 do
|
||||||
|
TSynEditUndoGroup(fItems[i]).Free;
|
||||||
|
fItems.Clear;
|
||||||
|
fFullUndoImposible := FALSE;
|
||||||
|
fUnModifiedItem:=-1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.EndBlock;
|
||||||
|
var
|
||||||
|
ugroup: TSynEditUndoGroup;
|
||||||
|
begin
|
||||||
|
if FInGroupCount > 0 then begin
|
||||||
|
Dec(FInGroupCount);
|
||||||
|
if (FInGroupCount = 0) and FUndoGroup.HasUndoInfo then
|
||||||
|
begin
|
||||||
|
// Keep position for REDO; Do not replace if present
|
||||||
|
if (not FUndoGroup.Items[FUndoGroup.Count - 1].IsCaretInfo)
|
||||||
|
and assigned(FOnNeedCaretUndo) then
|
||||||
|
FUndoGroup.Add(FOnNeedCaretUndo());
|
||||||
|
if (fItems.Count > 0) and FGroupUndo and
|
||||||
|
FUndoGroup.CanMergeWith(TSynEditUndoGroup(fItems[fItems.Count - 1])) then
|
||||||
|
begin
|
||||||
|
FUndoGroup.MergeWith(TSynEditUndoGroup(fItems[fItems.Count - 1]));
|
||||||
|
FUndoGroup.TranferTo(TSynEditUndoGroup(fItems[fItems.Count - 1]));
|
||||||
|
end else begin
|
||||||
|
ugroup := TSynEditUndoGroup.Create;
|
||||||
|
FUndoGroup.TranferTo(ugroup);
|
||||||
|
fItems.Add(ugroup);
|
||||||
|
end;
|
||||||
|
if Assigned(fOnAdded) then
|
||||||
|
fOnAdded(Self);
|
||||||
|
FIsInsideRedo := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.EnsureMaxEntries;
|
||||||
|
var
|
||||||
|
Item: TSynEditUndoGroup;
|
||||||
|
begin
|
||||||
|
if fItems.Count > fMaxUndoActions then begin
|
||||||
|
fFullUndoImposible := TRUE;
|
||||||
|
while fItems.Count > fMaxUndoActions do begin
|
||||||
|
Item := TSynEditUndoGroup(fItems[0]);
|
||||||
|
Item.Free;
|
||||||
|
fItems.Delete(0);
|
||||||
|
if fUnModifiedItem>=0 then dec(fUnModifiedItem);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.GetCanUndo: boolean;
|
||||||
|
begin
|
||||||
|
Result := fItems.Count > 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.GetCurrentReason: TSynEditorCommand;
|
||||||
|
begin
|
||||||
|
Result := FUndoGroup.Reason;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.GetItemCount: integer;
|
||||||
|
begin
|
||||||
|
Result := fItems.Count;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.SetCurrentReason(const AValue: TSynEditorCommand);
|
||||||
|
begin
|
||||||
|
if FUndoGroup.Reason = ecNone then
|
||||||
|
FUndoGroup.Reason := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.Lock;
|
||||||
|
begin
|
||||||
|
Inc(fLockCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.PopItem: TSynEditUndoGroup;
|
||||||
|
var
|
||||||
|
iLast: integer;
|
||||||
|
begin
|
||||||
|
Result := nil;
|
||||||
|
iLast := fItems.Count - 1;
|
||||||
|
if iLast >= 0 then begin
|
||||||
|
Result := TSynEditUndoGroup(fItems[iLast]);
|
||||||
|
fItems.Delete(iLast);
|
||||||
|
if fUnModifiedItem>fItems.Count then
|
||||||
|
fUnModifiedItem:=-1;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.SetMaxUndoActions(Value: integer);
|
||||||
|
begin
|
||||||
|
if Value < 0 then
|
||||||
|
Value := 0;
|
||||||
|
if Value <> fMaxUndoActions then begin
|
||||||
|
fMaxUndoActions := Value;
|
||||||
|
EnsureMaxEntries;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.RealCount: Integer;
|
||||||
|
begin
|
||||||
|
Result := fItems.Count;
|
||||||
|
if (FInGroupCount > 0) and FUndoGroup.HasUndoInfo then
|
||||||
|
Result := Result + 1;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.Unlock;
|
||||||
|
begin
|
||||||
|
if fLockCount > 0 then
|
||||||
|
Dec(fLockCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.IsLocked: Boolean;
|
||||||
|
begin
|
||||||
|
Result := fLockCount > 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoList.MarkTopAsUnmodified;
|
||||||
|
begin
|
||||||
|
fUnModifiedItem := RealCount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.IsTopMarkedAsUnmodified: boolean;
|
||||||
|
begin
|
||||||
|
Result := (RealCount = fUnModifiedItem);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoList.UnModifiedMarkerExists: boolean;
|
||||||
|
begin
|
||||||
|
Result := fUnModifiedItem >= 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoItem }
|
||||||
|
|
||||||
|
function TSynEditUndoItem.IsEqualContent(AnItem: TSynEditUndoItem): Boolean;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoItem.IsEqual(AnItem: TSynEditUndoItem): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (ClassType = AnItem.ClassType);
|
||||||
|
if Result then Result := Result and IsEqualContent(AnItem);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoItem.IsCaretInfo: Boolean;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
(*
|
||||||
|
function TSynEditUndoItem.ChangeStartPos: TPoint;
|
||||||
|
begin
|
||||||
|
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
||||||
|
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
||||||
|
then result := fChangeStartPos
|
||||||
|
else result := fChangeEndPos;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoItem.ChangeEndPos: TPoint;
|
||||||
|
begin
|
||||||
|
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
||||||
|
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
||||||
|
then result := fChangeEndPos
|
||||||
|
else result := fChangeStartPos;
|
||||||
|
end;
|
||||||
|
*)
|
||||||
|
|
||||||
|
{ TSynEditUndoGroup }
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.Grow;
|
||||||
|
begin
|
||||||
|
FCapacity := FCapacity + Max(10, FCapacity Div 8);
|
||||||
|
SetLength(FItems, FCapacity);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoGroup.HasUndoInfo: Boolean;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
i := 0;
|
||||||
|
while i < FCount do
|
||||||
|
if FItems[i].IsCaretInfo then
|
||||||
|
inc(i)
|
||||||
|
else
|
||||||
|
exit(true);
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.Append(AnUndoGroup: TSynEditUndoItem);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
i := FCount - 1;
|
||||||
|
while (i >= 0) and FItems[i].IsCaretInfo do
|
||||||
|
dec(i);
|
||||||
|
inc(i);
|
||||||
|
Insert(i, AnUndoGroup);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.TranferTo(AnUndoGroup: TSynEditUndoGroup);
|
||||||
|
begin
|
||||||
|
AnUndoGroup.Assign(self);
|
||||||
|
FCount := 0; // Do not clear; that would free the items
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoGroup.CanMergeWith(AnUndoGroup: TSynEditUndoGroup): Boolean;
|
||||||
|
begin
|
||||||
|
// Check if the other group can be merged to the START of this node
|
||||||
|
if AnUndoGroup.Count = 0 then exit(True);
|
||||||
|
Result := (FReason <> ecNone) and (AnUndoGroup.Reason = FReason)
|
||||||
|
and Items[0].IsCaretInfo
|
||||||
|
and AnUndoGroup.Items[AnUndoGroup.Count - 1].IsEqual(Items[0]);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.MergeWith(AnUndoGroup: TSynEditUndoGroup);
|
||||||
|
begin
|
||||||
|
// Merge other group to start
|
||||||
|
AnUndoGroup.Pop.Free;
|
||||||
|
if AnUndoGroup.Count > 0 then begin
|
||||||
|
fItems[0].Free;
|
||||||
|
fItems[0] := AnUndoGroup.Pop;
|
||||||
|
end;
|
||||||
|
while AnUndoGroup.Count > 0 do
|
||||||
|
Insert(0, AnUndoGroup.Pop);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoGroup.GetItem(Index: Integer): TSynEditUndoItem;
|
||||||
|
begin
|
||||||
|
Result := FItems[Index];
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TSynEditUndoGroup.Create;
|
||||||
|
begin
|
||||||
|
FCount := 0;
|
||||||
|
FCapacity := 0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TSynEditUndoGroup.Destroy;
|
||||||
|
begin
|
||||||
|
Clear;
|
||||||
|
FItems := nil;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.Assign(AnUndoGroup: TSynEditUndoGroup);
|
||||||
|
begin
|
||||||
|
Clear;
|
||||||
|
FCapacity := AnUndoGroup.Count;
|
||||||
|
FCount := FCapacity;
|
||||||
|
SetLength(FItems, FCapacity);
|
||||||
|
if FCapacity = 0 then
|
||||||
|
exit;
|
||||||
|
System.Move(AnUndoGroup.FItems[0], FItems[0], FCapacity * SizeOf(TSynEditUndoItem));
|
||||||
|
FReason := AnUndoGroup.Reason;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.Add(AnItem: TSynEditUndoItem);
|
||||||
|
begin
|
||||||
|
if (FCount > 0) and AnItem.IsCaretInfo
|
||||||
|
and FItems[FCount - 1].IsCaretInfo then
|
||||||
|
begin
|
||||||
|
FItems[FCount - 1].Free;
|
||||||
|
FItems[FCount - 1] := AnItem;
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
if FCount >= FCapacity then
|
||||||
|
Grow;
|
||||||
|
FItems[FCount] := AnItem;
|
||||||
|
inc (FCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.Clear;
|
||||||
|
begin
|
||||||
|
while FCount > 0 do begin
|
||||||
|
dec(FCount);
|
||||||
|
FItems[FCount].Free;
|
||||||
|
end;
|
||||||
|
if FCapacity > 100 then begin
|
||||||
|
FCapacity := 100;
|
||||||
|
SetLength(FItems, FCapacity);
|
||||||
|
end;
|
||||||
|
FReason := ecNone;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditUndoGroup.Insert(AIndex: Integer; AnItem: TSynEditUndoItem);
|
||||||
|
begin
|
||||||
|
if FCount >= FCapacity then
|
||||||
|
Grow;
|
||||||
|
System.Move(FItems[AIndex], FItems[AIndex+1],
|
||||||
|
(FCount - AIndex) * SizeOf(TSynEditUndoItem));
|
||||||
|
FItems[AIndex] := AnItem;
|
||||||
|
inc (FCount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoGroup.Pop: TSynEditUndoItem;
|
||||||
|
begin
|
||||||
|
if FCount <= 0 then
|
||||||
|
exit(nil);
|
||||||
|
dec(FCount);
|
||||||
|
Result := FItems[FCount];
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -43,18 +43,13 @@ interface
|
|||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, SysUtils, SynEditTextBase,
|
Classes, SysUtils, SynEditTextBase,
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
FileUtil, LCLProc, FPCAdds, LCLIntf, LCLType,
|
FileUtil, LCLProc, FPCAdds, LCLIntf, LCLType,
|
||||||
{$ELSE}
|
SynEditTypes, SynEditMiscProcs;
|
||||||
Windows,
|
|
||||||
{$ENDIF}
|
const
|
||||||
SynEditTypes, SynEditMiscProcs; //mh 2000-10-19
|
NullRange = TSynEditRange(-1);
|
||||||
|
|
||||||
type
|
type
|
||||||
{begin} //mh 2000-10-10
|
|
||||||
{$IFNDEF SYN_LAZARUS}
|
|
||||||
TSynEditRange = pointer;
|
|
||||||
{$ENDIF}
|
|
||||||
TSynEditRangeClass = class end; // For Register
|
TSynEditRangeClass = class end; // For Register
|
||||||
TSynEditFlagsClass = class end; // For Register
|
TSynEditFlagsClass = class end; // For Register
|
||||||
|
|
||||||
@ -65,26 +60,6 @@ type
|
|||||||
);
|
);
|
||||||
TSynEditStringFlags = set of TSynEditStringFlag;
|
TSynEditStringFlags = set of TSynEditStringFlag;
|
||||||
|
|
||||||
TSynChangeReason = (crInsert, crPaste, crDragDropInsert,
|
|
||||||
// Note: crSelDelete and crDragDropDelete have been deleted, because
|
|
||||||
// several undo entries can be chained together now via the ChangeNumber
|
|
||||||
// see also TCustomSynEdit.[Begin|End]UndoBlock methods
|
|
||||||
crDeleteAfterCursor, crDelete, {crSelDelete, crDragDropDelete, } //mh 2000-11-20
|
|
||||||
crLineBreak, crIndent, crUnindent,
|
|
||||||
crSilentDelete, crSilentDeleteAfterCursor, //mh 2000-10-30
|
|
||||||
crNothing {$IFDEF SYN_LAZARUS}, crTrimSpace, crTrimRealSpace {$ENDIF});
|
|
||||||
|
|
||||||
const
|
|
||||||
SynChangeReasonNames : Array [TSynChangeReason] of string =
|
|
||||||
('crInsert', 'crPaste', 'crDragDropInsert',
|
|
||||||
'crDeleteAfterCursor', 'crDelete', {'crSelDelete', 'crDragDropDelete', }
|
|
||||||
'crLineBreak', 'crIndent', 'crUnindent',
|
|
||||||
'crSilentDelete', 'crSilentDeleteAfterCursor',
|
|
||||||
'crNothing' {$IFDEF SYN_LAZARUS}, 'crTrimSpace', 'crTrimRealSpace' {$ENDIF});
|
|
||||||
|
|
||||||
NullRange = TSynEditRange(-1);
|
|
||||||
|
|
||||||
type
|
|
||||||
TStringListIndexEvent = procedure(Index: Integer) of object;
|
TStringListIndexEvent = procedure(Index: Integer) of object;
|
||||||
|
|
||||||
TSynEditStringAttribute = record
|
TSynEditStringAttribute = record
|
||||||
@ -97,7 +72,7 @@ type
|
|||||||
|
|
||||||
TLineRangeNotificationList = Class(TMethodList)
|
TLineRangeNotificationList = Class(TMethodList)
|
||||||
public
|
public
|
||||||
procedure CallRangeNotifyEvents(Sender: TSynEditStrings; aIndex, aCount: Integer);
|
Procedure CallRangeNotifyEvents(Sender: TSynEditStrings; aIndex, aCount: Integer);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
@ -146,6 +121,9 @@ type
|
|||||||
fIndexOfLongestLine: integer;
|
fIndexOfLongestLine: integer;
|
||||||
fOnChange: TNotifyEvent;
|
fOnChange: TNotifyEvent;
|
||||||
fOnChanging: TNotifyEvent;
|
fOnChanging: TNotifyEvent;
|
||||||
|
fRedoList: TSynEditUndoList;
|
||||||
|
fUndoList: TSynEditUndoList;
|
||||||
|
FIsUndoing: Boolean;
|
||||||
|
|
||||||
{$IFDEF SYN_LAZARUS}
|
{$IFDEF SYN_LAZARUS}
|
||||||
function GetFlags(Index: Integer): TSynEditStringFlags;
|
function GetFlags(Index: Integer): TSynEditStringFlags;
|
||||||
@ -159,6 +137,13 @@ type
|
|||||||
fOnCleared: TNotifyEvent;
|
fOnCleared: TNotifyEvent;
|
||||||
function GetExpandedString(Index: integer): string; override;
|
function GetExpandedString(Index: integer): string; override;
|
||||||
function GetLengthOfLongestLine: integer; override;
|
function GetLengthOfLongestLine: integer; override;
|
||||||
|
|
||||||
|
function GetRedoList: TSynEditUndoList; override;
|
||||||
|
function GetUndoList: TSynEditUndoList; override;
|
||||||
|
procedure SetIsUndoing(const AValue: Boolean); override;
|
||||||
|
procedure SendNotification(AReason: TSynEditNotifyReason;
|
||||||
|
ASender: TSynEditStrings; aIndex, aCount: Integer); override;
|
||||||
|
|
||||||
function GetRange(Index: integer): TSynEditRange; {$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
function GetRange(Index: integer): TSynEditRange; {$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
||||||
procedure PutRange(Index: integer; ARange: TSynEditRange); {$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
procedure PutRange(Index: integer; ARange: TSynEditRange); {$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
||||||
function GetAttribute(const Owner: TClass; const Index: Integer): Pointer; override;
|
function GetAttribute(const Owner: TClass; const Index: Integer): Pointer; override;
|
||||||
@ -174,6 +159,8 @@ type
|
|||||||
procedure SetCapacity(NewCapacity: integer);
|
procedure SetCapacity(NewCapacity: integer);
|
||||||
{$IFDEF SYN_COMPILER_3_UP} override; {$ENDIF} //mh 2000-10-18
|
{$IFDEF SYN_COMPILER_3_UP} override; {$ENDIF} //mh 2000-10-18
|
||||||
procedure SetUpdateState(Updating: Boolean); override;
|
procedure SetUpdateState(Updating: Boolean); override;
|
||||||
|
|
||||||
|
procedure UndoEditLinesDelete(LogY, ACount: Integer);
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
@ -191,7 +178,7 @@ type
|
|||||||
{$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
{$IFDEF SYN_LAZARUS}override;{$ENDIF}
|
||||||
{$IFDEF SYN_LAZARUS}
|
{$IFDEF SYN_LAZARUS}
|
||||||
procedure ClearRanges(ARange: TSynEditRange); override;
|
procedure ClearRanges(ARange: TSynEditRange); override;
|
||||||
procedure MarkModified(AFirst, ALast: Integer; AUndo: Boolean; AReason: TSynChangeReason);
|
procedure MarkModified(AFirst, ALast: Integer);
|
||||||
procedure MarkSaved;
|
procedure MarkSaved;
|
||||||
procedure SetDebugMarks(AFirst, ALast: Integer);
|
procedure SetDebugMarks(AFirst, ALast: Integer);
|
||||||
procedure ClearDebugMarks;
|
procedure ClearDebugMarks;
|
||||||
@ -212,78 +199,22 @@ type
|
|||||||
property Flags[Index: Integer]: TSynEditStringFlags read GetFlags
|
property Flags[Index: Integer]: TSynEditStringFlags read GetFlags
|
||||||
write SetFlags;
|
write SetFlags;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
public
|
||||||
|
property UndoList: TSynEditUndoList read GetUndoList write fUndoList;
|
||||||
|
property RedoList: TSynEditUndoList read GetRedoList write fRedoList;
|
||||||
|
procedure EditInsert(LogX, LogY: Integer; AText: String); override;
|
||||||
|
function EditDelete(LogX, LogY, ByteLen: Integer): String; override;
|
||||||
|
procedure EditLineBreak(LogX, LogY: Integer); override;
|
||||||
|
procedure EditLineJoin(LogY: Integer; FillText: String = ''); override;
|
||||||
|
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); override;
|
||||||
|
procedure EditLinesDelete(LogY, ACount: Integer); override;
|
||||||
|
procedure EditUndo(Item: TSynEditUndoItem); override;
|
||||||
|
procedure EditRedo(Item: TSynEditUndoItem); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
ESynEditStringList = class(Exception);
|
ESynEditStringList = class(Exception);
|
||||||
{end} //mh 2000-10-10
|
{end} //mh 2000-10-10
|
||||||
|
|
||||||
{ TSynEditUndoItem }
|
|
||||||
|
|
||||||
TSynEditUndoItem = class(TObject)
|
|
||||||
public
|
|
||||||
fChangeReason: TSynChangeReason;
|
|
||||||
fChangeSelMode: TSynSelectionMode;
|
|
||||||
fChangeStartPos: TPoint; // logical position (byte)
|
|
||||||
fChangeEndPos: TPoint; // logical position (byte)
|
|
||||||
fChangeStr: string;
|
|
||||||
fChangeNumber: integer; //sbs 2000-11-19
|
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
function ChangeStartPos: TPoint; // logical position (byte)
|
|
||||||
function ChangeEndPos: TPoint; // logical position (byte)
|
|
||||||
{$ENDIF}
|
|
||||||
end;
|
|
||||||
|
|
||||||
{ TSynEditUndoList }
|
|
||||||
|
|
||||||
TSynEditUndoList = class(TObject)
|
|
||||||
private
|
|
||||||
fBlockChangeNumber: integer; //sbs 2000-11-19
|
|
||||||
fBlockCount: integer; //sbs 2000-11-19
|
|
||||||
fFullUndoImposible: boolean; //mh 2000-10-03
|
|
||||||
fItems: TList;
|
|
||||||
fLockCount: integer;
|
|
||||||
fMaxUndoActions: integer;
|
|
||||||
fNextChangeNumber: integer; //sbs 2000-11-19
|
|
||||||
fOnAdded: TNotifyEvent;
|
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
fUnModifiedItem: integer;
|
|
||||||
{$ENDIF}
|
|
||||||
procedure EnsureMaxEntries;
|
|
||||||
function GetCanUndo: boolean;
|
|
||||||
function GetItemCount: integer;
|
|
||||||
procedure SetMaxUndoActions(Value: integer);
|
|
||||||
public
|
|
||||||
constructor Create;
|
|
||||||
destructor Destroy; override;
|
|
||||||
procedure AddChange(AReason: TSynChangeReason; AStart, AEnd: TPoint;
|
|
||||||
ChangeText: string; SelMode: TSynSelectionMode);
|
|
||||||
procedure AppendToLastChange(AReason: TSynChangeReason; AStart, AEnd: TPoint;
|
|
||||||
ChangeText: string; SelMode: TSynSelectionMode);
|
|
||||||
procedure BeginBlock; //sbs 2000-11-19
|
|
||||||
procedure Clear;
|
|
||||||
procedure EndBlock; //sbs 2000-11-19
|
|
||||||
procedure Lock;
|
|
||||||
function PeekItem: TSynEditUndoItem;
|
|
||||||
function PopItem: TSynEditUndoItem;
|
|
||||||
procedure PushItem(Item: TSynEditUndoItem);
|
|
||||||
procedure Unlock;
|
|
||||||
function IsLocked: Boolean;
|
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
procedure MarkTopAsUnmodified;
|
|
||||||
function IsTopMarkedAsUnmodified: boolean;
|
|
||||||
function UnModifiedMarkerExists: boolean;
|
|
||||||
{$ENDIF}
|
|
||||||
public
|
|
||||||
property BlockChangeNumber: integer read fBlockChangeNumber //sbs 2000-11-19
|
|
||||||
write fBlockChangeNumber;
|
|
||||||
property CanUndo: boolean read GetCanUndo;
|
|
||||||
property FullUndoImpossible: boolean read fFullUndoImposible; //mh 2000-10-03
|
|
||||||
property ItemCount: integer read GetItemCount;
|
|
||||||
property MaxUndoActions: integer read fMaxUndoActions
|
|
||||||
write SetMaxUndoActions;
|
|
||||||
property OnAddedUndo: TNotifyEvent read fOnAdded write fOnAdded;
|
|
||||||
end;
|
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
{$IFNDEF FPC}
|
{$IFNDEF FPC}
|
||||||
@ -297,6 +228,161 @@ const
|
|||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
SListIndexOutOfBounds = 'Invalid stringlist index %d';
|
SListIndexOutOfBounds = 'Invalid stringlist index %d';
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtInsert }
|
||||||
|
|
||||||
|
TSynEditUndoTxtInsert = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosX, FPosY, FLen: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(APosX, APosY, ALen: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtDelete }
|
||||||
|
|
||||||
|
TSynEditUndoTxtDelete = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosX, FPosY: Integer;
|
||||||
|
FText: String;
|
||||||
|
public
|
||||||
|
constructor Create(APosX, APosY: Integer; AText: String);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLineBreak }
|
||||||
|
|
||||||
|
TSynEditUndoTxtLineBreak = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosY: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(APosY: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLineJoin }
|
||||||
|
|
||||||
|
TSynEditUndoTxtLineJoin = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosX, FPosY: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(APosX, APosY: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLinesIns }
|
||||||
|
|
||||||
|
TSynEditUndoTxtLinesIns = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosY, FCount: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(ALine, ACount: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLinesDel }
|
||||||
|
|
||||||
|
TSynEditUndoTxtLinesDel = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosY, FCount: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(ALine, ACount: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtInsert }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTxtInsert.Create(APosX, APosY, ALen: Integer);
|
||||||
|
begin
|
||||||
|
FPosX := APosX;
|
||||||
|
FPosY := APosY;
|
||||||
|
FLen := ALen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTxtInsert.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringList;
|
||||||
|
if Result then
|
||||||
|
TSynEditStringList(Caller).EditDelete(FPosX, FPosY, FLen);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtDelete }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTxtDelete.Create(APosX, APosY: Integer; AText: String);
|
||||||
|
begin
|
||||||
|
FPosX := APosX;
|
||||||
|
FPosY := APosY;
|
||||||
|
FText := AText;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTxtDelete.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringList;
|
||||||
|
if Result then
|
||||||
|
TSynEditStringList(Caller).EditInsert(FPosX, FPosY, FText);
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLineBreak }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTxtLineBreak.Create(APosY: Integer);
|
||||||
|
begin
|
||||||
|
FPosY := APosY;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTxtLineBreak.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringList;
|
||||||
|
if Result then
|
||||||
|
TSynEditStringList(Caller).EditLineJoin(FPosY)
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLineJoin }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTxtLineJoin.Create(APosX, APosY: Integer);
|
||||||
|
begin
|
||||||
|
FPosX := APosX;
|
||||||
|
FPosY := APosY;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTxtLineJoin.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringList;
|
||||||
|
if Result then
|
||||||
|
TSynEditStringList(Caller).EditLineBreak(FPosX, FPosY)
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLinesIns }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTxtLinesIns.Create(ALine, ACount: Integer);
|
||||||
|
begin
|
||||||
|
FPosY := ALine;
|
||||||
|
FCount := ACount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTxtLinesIns.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringList;
|
||||||
|
if Result then
|
||||||
|
TSynEditStringList(Caller).UndoEditLinesDelete(FPosY, FCount)
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTxtLinesDel }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTxtLinesDel.Create(ALine, ACount: Integer);
|
||||||
|
begin
|
||||||
|
FPosY := ALine;
|
||||||
|
FCount := ACount;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTxtLinesDel.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringList;
|
||||||
|
if Result then
|
||||||
|
TSynEditStringList(Caller).EditLinesInsert(FPosY, FCount)
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TSynEditStringList }
|
{ TSynEditStringList }
|
||||||
|
|
||||||
procedure ListIndexOutOfBounds(Index: integer);
|
procedure ListIndexOutOfBounds(Index: integer);
|
||||||
@ -490,6 +576,24 @@ begin
|
|||||||
Result := 0;
|
Result := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TSynEditStringList.GetRedoList: TSynEditUndoList;
|
||||||
|
begin
|
||||||
|
Result := fRedoList;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditStringList.GetUndoList: TSynEditUndoList;
|
||||||
|
begin
|
||||||
|
if FIsUndoing then
|
||||||
|
Result := fRedoList
|
||||||
|
else
|
||||||
|
Result := fUndoList;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringList.SetIsUndoing(const AValue: Boolean);
|
||||||
|
begin
|
||||||
|
FIsUndoing := AValue;
|
||||||
|
end;
|
||||||
|
|
||||||
// Maps the Physical Width (ScreenCells) to each character
|
// Maps the Physical Width (ScreenCells) to each character
|
||||||
// Multibyte Chars have thw width on the first byte, and a 0 Width for all other bytes
|
// Multibyte Chars have thw width on the first byte, and a 0 Width for all other bytes
|
||||||
function TSynEditStringList.GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths;
|
function TSynEditStringList.GetPhysicalCharWidths(const Line: String; Index: Integer): TPhysicalCharWidths;
|
||||||
@ -512,8 +616,6 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{end} //mh 2000-10-19
|
|
||||||
|
|
||||||
function TSynEditStringList.GetObject(Index: integer): TObject;
|
function TSynEditStringList.GetObject(Index: integer): TObject;
|
||||||
begin
|
begin
|
||||||
if (Index >= 0) and (Index < Count) then
|
if (Index >= 0) and (Index < Count) then
|
||||||
@ -726,15 +828,11 @@ begin
|
|||||||
Ranges[Index] := ARange;
|
Ranges[Index] := ARange;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSynEditStringList.MarkModified(AFirst, ALast: Integer;
|
procedure TSynEditStringList.MarkModified(AFirst, ALast: Integer);
|
||||||
AUndo: Boolean; AReason: TSynChangeReason);
|
|
||||||
var
|
var
|
||||||
Index: Integer;
|
Index: Integer;
|
||||||
begin
|
begin
|
||||||
// AUndo = True => this change is also pushed to the undo list, False => to the redo list
|
for Index := AFirst - 1 to ALast - 1 do
|
||||||
// AReason - a reason of change
|
|
||||||
|
|
||||||
for Index := AFirst to ALast do
|
|
||||||
if (Index >= 0) and (Index < Count) then
|
if (Index >= 0) and (Index < Count) then
|
||||||
Flags[Index] := Flags[Index] + [sfModified] - [sfSaved];
|
Flags[Index] := Flags[Index] + [sfModified] - [sfSaved];
|
||||||
end;
|
end;
|
||||||
@ -799,257 +897,105 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TSynEditUndoList }
|
procedure TSynEditStringList.EditInsert(LogX, LogY: Integer; AText: String);
|
||||||
|
|
||||||
constructor TSynEditUndoList.Create;
|
|
||||||
begin
|
|
||||||
inherited Create;
|
|
||||||
fItems := TList.Create;
|
|
||||||
fMaxUndoActions := 1024;
|
|
||||||
fNextChangeNumber := 1; //sbs 2000-11-19
|
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
fUnModifiedItem:=-1;
|
|
||||||
{$ENDIF}
|
|
||||||
end;
|
|
||||||
|
|
||||||
destructor TSynEditUndoList.Destroy;
|
|
||||||
begin
|
|
||||||
Clear;
|
|
||||||
fItems.Free;
|
|
||||||
inherited Destroy;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TSynEditUndoList.AddChange(AReason: TSynChangeReason; AStart,
|
|
||||||
AEnd: TPoint; ChangeText: string; SelMode: TSynSelectionMode);
|
|
||||||
var
|
var
|
||||||
NewItem: TSynEditUndoItem;
|
s: string;
|
||||||
begin
|
begin
|
||||||
if fLockCount = 0 then begin
|
s := Strings[LogY - 1];
|
||||||
NewItem := TSynEditUndoItem.Create;
|
if LogX - 1 > Length(s) then
|
||||||
try
|
AText := StringOfChar(' ', LogX - 1 - Length(s)) + AText;
|
||||||
with NewItem do begin
|
Strings[LogY - 1] := copy(s,1, LogX - 1) + AText + copy(s, LogX, length(s));
|
||||||
fChangeReason := AReason;
|
UndoList.AddChange(TSynEditUndoTxtInsert.Create(LogX, LogY, Length(AText)));
|
||||||
fChangeSelMode := SelMode;
|
MarkModified(LogY, LogY);
|
||||||
fChangeStartPos := AStart;
|
|
||||||
fChangeEndPos := AEnd;
|
|
||||||
fChangeStr := ChangeText;
|
|
||||||
{begin} //sbs 2000-11-19
|
|
||||||
if fBlockChangeNumber <> 0 then
|
|
||||||
fChangeNumber := fBlockChangeNumber
|
|
||||||
else begin
|
|
||||||
fChangeNumber := fNextChangeNumber;
|
|
||||||
if fBlockCount = 0 then begin
|
|
||||||
Inc(fNextChangeNumber);
|
|
||||||
if fNextChangeNumber = 0 then
|
|
||||||
Inc(fNextChangeNumber);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
{end} //sbs 2000-11-19
|
|
||||||
end;
|
|
||||||
(* DebugLn(['TSynEditUndoList.AddChange ChangeNumber=',NewItem.fChangeNumber,
|
|
||||||
' Reason=', SynChangeReasonNames[AReason],' Astart=',dbgs(AStart),
|
|
||||||
' AEnd=',dbgs(AEnd),' SelMode=',ord(SelMode)]); *)
|
|
||||||
PushItem(NewItem);
|
|
||||||
except
|
|
||||||
NewItem.Free;
|
|
||||||
raise;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSynEditUndoList.AppendToLastChange(AReason: TSynChangeReason; AStart,
|
function TSynEditStringList.EditDelete(LogX, LogY, ByteLen: Integer): String;
|
||||||
AEnd: TPoint; ChangeText: string; SelMode: TSynSelectionMode);
|
|
||||||
var
|
var
|
||||||
NewItem: TSynEditUndoItem;
|
s: string;
|
||||||
begin
|
begin
|
||||||
if (fLockCount = 0) and (PeekItem <> nil) then begin
|
s := Strings[LogY - 1];
|
||||||
if (fItems.Count = fUnModifiedItem) then
|
Result := copy(s, LogX, ByteLen);
|
||||||
inc(fUnModifiedItem);
|
Strings[LogY - 1] := copy(s,1, LogX - 1) + copy(s, LogX + ByteLen, length(s));
|
||||||
NewItem := TSynEditUndoItem.Create;
|
UndoList.AddChange(TSynEditUndoTxtDelete.Create(LogX, LogY, Result));
|
||||||
try
|
MarkModified(LogY, LogY);
|
||||||
with NewItem do begin
|
|
||||||
fChangeReason := AReason;
|
|
||||||
fChangeSelMode := SelMode;
|
|
||||||
fChangeStartPos := AStart;
|
|
||||||
fChangeEndPos := AEnd;
|
|
||||||
fChangeStr := ChangeText;
|
|
||||||
fChangeNumber := PeekItem.fChangeNumber;
|
|
||||||
end;
|
|
||||||
//PushItem(NewItem);
|
|
||||||
fItems.Add(NewItem);
|
|
||||||
EnsureMaxEntries;
|
|
||||||
except
|
|
||||||
NewItem.Free;
|
|
||||||
raise;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{begin} //sbs 2000-11-19
|
procedure TSynEditStringList.EditLineBreak(LogX, LogY: Integer);
|
||||||
procedure TSynEditUndoList.BeginBlock;
|
|
||||||
begin
|
|
||||||
Inc(fBlockCount);
|
|
||||||
fBlockChangeNumber := fNextChangeNumber;
|
|
||||||
end;
|
|
||||||
{end} //sbs 2000-11-19
|
|
||||||
|
|
||||||
procedure TSynEditUndoList.Clear;
|
|
||||||
var
|
var
|
||||||
i: integer;
|
s: string;
|
||||||
begin
|
begin
|
||||||
for i := 0 to fItems.Count - 1 do
|
s := Strings[LogY - 1];
|
||||||
TSynEditUndoItem(fItems[i]).Free;
|
Strings[LogY - 1] := copy(s, 1, LogX - 1);
|
||||||
fItems.Clear;
|
Insert(LogY, copy(s, LogX, length(s)));
|
||||||
fFullUndoImposible := FALSE; //mh 2000-10-03
|
UndoList.AddChange(TSynEditUndoTxtLineBreak.Create(LogY));
|
||||||
{$IFDEF SYN_LAZARUS}
|
MarkModified(LogY, LogY + 1);
|
||||||
fUnModifiedItem:=-1;
|
|
||||||
{$ENDIF}
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{begin} //sbs 2000-11-19
|
procedure TSynEditStringList.EditLineJoin(LogY: Integer; FillText: String = '');
|
||||||
procedure TSynEditUndoList.EndBlock;
|
|
||||||
begin
|
|
||||||
if fBlockCount > 0 then begin
|
|
||||||
Dec(fBlockCount);
|
|
||||||
if fBlockCount = 0 then begin
|
|
||||||
fBlockChangeNumber := 0;
|
|
||||||
Inc(fNextChangeNumber);
|
|
||||||
if fNextChangeNumber = 0 then
|
|
||||||
Inc(fNextChangeNumber);
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
{end} //sbs 2000-11-19
|
|
||||||
|
|
||||||
procedure TSynEditUndoList.EnsureMaxEntries;
|
|
||||||
var
|
var
|
||||||
Item: TSynEditUndoItem;
|
t: string;
|
||||||
begin
|
begin
|
||||||
if fItems.Count > fMaxUndoActions then begin //mh 2000-10-03
|
t := Strings[LogY - 1];
|
||||||
fFullUndoImposible := TRUE; //mh 2000-10-03
|
if FillText <> '' then
|
||||||
while fItems.Count > fMaxUndoActions do begin
|
EditInsert(1 + Length(t), LogY, FillText);
|
||||||
Item := TSynEditUndoItem(fItems[0]);
|
UndoList.AddChange(TSynEditUndoTxtLineJoin.Create(1 + Length(Strings[LogY-1]),
|
||||||
Item.Free;
|
LogY));
|
||||||
fItems.Delete(0);
|
Strings[LogY - 1] := t + FillText + Strings[LogY] ;
|
||||||
{$IFDEF SYN_LAZARUS}
|
Delete(LogY);
|
||||||
if fUnModifiedItem>=0 then dec(fUnModifiedItem);
|
MarkModified(LogY, LogY);
|
||||||
{$ENDIF}
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSynEditUndoList.GetCanUndo: boolean;
|
procedure TSynEditStringList.EditLinesInsert(LogY, ACount: Integer;
|
||||||
|
AText: String = '');
|
||||||
begin
|
begin
|
||||||
Result := fItems.Count > 0;
|
InsertLines(LogY - 1, ACount);
|
||||||
|
UndoList.AddChange(TSynEditUndoTxtLinesIns.Create(LogY, ACount));
|
||||||
|
if AText <> '' then
|
||||||
|
EditInsert(1, LogY, AText);
|
||||||
|
MarkModified(LogY, LogY + ACount - 1);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSynEditUndoList.GetItemCount: integer;
|
procedure TSynEditStringList.EditLinesDelete(LogY, ACount: Integer);
|
||||||
begin
|
|
||||||
Result := fItems.Count;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TSynEditUndoList.Lock;
|
|
||||||
begin
|
|
||||||
Inc(fLockCount);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSynEditUndoList.PeekItem: TSynEditUndoItem;
|
|
||||||
var
|
var
|
||||||
iLast: integer;
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
Result := nil;
|
for i := LogY to LogY + ACount - 1 do
|
||||||
iLast := fItems.Count - 1;
|
EditDelete(1, i, length(Strings[i-1]));
|
||||||
if iLast >= 0 then
|
DeleteLines(LogY - 1, ACount);
|
||||||
Result := TSynEditUndoItem(fItems[iLast]);
|
UndoList.AddChange(TSynEditUndoTxtLinesDel.Create(LogY, ACount));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TSynEditUndoList.PopItem: TSynEditUndoItem;
|
procedure TSynEditStringList.EditUndo(Item: TSynEditUndoItem);
|
||||||
var
|
|
||||||
iLast: integer;
|
|
||||||
begin
|
begin
|
||||||
Result := nil;
|
IsUndoing := True;
|
||||||
iLast := fItems.Count - 1;
|
try
|
||||||
if iLast >= 0 then begin
|
EditRedo(Item);
|
||||||
Result := TSynEditUndoItem(fItems[iLast]);
|
finally
|
||||||
fItems.Delete(iLast);
|
IsUndoing := False;
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
if fUnModifiedItem>fItems.Count then fUnModifiedItem:=-1;
|
|
||||||
{$ENDIF}
|
|
||||||
(*DebugLn(['TSynEditUndoList.PopItem=',Result.fChangeNumber,
|
|
||||||
' Reason=', SynChangeReasonNames[Result.fChangeReason],' Astart=',dbgs(Result.fChangeStartPos),
|
|
||||||
' AEnd=',dbgs(result.fChangeEndPos),' SelMode=',ord(result.fChangeSelMode )]);*)
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSynEditUndoList.PushItem(Item: TSynEditUndoItem);
|
procedure TSynEditStringList.UndoEditLinesDelete(LogY, ACount: Integer);
|
||||||
begin
|
begin
|
||||||
if Assigned(Item) then begin
|
UndoList.AddChange(TSynEditUndoTxtLinesDel.Create(LogY, ACount));
|
||||||
fItems.Add(Item);
|
DeleteLines(LogY - 1, ACount);
|
||||||
EnsureMaxEntries;
|
end;
|
||||||
if Assigned(fOnAdded) then
|
|
||||||
fOnAdded(Self);
|
procedure TSynEditStringList.EditRedo(Item: TSynEditUndoItem);
|
||||||
|
begin
|
||||||
|
Item.PerformUndo(self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringList.SendNotification(AReason: TSynEditNotifyReason; ASender: TSynEditStrings; aIndex, aCount: Integer);
|
||||||
|
begin
|
||||||
|
case AReason of
|
||||||
|
senrLineChange:
|
||||||
|
FLineChangeNotificationList.CallRangeNotifyEvents(ASender, aIndex, aCount);
|
||||||
|
senrLineCount:
|
||||||
|
FLineRangeNotificationList.CallRangeNotifyEvents(ASender, aIndex, aCount);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSynEditUndoList.SetMaxUndoActions(Value: integer);
|
|
||||||
begin
|
|
||||||
if Value < 0 then
|
|
||||||
Value := 0;
|
|
||||||
if Value <> fMaxUndoActions then begin
|
|
||||||
fMaxUndoActions := Value;
|
|
||||||
EnsureMaxEntries;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
|
|
||||||
procedure TSynEditUndoList.Unlock;
|
|
||||||
begin
|
|
||||||
if fLockCount > 0 then
|
|
||||||
Dec(fLockCount);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSynEditUndoList.IsLocked: Boolean;
|
|
||||||
begin
|
|
||||||
Result := fLockCount > 0;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
procedure TSynEditUndoList.MarkTopAsUnmodified;
|
|
||||||
begin
|
|
||||||
fUnModifiedItem:=fItems.Count;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSynEditUndoList.IsTopMarkedAsUnmodified: boolean;
|
|
||||||
begin
|
|
||||||
Result:=(fItems.Count=fUnModifiedItem);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSynEditUndoList.UnModifiedMarkerExists: boolean;
|
|
||||||
begin
|
|
||||||
Result:=fUnModifiedItem>=0;
|
|
||||||
end;
|
|
||||||
|
|
||||||
{$ENDIF}
|
|
||||||
|
|
||||||
{ TSynEditUndoItem }
|
|
||||||
|
|
||||||
{$IFDEF SYN_LAZARUS}
|
|
||||||
function TSynEditUndoItem.ChangeStartPos: TPoint;
|
|
||||||
begin
|
|
||||||
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
|
||||||
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
|
||||||
then result := fChangeStartPos
|
|
||||||
else result := fChangeEndPos;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSynEditUndoItem.ChangeEndPos: TPoint;
|
|
||||||
begin
|
|
||||||
if (fChangeStartPos.Y < fChangeEndPos.Y)
|
|
||||||
or ((fChangeStartPos.Y = fChangeEndPos.Y) and (fChangeStartPos.X < fChangeEndPos.X))
|
|
||||||
then result := fChangeEndPos
|
|
||||||
else result := fChangeStartPos;
|
|
||||||
end;
|
|
||||||
{$ENDIF}
|
|
||||||
|
|
||||||
{ TSynEditStringMemory }
|
{ TSynEditStringMemory }
|
||||||
type
|
type
|
||||||
PObject = ^TObject;
|
PObject = ^TObject;
|
||||||
|
@ -29,7 +29,7 @@ interface
|
|||||||
uses
|
uses
|
||||||
LCLProc,
|
LCLProc,
|
||||||
Classes, SysUtils, SynEditTypes, SynEditTextBase, SynEditTextBuffer,
|
Classes, SysUtils, SynEditTypes, SynEditTextBase, SynEditTextBuffer,
|
||||||
SynEditPointClasses;
|
SynEditPointClasses, SynEditMiscProcs;
|
||||||
|
|
||||||
type
|
type
|
||||||
|
|
||||||
@ -42,7 +42,6 @@ type
|
|||||||
fCaret: TSynEditCaret;
|
fCaret: TSynEditCaret;
|
||||||
FIsTrimming: Boolean;
|
FIsTrimming: Boolean;
|
||||||
FTrimType: TSynEditStringTrimmingType;
|
FTrimType: TSynEditStringTrimmingType;
|
||||||
fUndoList: TSynEditUndoList;
|
|
||||||
fSpaces: String;
|
fSpaces: String;
|
||||||
fLineText: String;
|
fLineText: String;
|
||||||
fLineIndex: Integer;
|
fLineIndex: Integer;
|
||||||
@ -55,9 +54,15 @@ type
|
|||||||
procedure SetEnabled(const AValue : Boolean);
|
procedure SetEnabled(const AValue : Boolean);
|
||||||
procedure SetTrimType(const AValue: TSynEditStringTrimmingType);
|
procedure SetTrimType(const AValue: TSynEditStringTrimmingType);
|
||||||
function TrimLine(const S : String; Index: Integer; RealUndo: Boolean = False) : String;
|
function TrimLine(const S : String; Index: Integer; RealUndo: Boolean = False) : String;
|
||||||
|
procedure StoreSpacesForLine(const Index: Integer; const SpaceStr, LineStr: String);
|
||||||
function Spaces(Index: Integer) : String;
|
function Spaces(Index: Integer) : String;
|
||||||
procedure DoLinesChanged(Index, N: integer);
|
procedure DoLinesChanged(Index, N: integer);
|
||||||
procedure TrimAfterLock;
|
procedure TrimAfterLock;
|
||||||
|
procedure EditInsertTrim(LogX, LogY: Integer; AText: String);
|
||||||
|
function EditDeleteTrim(LogX, LogY, ByteLen: Integer): String;
|
||||||
|
procedure EditMoveToTrim(LogY, Len: Integer);
|
||||||
|
procedure EditMoveFromTrim(LogY, Len: Integer);
|
||||||
|
procedure UpdateLineText(LogY: Integer);
|
||||||
protected
|
protected
|
||||||
function GetExpandedString(Index: integer): string; override;
|
function GetExpandedString(Index: integer): string; override;
|
||||||
function GetLengthOfLongestLine: integer; override;
|
function GetLengthOfLongestLine: integer; override;
|
||||||
@ -83,16 +88,184 @@ type
|
|||||||
procedure Lock;
|
procedure Lock;
|
||||||
procedure UnLock;
|
procedure UnLock;
|
||||||
procedure ForceTrim; // for redo; redo can not wait for UnLock
|
procedure ForceTrim; // for redo; redo can not wait for UnLock
|
||||||
procedure UndoRealSpaces(Item: TSynEditUndoItem);
|
|
||||||
property Enabled : Boolean read fEnabled write SetEnabled;
|
property Enabled : Boolean read fEnabled write SetEnabled;
|
||||||
property UndoTrimmedSpaces: Boolean read FUndoTrimmedSpaces write FUndoTrimmedSpaces;
|
property UndoTrimmedSpaces: Boolean read FUndoTrimmedSpaces write FUndoTrimmedSpaces;
|
||||||
property UndoList: TSynEditUndoList read fUndoList write fUndoList;
|
|
||||||
property IsTrimming: Boolean read FIsTrimming;
|
property IsTrimming: Boolean read FIsTrimming;
|
||||||
property TrimType: TSynEditStringTrimmingType read FTrimType write SetTrimType;
|
property TrimType: TSynEditStringTrimmingType read FTrimType write SetTrimType;
|
||||||
|
public
|
||||||
|
procedure EditInsert(LogX, LogY: Integer; AText: String); override;
|
||||||
|
Function EditDelete(LogX, LogY, ByteLen: Integer): String; override;
|
||||||
|
procedure EditLineBreak(LogX, LogY: Integer); override;
|
||||||
|
procedure EditLineJoin(LogY: Integer; FillText: String = ''); override;
|
||||||
|
procedure EditLinesInsert(LogY, ACount: Integer; AText: String = ''); override;
|
||||||
|
procedure EditLinesDelete(LogY, ACount: Integer); override;
|
||||||
|
procedure EditUndo(Item: TSynEditUndoItem); override;
|
||||||
|
procedure EditRedo(Item: TSynEditUndoItem); override;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
|
type
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimMoveTo }
|
||||||
|
|
||||||
|
TSynEditUndoTrimMoveTo = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosY, FLen: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(APosY, ALen: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimMoveFrom }
|
||||||
|
|
||||||
|
TSynEditUndoTrimMoveFrom = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosY, FLen: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(APosY, ALen: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimInsert }
|
||||||
|
|
||||||
|
TSynEditUndoTrimInsert = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosX, FPosY, FLen: Integer;
|
||||||
|
public
|
||||||
|
constructor Create(APosX, APosY, ALen: Integer);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimDelete }
|
||||||
|
|
||||||
|
TSynEditUndoTrimDelete = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosX, FPosY: Integer;
|
||||||
|
FText: String;
|
||||||
|
public
|
||||||
|
constructor Create(APosX, APosY: Integer; AText: String);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimForget }
|
||||||
|
|
||||||
|
TSynEditUndoTrimForget = class(TSynEditUndoItem)
|
||||||
|
private
|
||||||
|
FPosY: Integer;
|
||||||
|
FText: String;
|
||||||
|
public
|
||||||
|
constructor Create(APosY: Integer; AText: String);
|
||||||
|
function PerformUndo(Caller: TObject): Boolean; override;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimMoveTo }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTrimMoveTo.Create(APosY, ALen: Integer);
|
||||||
|
begin
|
||||||
|
FPosY := APosY;
|
||||||
|
FLen := ALen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTrimMoveTo.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringTrimmingList;
|
||||||
|
if Result then
|
||||||
|
with TSynEditStringTrimmingList(Caller) do begin
|
||||||
|
EditMoveFromTrim(FPosY, FLen);
|
||||||
|
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||||
|
FPosY - 1, 1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimMoveFrom }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTrimMoveFrom.Create(APosY, ALen: Integer);
|
||||||
|
begin
|
||||||
|
FPosY := APosY;
|
||||||
|
FLen := ALen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTrimMoveFrom.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringTrimmingList;
|
||||||
|
if Result then
|
||||||
|
with TSynEditStringTrimmingList(Caller) do begin
|
||||||
|
EditMoveToTrim(FPosY, FLen);
|
||||||
|
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||||
|
FPosY - 1, 1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimInsert }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTrimInsert.Create(APosX, APosY, ALen: Integer);
|
||||||
|
begin
|
||||||
|
FPosX := APosX;
|
||||||
|
FPosY := APosY;
|
||||||
|
FLen := ALen;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTrimInsert.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringTrimmingList;
|
||||||
|
if Result then
|
||||||
|
with TSynEditStringTrimmingList(Caller) do begin
|
||||||
|
EditDeleteTrim(FPosX, FPosY, FLen);
|
||||||
|
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||||
|
FPosY - 1, 1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimDelete }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTrimDelete.Create(APosX, APosY: Integer; AText: String);
|
||||||
|
begin
|
||||||
|
FPosX := APosX;
|
||||||
|
FPosY := APosY;
|
||||||
|
FText := AText;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTrimDelete.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringTrimmingList;
|
||||||
|
if Result then
|
||||||
|
with TSynEditStringTrimmingList(Caller) do begin
|
||||||
|
EditInsertTrim(FPosX, FPosY, FText);
|
||||||
|
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||||
|
FPosY - 1, 1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ TSynEditUndoTrimForget }
|
||||||
|
|
||||||
|
constructor TSynEditUndoTrimForget.Create(APosY: Integer; AText: String);
|
||||||
|
begin
|
||||||
|
FPosY := APosY;
|
||||||
|
FText := AText;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditUndoTrimForget.PerformUndo(Caller: TObject): Boolean;
|
||||||
|
begin
|
||||||
|
Result := Caller is TSynEditStringTrimmingList;
|
||||||
|
if Result then
|
||||||
|
with TSynEditStringTrimmingList(Caller) do begin
|
||||||
|
UndoList.Lock;
|
||||||
|
EditInsertTrim(1, FPosY, FText);
|
||||||
|
UndoList.Unlock;
|
||||||
|
SendNotification(senrLineChange, TSynEditStringTrimmingList(Caller),
|
||||||
|
FPosY - 1, 1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function LastNoneSpacePos(const s: String): Integer;
|
||||||
|
begin
|
||||||
|
Result := length(s);
|
||||||
|
while (Result > 0) and (s[Result] in [#9, ' ']) do dec(Result);
|
||||||
|
end;
|
||||||
|
|
||||||
{ TSynEditStringTrimmingList }
|
{ TSynEditStringTrimmingList }
|
||||||
|
|
||||||
@ -138,8 +311,7 @@ begin
|
|||||||
s := fSynStrings[fLineIndex];
|
s := fSynStrings[fLineIndex];
|
||||||
fSynStrings[fLineIndex] := s; // trigger OnPutted, so the line gets repainted
|
fSynStrings[fLineIndex] := s; // trigger OnPutted, so the line gets repainted
|
||||||
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then begin
|
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then begin
|
||||||
fUndoList.AppendToLastChange(crTrimSpace, Point(1+length(s), fLineIndex+1),
|
UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, FSpaces));
|
||||||
Point(1+length(s)+length(fSpaces), fLineIndex+1), fSpaces, smNormal);
|
|
||||||
fSpaces := '';
|
fSpaces := '';
|
||||||
end else begin
|
end else begin
|
||||||
// same line, only right of caret
|
// same line, only right of caret
|
||||||
@ -150,8 +322,7 @@ begin
|
|||||||
j := i - length(s) - 1;
|
j := i - length(s) - 1;
|
||||||
s := copy(FSpaces, j + 1, MaxInt);
|
s := copy(FSpaces, j + 1, MaxInt);
|
||||||
FSpaces := copy(FSpaces, 1, j);
|
FSpaces := copy(FSpaces, 1, j);
|
||||||
fUndoList.AppendToLastChange(crTrimSpace, Point(i, fLineIndex+1),
|
UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(FLineIndex+1, s));
|
||||||
Point(i + length(s), fLineIndex+1), s, smNormal);
|
|
||||||
end;
|
end;
|
||||||
FIsTrimming := False;
|
FIsTrimming := False;
|
||||||
FLineEdited := False;
|
FLineEdited := False;
|
||||||
@ -180,21 +351,6 @@ begin
|
|||||||
FTrimType := AValue;
|
FTrimType := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TSynEditStringTrimmingList.UndoRealSpaces(Item: TSynEditUndoItem);
|
|
||||||
var
|
|
||||||
i: Integer;
|
|
||||||
begin
|
|
||||||
if (not fEnabled) then exit;
|
|
||||||
if length(fSynStrings.Strings[Item.fChangeStartPos.y-1]) + 1 <> Item.fChangeStartPos.x then
|
|
||||||
exit;
|
|
||||||
fSynStrings.Strings[Item.fChangeStartPos.y-1]
|
|
||||||
:= copy(fSynStrings.Strings[Item.fChangeStartPos.y-1],
|
|
||||||
1, Item.fChangeStartPos.x-1) + Item.fChangeStr;
|
|
||||||
if (fLineIndex = Item.fChangeStartPos.y-1) then fSpaces := '';
|
|
||||||
i := fLockList.IndexOfObject(TObject(Pointer(Item.fChangeStartPos.y-1)));
|
|
||||||
if i >= 0 then fLockList.Delete(i);
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TSynEditStringTrimmingList.TrimLine(const S: String; Index: Integer;
|
function TSynEditStringTrimmingList.TrimLine(const S: String; Index: Integer;
|
||||||
RealUndo: Boolean = False): String;
|
RealUndo: Boolean = False): String;
|
||||||
var
|
var
|
||||||
@ -205,35 +361,40 @@ begin
|
|||||||
if RealUndo then begin
|
if RealUndo then begin
|
||||||
temp := fSynStrings.Strings[Index];
|
temp := fSynStrings.Strings[Index];
|
||||||
l := length(temp);
|
l := length(temp);
|
||||||
i:= l;
|
i := LastNoneSpacePos(temp);
|
||||||
while (i>0) and (temp[i] in [#9, ' ']) do dec(i);
|
|
||||||
// Add RealSpaceUndo
|
// Add RealSpaceUndo
|
||||||
if i < l then
|
if i < l then
|
||||||
fUndoList.AddChange(crTrimRealSpace, Point(i+1, Index+1),
|
EditInsertTrim(1, Index + 1,
|
||||||
Point(l, Index+1), copy(temp, i+1, l-i), smNormal);
|
inherited EditDelete(1 + i, Index + 1, l - i));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
l := length(s);
|
l := length(s);
|
||||||
i := l;
|
i := LastNoneSpacePos(s);
|
||||||
while (i>0) and (s[i] in [#9, ' ']) do dec(i);
|
|
||||||
temp := copy(s, i+1, l-i);
|
temp := copy(s, i+1, l-i);
|
||||||
if i=l then
|
if i=l then
|
||||||
result := s // No need to make a copy
|
result := s // No need to make a copy
|
||||||
else
|
else
|
||||||
result := copy(s, 1, i);
|
result := copy(s, 1, i);
|
||||||
|
|
||||||
|
StoreSpacesForLine(Index, temp, Result);
|
||||||
|
end ;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.StoreSpacesForLine(const Index: Integer; const SpaceStr, LineStr: String);
|
||||||
|
var
|
||||||
|
i: LongInt;
|
||||||
|
begin
|
||||||
if fLockCount > 0 then begin
|
if fLockCount > 0 then begin
|
||||||
i := fLockList.IndexOfObject(TObject(pointer(Index)));
|
i := fLockList.IndexOfObject(TObject(pointer(Index)));
|
||||||
if i < 0 then
|
if i < 0 then
|
||||||
fLockList.AddObject(temp, TObject(pointer(Index)))
|
fLockList.AddObject(SpaceStr, TObject(pointer(Index)))
|
||||||
else
|
else
|
||||||
fLockList[i] := temp;
|
fLockList[i] := SpaceStr;
|
||||||
end
|
|
||||||
else if (fLineIndex = Index) then begin
|
|
||||||
fSpaces := temp;
|
|
||||||
fLineText:=result;
|
|
||||||
end;
|
end;
|
||||||
end ;
|
if (fLineIndex = Index) then begin
|
||||||
|
fSpaces := SpaceStr;
|
||||||
|
fLineText:= LineStr;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function TSynEditStringTrimmingList.Spaces(Index : Integer) : String;
|
function TSynEditStringTrimmingList.Spaces(Index : Integer) : String;
|
||||||
var
|
var
|
||||||
@ -267,7 +428,7 @@ begin
|
|||||||
j := Integer(Pointer(fLockList.Objects[i]));
|
j := Integer(Pointer(fLockList.Objects[i]));
|
||||||
if (j >= Index) and (j < Index - N) then
|
if (j >= Index) and (j < Index - N) then
|
||||||
fLockList.Delete(i)
|
fLockList.Delete(i)
|
||||||
else if j > Index then
|
else if j >= Index then
|
||||||
fLockList.Objects[i] := TObject(Pointer(j + N));
|
fLockList.Objects[i] := TObject(Pointer(j + N));
|
||||||
end;
|
end;
|
||||||
end else begin
|
end else begin
|
||||||
@ -295,7 +456,7 @@ end;
|
|||||||
|
|
||||||
procedure TSynEditStringTrimmingList.TrimAfterLock;
|
procedure TSynEditStringTrimmingList.TrimAfterLock;
|
||||||
var
|
var
|
||||||
i, index, llen, slen: Integer;
|
i, index, slen: Integer;
|
||||||
ltext: String;
|
ltext: String;
|
||||||
begin
|
begin
|
||||||
if (not fEnabled) then exit;
|
if (not fEnabled) then exit;
|
||||||
@ -314,10 +475,8 @@ begin
|
|||||||
slen := length(fLockList[i]);
|
slen := length(fLockList[i]);
|
||||||
if (slen > 0) and (index >= 0) and (index < fSynStrings.Count) then begin
|
if (slen > 0) and (index >= 0) and (index < fSynStrings.Count) then begin
|
||||||
ltext := fSynStrings[index];
|
ltext := fSynStrings[index];
|
||||||
llen := length(ltext);
|
|
||||||
fSynStrings[index] := ltext; // trigger OnPutted, so the line gets repainted
|
fSynStrings[index] := ltext; // trigger OnPutted, so the line gets repainted
|
||||||
fUndoList.AppendToLastChange(crTrimSpace, Point(1+llen, index+1),
|
UndoList.AppendToLastChange(TSynEditUndoTrimForget.Create(Index+1, fLockList[i]));
|
||||||
Point(1+llen+slen, index+1), fLockList[i], smNormal);
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
FIsTrimming := False;
|
FIsTrimming := False;
|
||||||
@ -451,5 +610,247 @@ begin
|
|||||||
fLineIndex := Index1;
|
fLineIndex := Index1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditInsertTrim(LogX, LogY: Integer;
|
||||||
|
AText: String);
|
||||||
|
var
|
||||||
|
s: string;
|
||||||
|
begin
|
||||||
|
if AText = '' then
|
||||||
|
exit;
|
||||||
|
s := Spaces(LogY - 1);
|
||||||
|
StoreSpacesForLine(LogY - 1,
|
||||||
|
copy(s,1, LogX - 1) + AText + copy(s, LogX, length(s)),
|
||||||
|
fSynStrings.Strings[LogY - 1]);
|
||||||
|
UndoList.AddChange(TSynEditUndoTrimInsert.Create(LogX, LogY, Length(AText)));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TSynEditStringTrimmingList.EditDeleteTrim(LogX, LogY, ByteLen:
|
||||||
|
Integer): String;
|
||||||
|
var
|
||||||
|
s: string;
|
||||||
|
begin
|
||||||
|
if ByteLen <= 0 then
|
||||||
|
exit('');
|
||||||
|
s := Spaces(LogY - 1);
|
||||||
|
Result := copy(s, LogX, ByteLen);
|
||||||
|
StoreSpacesForLine(LogY - 1,
|
||||||
|
copy(s,1, LogX - 1) + copy(s, LogX + ByteLen, length(s)),
|
||||||
|
fSynStrings.Strings[LogY - 1]);
|
||||||
|
UndoList.AddChange(TSynEditUndoTrimDelete.Create(LogX, LogY, Result));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditMoveToTrim(LogY, Len: Integer);
|
||||||
|
var
|
||||||
|
t, s: String;
|
||||||
|
begin
|
||||||
|
if Len <= 0 then
|
||||||
|
exit;
|
||||||
|
t := fSynStrings[LogY - 1];
|
||||||
|
s := copy(t, 1 + length(t) - Len, Len) + Spaces(LogY - 1);
|
||||||
|
t := copy(t, 1, length(t) - Len);
|
||||||
|
fSynStrings[LogY - 1] := t;
|
||||||
|
StoreSpacesForLine(LogY - 1, s, t);
|
||||||
|
UndoList.AddChange(TSynEditUndoTrimMoveTo.Create(LogY, Len));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditMoveFromTrim(LogY, Len: Integer);
|
||||||
|
var
|
||||||
|
t, s: String;
|
||||||
|
begin
|
||||||
|
if Len <= 0 then
|
||||||
|
exit;
|
||||||
|
s := Spaces(LogY - 1);
|
||||||
|
t := fSynStrings[LogY - 1] + copy(s, 1, Len);
|
||||||
|
s := copy(s, 1 + Len, Len);
|
||||||
|
fSynStrings[LogY - 1] := t;
|
||||||
|
StoreSpacesForLine(LogY - 1, s, t);
|
||||||
|
UndoList.AddChange(TSynEditUndoTrimMoveFrom.Create(LogY, Len));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.UpdateLineText(LogY: Integer);
|
||||||
|
begin
|
||||||
|
if LogY - 1 = fLineIndex then
|
||||||
|
fLineText := fSynStrings[LogY - 1];
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditInsert(LogX, LogY: Integer; AText: String);
|
||||||
|
var
|
||||||
|
t: String;
|
||||||
|
Len, LenNS: Integer;
|
||||||
|
IsSpaces: Boolean;
|
||||||
|
begin
|
||||||
|
if (not fEnabled) then begin
|
||||||
|
fSynStrings.EditInsert(LogX, LogY, AText);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
t := Strings[LogY - 1]; // include trailing
|
||||||
|
if LogX - 1 > Length(t) then begin
|
||||||
|
AText := StringOfChar(' ', LogX - 1 - Length(t)) + AText;
|
||||||
|
LogX := 1 + Length(t);
|
||||||
|
end;
|
||||||
|
IsSpaces := LastNoneSpacePos(AText) = 0;
|
||||||
|
t := fSynStrings[LogY - 1];
|
||||||
|
Len := length(t);
|
||||||
|
LenNS := LastNoneSpacePos(t);
|
||||||
|
if (LenNS < LogX - 1) and not IsSpaces then
|
||||||
|
LenNs := LogX - 1;
|
||||||
|
|
||||||
|
// Trim any existing (commited/real) spaces // skip if we append none-spaces
|
||||||
|
if (LenNS < Len) and (IsSpaces or (LogX <= len)) then
|
||||||
|
begin
|
||||||
|
EditMoveToTrim(LogY, Len - LenNS);
|
||||||
|
Len := LenNS;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if LogX > len then begin
|
||||||
|
if IsSpaces then begin
|
||||||
|
EditInsertTrim(LogX - Len, LogY, AText);
|
||||||
|
AText := '';
|
||||||
|
end else begin
|
||||||
|
// Get Fill Spaces
|
||||||
|
EditMoveFromTrim(LogY, LogX - 1 - len);
|
||||||
|
// Trim
|
||||||
|
Len := length(AText);
|
||||||
|
LenNS := LastNoneSpacePos(AText);
|
||||||
|
if LenNS < Len then begin
|
||||||
|
EditInsertTrim(1, LogY, copy(AText, 1 + LenNS, Len));
|
||||||
|
AText := copy(AText, 1, LenNS);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if AText <> '' then
|
||||||
|
inherited EditInsert(LogX, LogY, AText)
|
||||||
|
else
|
||||||
|
SendNotification(senrLineChange, self, LogY - 1, 1);
|
||||||
|
|
||||||
|
// update spaces
|
||||||
|
UpdateLineText(LogY);
|
||||||
|
end;
|
||||||
|
|
||||||
|
Function TSynEditStringTrimmingList.EditDelete(LogX, LogY, ByteLen
|
||||||
|
: Integer): String;
|
||||||
|
var
|
||||||
|
t: String;
|
||||||
|
Len: Integer;
|
||||||
|
begin
|
||||||
|
if (not fEnabled) then begin
|
||||||
|
fSynStrings.EditDelete(LogX, LogY, ByteLen);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
Result := '';
|
||||||
|
t := fSynStrings[LogY - 1];
|
||||||
|
Len := length(t);
|
||||||
|
|
||||||
|
// Delete uncommited spaces
|
||||||
|
if LogX + ByteLen > Len + 1 then begin
|
||||||
|
if LogX > Len + 1 then
|
||||||
|
ByteLen := ByteLen - (LogX - (Len + 1));
|
||||||
|
Result := EditDeleteTrim(max(LogX - Len, 1), LogY, LogX - 1 + ByteLen - Len);
|
||||||
|
ByteLen := Len + 1 - LogX;
|
||||||
|
end;
|
||||||
|
|
||||||
|
if ByteLen > 0 then
|
||||||
|
Result := inherited EditDelete(LogX, LogY, ByteLen) + Result
|
||||||
|
else
|
||||||
|
SendNotification(senrLineChange, self, LogY - 1, 1);
|
||||||
|
UpdateLineText(LogY);
|
||||||
|
|
||||||
|
// Trim any existing (commited/real) spaces
|
||||||
|
t := fSynStrings[LogY - 1];
|
||||||
|
EditMoveToTrim(LogY, length(t) - LastNoneSpacePos(t));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditLineBreak(LogX, LogY: Integer);
|
||||||
|
var
|
||||||
|
s, t: string;
|
||||||
|
begin
|
||||||
|
if (not fEnabled) then begin
|
||||||
|
fSynStrings.EditLineBreak(LogX, LogY);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
s := Spaces(LogY - 1);
|
||||||
|
t := fSynStrings[LogY - 1];
|
||||||
|
if LogX > length(t) then begin
|
||||||
|
fSynStrings.EditLineBreak(1 + length(t), LogY);
|
||||||
|
if s <> '' then
|
||||||
|
s := EditDeleteTrim(LogX - length(t), LogY, length(s) - (LogX - 1 - length(t)));
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
s := EditDeleteTrim(1, LogY, length(s));
|
||||||
|
fSynStrings.EditLineBreak(LogX, LogY);
|
||||||
|
end;
|
||||||
|
DoLinesChanged(LogY, 1);
|
||||||
|
UpdateLineText(LogY + 1);
|
||||||
|
EditInsertTrim(1, LogY + 1, s);
|
||||||
|
// Trim any existing (commited/real) spaces
|
||||||
|
s := fSynStrings[LogY - 1];
|
||||||
|
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
|
||||||
|
s := fSynStrings[LogY];
|
||||||
|
EditMoveToTrim(LogY + 1, length(s) - LastNoneSpacePos(s));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditLineJoin(LogY: Integer;
|
||||||
|
FillText: String = '');
|
||||||
|
var
|
||||||
|
s: String;
|
||||||
|
begin
|
||||||
|
if (not fEnabled) then begin
|
||||||
|
fSynStrings.EditLineJoin(LogY, FillText);
|
||||||
|
exit;
|
||||||
|
end;
|
||||||
|
|
||||||
|
EditMoveFromTrim(LogY, length(Spaces(LogY - 1)));
|
||||||
|
|
||||||
|
s := EditDeleteTrim(1, LogY + 1, length(Spaces(LogY))); // next line
|
||||||
|
//Todo: if FillText isSpacesOnly AND NextLineIsSpacesOnly => add direct to trailing
|
||||||
|
fSynStrings.EditLineJoin(LogY, FillText);
|
||||||
|
DoLinesChanged(LogY - 1, -1);
|
||||||
|
UpdateLineText(LogY);
|
||||||
|
EditInsertTrim(1, LogY, s);
|
||||||
|
|
||||||
|
// Trim any existing (commited/real) spaces
|
||||||
|
s := fSynStrings[LogY - 1];
|
||||||
|
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditLinesInsert(LogY, ACount: Integer;
|
||||||
|
AText: String = '');
|
||||||
|
var
|
||||||
|
s: string;
|
||||||
|
begin
|
||||||
|
fSynStrings.EditLinesInsert(LogY, ACount, AText);
|
||||||
|
s := fSynStrings[LogY - 1];
|
||||||
|
EditMoveToTrim(LogY, length(s) - LastNoneSpacePos(s));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditLinesDelete(LogY, ACount: Integer);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
begin
|
||||||
|
for i := LogY to LogY + ACount - 1 do
|
||||||
|
EditMoveFromTrim(i, length(Spaces(i - 1)));
|
||||||
|
fSynStrings.EditLinesDelete(LogY, ACount);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditUndo(Item: TSynEditUndoItem);
|
||||||
|
begin
|
||||||
|
IsUndoing := True;
|
||||||
|
try
|
||||||
|
EditRedo(Item);
|
||||||
|
finally
|
||||||
|
IsUndoing := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TSynEditStringTrimmingList.EditRedo(Item: TSynEditUndoItem);
|
||||||
|
begin
|
||||||
|
if not Item.PerformUndo(self) then
|
||||||
|
inherited EditRedo(Item);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ type
|
|||||||
// to be binary (clipboard) compatible with other (Delphi compiled) synedits
|
// to be binary (clipboard) compatible with other (Delphi compiled) synedits
|
||||||
// use {$PACKENUM 1}
|
// use {$PACKENUM 1}
|
||||||
{$IFDEF SYN_LAZARUS}{$PACKENUM 1}{$ENDIF SYN_LAZARUS}
|
{$IFDEF SYN_LAZARUS}{$PACKENUM 1}{$ENDIF SYN_LAZARUS}
|
||||||
TSynSelectionMode = (smNormal, smLine, smColumn);
|
TSynSelectionMode = (smNormal, smLine, smColumn, smCurrent);
|
||||||
{$IFDEF SYN_LAZARUS}{$PACKENUM 4}{$ENDIF SYN_LAZARUS}
|
{$IFDEF SYN_LAZARUS}{$PACKENUM 4}{$ENDIF SYN_LAZARUS}
|
||||||
|
|
||||||
TSynSearchOption = (ssoMatchCase, ssoWholeWord, ssoBackwards,
|
TSynSearchOption = (ssoMatchCase, ssoWholeWord, ssoBackwards,
|
||||||
|
Loading…
Reference in New Issue
Block a user