lazarus/components/synedit/synedittexttrimmer.pas
martin 6a87b62df4 SynEdit: Clean up, dead code and duplicates
git-svn-id: trunk@18597 -
2009-02-07 21:07:30 +00:00

455 lines
14 KiB
ObjectPascal

{-------------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Alternatively, the contents of this file may be used under the terms of the
GNU General Public License Version 2 or later (the "GPL"), in which case
the provisions of the GPL are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the GPL and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the GPL.
If you do not delete the provisions above, a recipient may use your version
of this file under either the MPL or the GPL.
-------------------------------------------------------------------------------}
unit SynEditTextTrimmer;
{$I synedit.inc}
interface
uses
LCLProc,
Classes, SysUtils, SynEditTypes, SynEditTextBase, SynEditTextBuffer,
SynEditPointClasses;
type
TSynEditStringTrimmingType = (settLeaveLine, settEditLine, settMoveCaret);
{ TSynEditStringTrimmingList }
TSynEditStringTrimmingList = class(TSynEditStringsLinked)
private
fCaret: TSynEditCaret;
FIsTrimming: Boolean;
FTrimType: TSynEditStringTrimmingType;
fUndoList: TSynEditUndoList;
fSpaces: String;
fLineText: String;
fLineIndex: Integer;
fEnabled: Boolean;
FUndoTrimmedSpaces: Boolean;
fLockCount: Integer;
fLockList : TStringList;
FLineEdited: Boolean;
procedure DoCaretChanged(Sender : TObject);
procedure SetEnabled(const AValue : Boolean);
procedure SetTrimType(const AValue: TSynEditStringTrimmingType);
function TrimLine(const S : String; Index: Integer; RealUndo: Boolean = False) : String;
function Spaces(Index: Integer) : String;
procedure DoLinesChanged(Index, N: integer);
procedure TrimAfterLock;
protected
function GetExpandedString(Index: integer): string; override;
function GetLengthOfLongestLine: integer; override;
function Get(Index: integer): string; override;
function GetObject(Index: integer): TObject; override;
procedure Put(Index: integer; const S: string); override;
procedure PutObject(Index: integer; AObject: TObject); override;
public
constructor Create(ASynStringSource: TSynEditStrings; ACaret: TSynEditCaret);
destructor Destroy; override;
function Add(const S: string): integer; override;
procedure AddStrings(AStrings: TStrings); override;
procedure Clear; override;
procedure Delete(Index: integer); override;
procedure DeleteLines(Index, NumLines: integer); override;
procedure Insert(Index: integer; const S: string); override;
procedure InsertLines(Index, NumLines: integer); override;
procedure InsertStrings(Index: integer; NewStrings: TStrings); override;
procedure Exchange(Index1, Index2: integer); override;
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
public
procedure Lock;
procedure UnLock;
procedure ForceTrim; // for redo; redo can not wait for UnLock
procedure UndoRealSpaces(Item: TSynEditUndoItem);
property Enabled : Boolean read fEnabled write SetEnabled;
property UndoTrimmedSpaces: Boolean read FUndoTrimmedSpaces write FUndoTrimmedSpaces;
property UndoList: TSynEditUndoList read fUndoList write fUndoList;
property IsTrimming: Boolean read FIsTrimming;
property TrimType: TSynEditStringTrimmingType read FTrimType write SetTrimType;
end;
implementation
{ TSynEditStringTrimmingList }
constructor TSynEditStringTrimmingList.Create(ASynStringSource : TSynEditStrings; ACaret: TSynEditCaret);
begin
fCaret := ACaret;
fCaret.AddChangeHandler(@DoCaretChanged);
fLockList := TStringList.Create;
fLineIndex:= -1;
fSpaces := '';
fEnabled:=false;
FUndoTrimmedSpaces := False;
FIsTrimming := False;
FLineEdited := False;
FTrimType := settLeaveLine;
Inherited Create(ASynStringSource);
end;
destructor TSynEditStringTrimmingList.Destroy;
begin
fCaret.RemoveChangeHandler(@DoCaretChanged);
FreeAndNil(fLockList);
inherited Destroy;
end;
procedure TSynEditStringTrimmingList.DoCaretChanged(Sender : TObject);
var
s: String;
i, j: Integer;
begin
if (not fEnabled) then exit;
if (fLockCount > 0) or (length(fSpaces) = 0) or (fLineIndex < 0)
or (fLineIndex >= fSynStrings.Count)
or ((FTrimType in [settLeaveLine]) AND (fLineIndex = TSynEditCaret(Sender).LinePos - 1))
or ((FTrimType in [settEditLine]) and not FLineEdited)
then begin
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then
fSpaces := '';
fLineIndex := TSynEditCaret(Sender).LinePos - 1;
exit;
end;
FIsTrimming := True;
s := fSynStrings[fLineIndex];
fSynStrings[fLineIndex] := s; // trigger OnPutted, so the line gets repainted
if (fLineIndex <> TSynEditCaret(Sender).LinePos - 1) then begin
fUndoList.AppendToLastChange(crTrimSpace, Point(1+length(s), fLineIndex+1),
Point(1+length(s)+length(fSpaces), fLineIndex+1), fSpaces, smNormal);
fSpaces := '';
end else begin
// same line, only right of caret
i := TSynEditCaret(Sender).BytePos;
if i <= length(s) + 1 then
j := 0
else
j := i - length(s) - 1;
s := copy(FSpaces, j + 1, MaxInt);
FSpaces := copy(FSpaces, 1, j);
fUndoList.AppendToLastChange(crTrimSpace, Point(i, fLineIndex+1),
Point(i + length(s), fLineIndex+1), s, smNormal);
end;
FIsTrimming := False;
FLineEdited := False;
fLineIndex := TSynEditCaret(Sender).LinePos - 1;
end;
procedure TSynEditStringTrimmingList.SetEnabled(const AValue : Boolean);
begin
if fEnabled = AValue then exit;
fEnabled:=AValue;
fLockList.Clear;
fLockCount:=0;
FSpaces := '';
FLineIndex := -1;
FLockList.Clear;
FIsTrimming := True;
FLineEdited := False;
if fEnabled and (fLineIndex >= 0) and (fLineIndex < fSynStrings.Count) then
fSynStrings[fLineIndex] := TrimLine(fSynStrings[fLineIndex], fLineIndex);
FIsTrimming := False;
end;
procedure TSynEditStringTrimmingList.SetTrimType(const AValue: TSynEditStringTrimmingType);
begin
if FTrimType = AValue then exit;
FTrimType := AValue;
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;
RealUndo: Boolean = False): String;
var
l, i:integer;
temp: String;
begin
if (not fEnabled) then exit(s);
if RealUndo then begin
temp := fSynStrings.Strings[Index];
l := length(temp);
i:= l;
while (i>0) and (temp[i] in [#9, ' ']) do dec(i);
// Add RealSpaceUndo
if i < l then
fUndoList.AddChange(crTrimRealSpace, Point(i+1, Index+1),
Point(l, Index+1), copy(temp, i+1, l-i), smNormal);
end;
l := length(s);
i := l;
while (i>0) and (s[i] in [#9, ' ']) do dec(i);
temp := copy(s, i+1, l-i);
if i=l then
result := s // No need to make a copy
else
result := copy(s, 1, i);
if fLockCount > 0 then begin
i := fLockList.IndexOfObject(TObject(pointer(Index)));
if i < 0 then
fLockList.AddObject(temp, TObject(pointer(Index)))
else
fLockList[i] := temp;
end
else if (fLineIndex = Index) then begin
fSpaces := temp;
fLineText:=result;
end;
end ;
function TSynEditStringTrimmingList.Spaces(Index : Integer) : String;
var
i : Integer;
begin
if (not fEnabled) then exit('');
if fLockCount > 0 then begin
i := fLockList.IndexOfObject(TObject(Pointer(Index)));
if i < 0 then
result := ''
else
result := fLockList[i];
exit;
end;
if Index <> fLineIndex then exit('');
if (fLineIndex < 0) or (fLineIndex >= fSynStrings.Count)
or (fLineText <> fSynStrings[fLineIndex]) then begin
fSpaces:='';
fLineText:='';
end;
Result:= fSpaces;
end;
procedure TSynEditStringTrimmingList.DoLinesChanged(Index, N : integer);
var
i, j: Integer;
begin
if (not fEnabled) then exit;
if fLockCount > 0 then begin
for i := fLockList.Count-1 downto 0 do begin
j := Integer(Pointer(fLockList.Objects[i]));
if (j >= Index) and (j < Index - N) then
fLockList.Delete(i)
else if j > Index then
fLockList.Objects[i] := TObject(Pointer(j + N));
end;
end else begin
if (fLineIndex >= Index) and (fLineIndex < Index - N) then
fLineIndex:=-1
else if fLineIndex > Index then
inc(fLineIndex, N);
end;
end;
procedure TSynEditStringTrimmingList.Lock;
begin
if (fLockCount = 0) and (fLineIndex >= 0) and Enabled then begin
fLockList.AddObject(Spaces(fLineIndex), TObject(Pointer(fLineIndex)));
FLineEdited := False;
end;
inc(fLockCount);
end;
procedure TSynEditStringTrimmingList.UnLock;
begin
dec(fLockCount);
if (fLockCount = 0) then TrimAfterLock;
end;
procedure TSynEditStringTrimmingList.TrimAfterLock;
var
i, index, llen, slen: Integer;
ltext: String;
begin
if (not fEnabled) then exit;
FIsTrimming := True;
i := fLockList.IndexOfObject(TObject(Pointer(fLineIndex)));
if i >= 0 then begin
fSpaces:= fLockList[i];
if (fLineIndex >= 0) and (fLineIndex < fSynStrings.Count) then
fLineText := fSynStrings[fLineIndex];
fLockList.Delete(i);
DoCaretChanged(fCaret);
end;
FIsTrimming := True;
for i := 0 to fLockList.Count-1 do begin
index := Integer(Pointer(fLockList.Objects[i]));
slen := length(fLockList[i]);
if (slen > 0) and (index >= 0) and (index < fSynStrings.Count) then begin
ltext := fSynStrings[index];
llen := length(ltext);
fSynStrings[index] := ltext; // trigger OnPutted, so the line gets repainted
fUndoList.AppendToLastChange(crTrimSpace, Point(1+llen, index+1),
Point(1+llen+slen, index+1), fLockList[i], smNormal);
end;
end;
FIsTrimming := False;
FLineEdited := False;
fLockList.Clear;
end;
procedure TSynEditStringTrimmingList.ForceTrim;
begin
TrimAfterLock;
end;
// Lines
function TSynEditStringTrimmingList.GetExpandedString(Index : integer) : string;
begin
Result:= fSynStrings.ExpandedStrings[Index] + Spaces(Index);
end;
function TSynEditStringTrimmingList.GetLengthOfLongestLine : integer;
var
i: Integer;
begin
Result:= fSynStrings.LengthOfLongestLine;
if (fLineIndex >= 0) and (fLineIndex < Count) then begin
i:= length(ExpandedStrings[fLineIndex]);
if (i > Result) then Result := i;
end;
end;
function TSynEditStringTrimmingList.Get(Index : integer) : string;
begin
Result:= fSynStrings.Strings[Index] + Spaces(Index);
end;
function TSynEditStringTrimmingList.GetObject(Index : integer) : TObject;
begin
Result:= fSynStrings.Objects[Index];
end;
procedure TSynEditStringTrimmingList.Put(Index : integer; const S : string);
begin
FLineEdited := True;
fSynStrings.Strings[Index]:= TrimLine(S, Index, True);
end;
procedure TSynEditStringTrimmingList.PutObject(Index : integer; AObject : TObject);
begin
FLineEdited := True;
fSynStrings.Objects[Index]:= AObject;
end;
function TSynEditStringTrimmingList.Add(const S : string) : integer;
var
c : Integer;
begin
FLineEdited := True;
c := fSynStrings.Count;
DoLinesChanged(c, 1);
Result := fSynStrings.Add(TrimLine(S, c));
end;
procedure TSynEditStringTrimmingList.AddStrings(AStrings : TStrings);
var
i, c : Integer;
begin
c := fSynStrings.Count;
DoLinesChanged(c, AStrings.Count);
for i := 0 to AStrings.Count-1 do
AStrings[i] := TrimLine(AStrings[i], c + i);
fSynStrings.AddStrings(AStrings);
end;
procedure TSynEditStringTrimmingList.Clear;
begin
fSynStrings.Clear;
fLineIndex:=-1;
end;
procedure TSynEditStringTrimmingList.Delete(Index : integer);
begin
FLineEdited := True;
TrimLine('', Index, True);
fSynStrings.Delete(Index);
DoLinesChanged(Index, -1);
end;
procedure TSynEditStringTrimmingList.DeleteLines(Index, NumLines : integer);
var
i: Integer;
begin
FLineEdited := True;
for i := 0 to NumLines-1 do
TrimLine('', Index+i, True);
fSynStrings.DeleteLines(Index, NumLines);
DoLinesChanged(Index, -NumLines);
end;
procedure TSynEditStringTrimmingList.Insert(Index : integer; const S : string);
begin
FLineEdited := True;
DoLinesChanged(Index, 1);
fSynStrings.Insert(Index, TrimLine(S, Index));
end;
procedure TSynEditStringTrimmingList.InsertLines(Index, NumLines : integer);
begin
FLineEdited := True;
DoLinesChanged(Index, NumLines);
fSynStrings.InsertLines(Index, NumLines);
end;
procedure TSynEditStringTrimmingList.InsertStrings(Index : integer; NewStrings : TStrings);
var
i : Integer;
begin
FLineEdited := True;
DoLinesChanged(Index, NewStrings.Count);
for i := 0 to NewStrings.Count-1 do
NewStrings[i] := TrimLine(NewStrings[i], Index+i, True);
fSynStrings.InsertStrings(Index, NewStrings);
end;
procedure TSynEditStringTrimmingList.Exchange(Index1, Index2 : integer);
begin
FLineEdited := True;
fSynStrings.Exchange(Index1, Index2);
if fLineIndex = Index1 then
fLineIndex := Index2
else if fLineIndex = Index2 then
fLineIndex := Index1;
end;
end.