mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-06-03 00:58:17 +02:00
SynEdit Refactor: Move Selection to it's own class. (Also fixes insert into *empty* buffer, which did skip the 2nd last line / Fixes undo/paste of line-mode selection)
git-svn-id: trunk@17713 -
This commit is contained in:
parent
ebe1b9d29a
commit
6c76be7c59
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -1406,6 +1406,7 @@ components/synedit/syneditmarkupspecialline.pp svneol=native#text/plain
|
||||
components/synedit/syneditmiscclasses.pp svneol=native#text/pascal
|
||||
components/synedit/syneditmiscprocs.pp svneol=native#text/pascal
|
||||
components/synedit/syneditplugins.pas svneol=native#text/pascal
|
||||
components/synedit/syneditpointclasses.pas svneol=native#text/plain
|
||||
components/synedit/syneditregexsearch.pas svneol=native#text/pascal
|
||||
components/synedit/syneditsearch.pp svneol=native#text/pascal
|
||||
components/synedit/syneditstrconst.pp svneol=native#text/pascal
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,7 +30,8 @@ interface
|
||||
|
||||
uses
|
||||
LCLProc,
|
||||
Classes, SysUtils, SynEditTypes, SynEditTextBuffer, SynEditTextBase, SynEditMiscClasses;
|
||||
Classes, SysUtils, SynEditTypes, SynEditTextBuffer, SynEditTextBase,
|
||||
SynEditMiscClasses, SynEditPointClasses;
|
||||
|
||||
type
|
||||
|
||||
|
@ -191,27 +191,6 @@ type
|
||||
property Options: TSynSearchOptions write SetOptions;
|
||||
end;
|
||||
|
||||
{ TSynEditCaret }
|
||||
|
||||
TSynEditCaret = class
|
||||
fLinePos : Integer; // 1 based
|
||||
fCharPos : Integer; // 1 based
|
||||
fOnChangeList : TMethodList;
|
||||
private
|
||||
function GetLineCharPos : TPoint;
|
||||
procedure SetLineCharPos(const AValue : TPoint);
|
||||
procedure setCharPos(const AValue : Integer);
|
||||
procedure setLinePos(const AValue : Integer);
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure AddChangeHandler(AHandler: TNotifyEvent);
|
||||
procedure RemoveChangeHandler(AHandler: TNotifyEvent);
|
||||
property LinePos : Integer read fLinePos write setLinePos;
|
||||
property CharPos : Integer read fCharPos write setCharPos;
|
||||
property LineCharPos : TPoint read GetLineCharPos write SetLineCharPos;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@ -552,57 +531,5 @@ begin
|
||||
end;
|
||||
{$ENDIF}
|
||||
|
||||
{ TSynEditCaret }
|
||||
|
||||
function TSynEditCaret.GetLineCharPos : TPoint;
|
||||
begin
|
||||
Result := Point(fCharPos, fLinePos);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.SetLineCharPos(const AValue : TPoint);
|
||||
begin
|
||||
if (fCharPos = AValue.X) and (fLinePos = AValue.Y) then exit;
|
||||
fCharPos:= AValue.X;
|
||||
fLinePos:= AValue.Y;
|
||||
fOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.setCharPos(const AValue : Integer);
|
||||
begin
|
||||
if fCharPos = AValue then exit;
|
||||
fCharPos:= AValue;
|
||||
fOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.setLinePos(const AValue : Integer);
|
||||
begin
|
||||
if fLinePos = AValue then exit;
|
||||
fLinePos:= AValue;
|
||||
fOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
|
||||
constructor TSynEditCaret.Create;
|
||||
begin
|
||||
fOnChangeList := TMethodList.Create;
|
||||
fLinePos:= 1;
|
||||
fCharPos:= 1;
|
||||
end;
|
||||
|
||||
destructor TSynEditCaret.Destroy;
|
||||
begin
|
||||
FreeAndNil(fOnChangeList);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.AddChangeHandler(AHandler : TNotifyEvent);
|
||||
begin
|
||||
fOnChangeList.Add(TMethod(AHandler));
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.RemoveChangeHandler(AHandler : TNotifyEvent);
|
||||
begin
|
||||
fOnChangeList.Remove(TMethod(AHandler));
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -131,6 +131,9 @@ function DecodeString(s: string): string;
|
||||
function fsNot (s : TFontStyles) : TFontStyles; inline;
|
||||
function fsXor (s1,s2 : TFontStyles) : TFontStyles; inline;
|
||||
|
||||
function CreateTabsAndSpaces(StartPos, SpaceLen, TabWidth: integer;
|
||||
UseTabs: boolean): string;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
@ -763,5 +766,33 @@ end; { DecodeString }
|
||||
{$IFDEF RestoreRangeChecking}{$R+}{$ENDIF}
|
||||
{end} //gp 2000-06-24
|
||||
|
||||
function CreateTabsAndSpaces(StartPos, SpaceLen, TabWidth: integer;
|
||||
UseTabs: boolean): string;
|
||||
var
|
||||
TabCount: Integer;
|
||||
EndPos: Integer;
|
||||
PosPlusOneTab: Integer;
|
||||
begin
|
||||
Result:='';
|
||||
if not UseTabs then begin
|
||||
Result:=StringOfChar(' ',SpaceLen);
|
||||
exit;
|
||||
end;
|
||||
TabCount:=0;
|
||||
EndPos:=StartPos+SpaceLen;
|
||||
while StartPos<EndPos do begin
|
||||
PosPlusOneTab:=StartPos+TabWidth-((StartPos-1) mod TabWidth);
|
||||
if PosPlusOneTab<=EndPos then begin
|
||||
inc(TabCount);
|
||||
StartPos:=PosPlusOneTab;
|
||||
end else begin
|
||||
Result:=StringOfChar(' ',EndPos-StartPos);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
if TabCount>0 then
|
||||
Result:=StringOfChar(#9,TabCount)+Result;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
897
components/synedit/syneditpointclasses.pas
Normal file
897
components/synedit/syneditpointclasses.pas
Normal file
@ -0,0 +1,897 @@
|
||||
{-------------------------------------------------------------------------------
|
||||
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 SynEditPointClasses;
|
||||
|
||||
{$I synedit.inc}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, LCLProc,
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
Imm,
|
||||
{$ENDIF}
|
||||
SynEditTextBase, SynEditTypes, SynEditMiscProcs, SynEditTextBuffer;
|
||||
|
||||
type
|
||||
|
||||
TInvalidateLines = procedure(FirstLine, LastLine: integer) of Object;
|
||||
TLinesCountChanged = procedure (FirstLine, Count: integer) of Object;
|
||||
|
||||
{ TSynEditPointBase }
|
||||
|
||||
TSynEditPointBase = class
|
||||
protected
|
||||
FLines: TSynEditStrings;
|
||||
FOnChangeList: TMethodList;
|
||||
public
|
||||
constructor Create;
|
||||
constructor Create(Lines: TSynEditStrings);
|
||||
destructor Destroy; override;
|
||||
procedure AddChangeHandler(AHandler: TNotifyEvent);
|
||||
procedure RemoveChangeHandler(AHandler: TNotifyEvent);
|
||||
property Lines: TSynEditStrings read FLines write FLines;
|
||||
end;
|
||||
|
||||
TSynEditCaret = class;
|
||||
|
||||
{ TSynEditSelection }
|
||||
|
||||
TSynEditSelection = class(TSynEditPointBase)
|
||||
FCaret: TSynEditCaret;
|
||||
fUndoList: TSynEditUndoList;
|
||||
FInvalidateLinesMethod: TInvalidateLines;
|
||||
FLinesDeletedMethod: TLinesCountChanged;
|
||||
FLinesInsertedMethod: TLinesCountChanged;
|
||||
FEnabled: Boolean;
|
||||
FSpacesToTabs: Boolean;
|
||||
FSelectionMode: TSynSelectionMode;
|
||||
FStartLinePos: Integer; // 1 based
|
||||
FStartBytePos: Integer; // 1 based
|
||||
FEndLinePos: Integer; // 1 based
|
||||
FEndBytePos: Integer; // 1 based
|
||||
private
|
||||
function AdjustBytePosToCharacterStart(Line: integer; BytePos: integer): integer;
|
||||
function GetFirstLineBytePos: TPoint;
|
||||
function GetLastLineBytePos: TPoint;
|
||||
procedure SetEnabled(const Value : Boolean);
|
||||
procedure SetSelectionMode(const Value: TSynSelectionMode);
|
||||
function GetStartLineBytePos: TPoint;
|
||||
procedure SetStartLineBytePos(Value: TPoint);
|
||||
function GetEndLineBytePos: TPoint;
|
||||
procedure SetEndLineBytePos(Value: TPoint);
|
||||
function GetSelText: string;
|
||||
procedure SetSelText(const Value: string);
|
||||
public
|
||||
FMaxLeftChar: Integer;
|
||||
constructor Create(ALines: TSynEditStrings);
|
||||
//destructor Destroy; override;
|
||||
procedure SetSelTextPrimitive(PasteMode: TSynSelectionMode; Value: PChar;
|
||||
ATag: PInteger);
|
||||
function SelAvail: Boolean;
|
||||
function IsBackwardSel: Boolean; // SelStart < SelEnd ?
|
||||
property Enabled: Boolean read FEnabled write SetEnabled;
|
||||
property SpacesToTabs: Boolean read FSpacesToTabs write FSpacesToTabs;
|
||||
property SelectionMode: TSynSelectionMode
|
||||
read FSelectionMode write SetSelectionMode;
|
||||
property SelText: String read GetSelText write SetSelText;
|
||||
property StartLineBytePos: TPoint
|
||||
read GetStartLineBytePos write SetStartLineBytePos;
|
||||
property EndLineBytePos: TPoint
|
||||
read GetEndLineBytePos write SetEndLineBytePos;
|
||||
property StartLinePos: Integer read FStartLinePos;
|
||||
property EndLinePos: Integer read FEndLinePos;
|
||||
property StartBytePos: Integer read FStartBytePos;
|
||||
property EndBytePos: Integer read FEndBytePos;
|
||||
// Bounds ordered
|
||||
property FirstLineBytePos: TPoint read GetFirstLineBytePos;
|
||||
property LastLineBytePos: TPoint read GetLastLineBytePos;
|
||||
property InvalidateLinesMethod : TInvalidateLines write FInvalidateLinesMethod;
|
||||
property LinesDeletedMethod: TLinesCountChanged write FLinesDeletedMethod;
|
||||
property LinesInsertedMethod: TLinesCountChanged write FLinesInsertedMethod;
|
||||
property Caret: TSynEditCaret read FCaret write FCaret;
|
||||
property UndoList: TSynEditUndoList read fUndoList write fUndoList;
|
||||
end;
|
||||
|
||||
{ TSynEditCaret }
|
||||
|
||||
TSynEditCaret = class(TSynEditPointBase)
|
||||
fLinePos: Integer; // 1 based
|
||||
fCharPos: Integer; // 1 based
|
||||
private
|
||||
function GetLineCharPos: TPoint;
|
||||
function GetLineText : string;
|
||||
procedure SetLineCharPos(const AValue: TPoint);
|
||||
procedure setCharPos(const AValue: Integer);
|
||||
procedure setLinePos(const AValue: Integer);
|
||||
procedure SetLineText(const AValue : string);
|
||||
public
|
||||
constructor Create;
|
||||
property LinePos : Integer read fLinePos write setLinePos;
|
||||
property CharPos : Integer read fCharPos write setCharPos;
|
||||
property LineCharPos : TPoint read GetLineCharPos write SetLineCharPos;
|
||||
property LineText: string read GetLineText write SetLineText;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TSynEditPointBase }
|
||||
|
||||
constructor TSynEditPointBase.Create;
|
||||
begin
|
||||
FOnChangeList := TMethodList.Create;
|
||||
end;
|
||||
|
||||
constructor TSynEditPointBase.Create(Lines : TSynEditStrings);
|
||||
begin
|
||||
Create;
|
||||
FLines := Lines;
|
||||
end;
|
||||
|
||||
destructor TSynEditPointBase.Destroy;
|
||||
begin
|
||||
FreeAndNil(FOnChangeList);
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TSynEditPointBase.AddChangeHandler(AHandler : TNotifyEvent);
|
||||
begin
|
||||
FOnChangeList.Add(TMethod(AHandler));
|
||||
end;
|
||||
|
||||
procedure TSynEditPointBase.RemoveChangeHandler(AHandler : TNotifyEvent);
|
||||
begin
|
||||
FOnChangeList.Remove(TMethod(AHandler));
|
||||
end;
|
||||
|
||||
{ TSynEditCaret }
|
||||
|
||||
function TSynEditCaret.GetLineCharPos : TPoint;
|
||||
begin
|
||||
Result := Point(fCharPos, fLinePos);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.SetLineCharPos(const AValue : TPoint);
|
||||
begin
|
||||
if (fCharPos = AValue.X) and (fLinePos = AValue.Y) then exit;
|
||||
fCharPos:= AValue.X;
|
||||
fLinePos:= AValue.Y;
|
||||
fOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.setCharPos(const AValue : Integer);
|
||||
begin
|
||||
if fCharPos = AValue then exit;
|
||||
fCharPos:= AValue;
|
||||
fOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.setLinePos(const AValue : Integer);
|
||||
begin
|
||||
if fLinePos = AValue then exit;
|
||||
fLinePos:= AValue;
|
||||
fOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
|
||||
function TSynEditCaret.GetLineText : string;
|
||||
begin
|
||||
if (LinePos >= 1) and (LinePos <= FLines.Count) then
|
||||
Result := FLines[LinePos - 1]
|
||||
else
|
||||
Result := '';
|
||||
end;
|
||||
|
||||
procedure TSynEditCaret.SetLineText(const AValue : string);
|
||||
begin
|
||||
if (LinePos >= 1) and (LinePos <= Max(1, FLines.Count)) then
|
||||
FLines[LinePos - 1] := AValue;
|
||||
end;
|
||||
|
||||
constructor TSynEditCaret.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
fLinePos:= 1;
|
||||
fCharPos:= 1;
|
||||
end;
|
||||
|
||||
{ TSynEditSelection }
|
||||
|
||||
constructor TSynEditSelection.Create(ALines : TSynEditStrings);
|
||||
begin
|
||||
Inherited Create(ALines);
|
||||
fMaxLeftChar := 1024;
|
||||
FSelectionMode := smNormal;
|
||||
FStartLinePos := 1;
|
||||
FStartBytePos := 1;
|
||||
FEndLinePos := 1;
|
||||
FEndBytePos := 1;
|
||||
FEnabled := True;
|
||||
end;
|
||||
|
||||
function TSynEditSelection.GetSelText : string;
|
||||
|
||||
function CopyPadded(const S: string; Index, Count: integer): string;
|
||||
var
|
||||
SrcLen: Integer;
|
||||
DstLen: integer;
|
||||
P: PChar;
|
||||
begin
|
||||
SrcLen := Length(S);
|
||||
DstLen := Index + Count;
|
||||
if SrcLen >= DstLen then
|
||||
Result := Copy(S, Index, Count)
|
||||
else begin
|
||||
SetLength(Result, DstLen);
|
||||
P := PChar(Pointer(Result));
|
||||
StrPCopy(P, Copy(S, Index, Count));
|
||||
Inc(P, SrcLen);
|
||||
FillChar(P^, DstLen - Srclen, $20);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CopyAndForward(const S: string; Index, Count: Integer; var P:
|
||||
PChar);
|
||||
var
|
||||
pSrc: PChar;
|
||||
SrcLen: Integer;
|
||||
DstLen: Integer;
|
||||
begin
|
||||
SrcLen := Length(S);
|
||||
if (Index <= SrcLen) and (Count > 0) then begin
|
||||
Dec(Index);
|
||||
pSrc := PChar(Pointer(S)) + Index;
|
||||
DstLen := Min(SrcLen - Index, Count);
|
||||
Move(pSrc^, P^, DstLen);
|
||||
Inc(P, DstLen);
|
||||
P^ := #0;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CopyPaddedAndForward(const S: string; Index, Count: Integer;
|
||||
var P: PChar);
|
||||
var
|
||||
OldP: PChar;
|
||||
Len: Integer;
|
||||
begin
|
||||
OldP := P;
|
||||
CopyAndForward(S, Index, Count, P);
|
||||
Len := Count - (P - OldP);
|
||||
FillChar(P^, Len, #$20);
|
||||
Inc(P, Len);
|
||||
end;
|
||||
|
||||
|
||||
const
|
||||
sLineBreak = {$IFDEF SYN_LAZARUS}LineEnding{$ELSE}#$0D#$0A{$ENDIF};
|
||||
var
|
||||
First, Last, TotalLen: Integer;
|
||||
ColFrom, ColTo: Integer;
|
||||
I: Integer;
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
l, r: Integer;
|
||||
s: string;
|
||||
{$ELSE}
|
||||
ColLen: integer;
|
||||
{$ENDIF}
|
||||
P: PChar;
|
||||
begin
|
||||
if not SelAvail then
|
||||
Result := ''
|
||||
else begin
|
||||
if IsBackwardSel then begin
|
||||
ColFrom := FEndBytePos;
|
||||
First := FEndLinePos - 1;
|
||||
ColTo := FStartBytePos;
|
||||
Last := FStartLinePos - 1;
|
||||
end else begin
|
||||
ColFrom := FStartBytePos;
|
||||
First := FStartLinePos - 1;
|
||||
ColTo := FEndBytePos;
|
||||
Last := FEndLinePos - 1;
|
||||
end;
|
||||
TotalLen := 0;
|
||||
case SelectionMode of
|
||||
smNormal:
|
||||
if (First = Last) then
|
||||
Result := Copy(FLines[First], ColFrom, ColTo - ColFrom)
|
||||
else begin
|
||||
// step1: calculate total length of result string
|
||||
TotalLen := Max(0, Length(FLines[First]) - ColFrom + 1);
|
||||
for i := First + 1 to Last - 1 do
|
||||
Inc(TotalLen, Length(FLines[i]));
|
||||
Inc(TotalLen, ColTo - 1);
|
||||
Inc(TotalLen, Length(sLineBreak) * (Last - First));
|
||||
// step2: build up result string
|
||||
SetLength(Result, TotalLen);
|
||||
P := PChar(Pointer(Result));
|
||||
CopyAndForward(FLines[First], ColFrom, MaxInt, P);
|
||||
CopyAndForward(sLineBreak, 1, MaxInt, P);
|
||||
for i := First + 1 to Last - 1 do begin
|
||||
CopyAndForward(FLines[i], 1, MaxInt, P);
|
||||
CopyAndForward(sLineBreak, 1, MaxInt, P);
|
||||
end;
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
CopyPaddedAndForward(FLines[Last], 1, ColTo - 1, P);
|
||||
{$ELSE}
|
||||
CopyAndForward(FLines[Last], 1, ColTo - 1, P);
|
||||
{$ENDIF}
|
||||
end;
|
||||
smColumn:
|
||||
begin
|
||||
if ColFrom > ColTo then
|
||||
SwapInt(ColFrom, ColTo);
|
||||
// step1: calclate total length of result string
|
||||
{$IFNDEF SYN_MBCSSUPPORT}
|
||||
ColLen := ColTo - ColFrom;
|
||||
TotalLen := ColLen + (ColLen + Length(sLineBreak)) * (Last - First);
|
||||
// step2: build up result string
|
||||
SetLength(Result, TotalLen);
|
||||
P := PChar(Pointer(Result));
|
||||
for i := First to Last - 1 do begin
|
||||
CopyPaddedAndForward(FLines[i], ColFrom, ColLen, P);
|
||||
CopyAndForward(sLineBreak, 1, MaxInt, P);
|
||||
end;
|
||||
CopyPaddedAndForward(FLines[Last], ColFrom, ColLen, P);
|
||||
{$ELSE} //SYN_MBCSSUPPORT
|
||||
for i := First to Last do begin
|
||||
s := FLines[i];
|
||||
l := ColFrom;
|
||||
r := ColTo;
|
||||
MBCSGetSelRangeInLineWhenColumnSelectionMode(s, l, r);
|
||||
Inc(TotalLen, r - l);
|
||||
end;
|
||||
Inc(TotalLen, Length(sLineBreak) * (Last - First));
|
||||
// step2: build up result string
|
||||
SetLength(Result, TotalLen);
|
||||
P := PChar(Result);
|
||||
for i := First to Last - 1 do begin
|
||||
s := FLines[i];
|
||||
l := ColFrom;
|
||||
r := ColTo;
|
||||
MBCSGetSelRangeInLineWhenColumnSelectionMode(s, l, r);
|
||||
CopyPaddedAndForward(s, l, r - l, P);
|
||||
CopyAndForward(sLineBreak, 1, MaxInt, P);
|
||||
end;
|
||||
s := FLines[Last];
|
||||
l := ColFrom;
|
||||
r := ColTo;
|
||||
MBCSGetSelRangeInLineWhenColumnSelectionMode(s, l, r);
|
||||
CopyPaddedAndForward(FLines[Last], l, r - l, P);
|
||||
{$ENDIF}
|
||||
end;
|
||||
smLine:
|
||||
begin
|
||||
// If block selection includes LastLine,
|
||||
// line break code(s) of the last line will not be added.
|
||||
// step1: calclate total length of result string
|
||||
for i := First to Last do
|
||||
Inc(TotalLen, Length(FLines[i]) + Length(sLineBreak));
|
||||
if Last = FLines.Count then
|
||||
Dec(TotalLen, Length(sLineBreak));
|
||||
// step2: build up result string
|
||||
SetLength(Result, TotalLen);
|
||||
P := PChar(Pointer(Result));
|
||||
for i := First to Last - 1 do begin
|
||||
CopyAndForward(FLines[i], 1, MaxInt, P);
|
||||
CopyAndForward(sLineBreak, 1, MaxInt, P);
|
||||
end;
|
||||
CopyAndForward(FLines[Last], 1, MaxInt, P);
|
||||
if (Last + 1) < FLines.Count then
|
||||
CopyAndForward(sLineBreak, 1, MaxInt, P);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditSelection.SetSelText(const Value : string);
|
||||
var
|
||||
StartOfBlock, EndOfBlock: TPoint;
|
||||
begin
|
||||
if SelAvail then begin
|
||||
if IsBackwardSel then
|
||||
fUndoList.AddChange(crDelete, StartLineBytePos, EndLineBytePos,
|
||||
GetSelText, SelectionMode)
|
||||
else
|
||||
fUndoList.AddChange(crDeleteAfterCursor, EndLineBytePos, StartLineBytePos,
|
||||
GetSelText, SelectionMode);
|
||||
StartOfBlock := FirstLineBytePos;
|
||||
EndOfBlock := LastLineBytePos;
|
||||
end else begin
|
||||
StartOfBlock := FCaret.LineCharPos;
|
||||
EndOfBlock := FCaret.LineCharPos;
|
||||
end;
|
||||
StartLineBytePos := StartOfBlock;
|
||||
EndLineBytePos := EndOfBlock;
|
||||
SetSelTextPrimitive(smNormal, PChar(Value), nil);
|
||||
if SelectionMode = smLine then
|
||||
StartOfBlock.X := 1;
|
||||
if length(Value) > 0 then
|
||||
fUndoList.AddChange(crInsert, StartOfBlock, EndLineBytePos, '', smNormal);
|
||||
end;
|
||||
|
||||
procedure TSynEditSelection.SetSelTextPrimitive(PasteMode : TSynSelectionMode; Value : PChar; ATag : PInteger);
|
||||
var
|
||||
BB, BE: TPoint;
|
||||
TempString: string;
|
||||
|
||||
procedure DeleteSelection;
|
||||
var
|
||||
x, MarkOffset: Integer;
|
||||
UpdateMarks: boolean;
|
||||
NewCaretXY: TPoint;
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
l, r: Integer;
|
||||
{$ENDIF}
|
||||
begin
|
||||
UpdateMarks := FALSE;
|
||||
MarkOffset := 0;
|
||||
case SelectionMode of
|
||||
smNormal:
|
||||
begin
|
||||
NewCaretXY := FLines.LogicalToPhysicalPos(BB);
|
||||
if FLines.Count > 0 then begin
|
||||
// Create a string that contains everything on the first line up
|
||||
// to the selection mark, and everything on the last line after
|
||||
// the selection mark.
|
||||
TempString := Copy(FLines[BB.Y - 1], 1, BB.X - 1) +
|
||||
Copy(FLines[BE.Y - 1], BE.X, MaxInt);
|
||||
// Delete all FLines in the selection range.
|
||||
TSynEditStrings(FLines).DeleteLines(BB.Y-1, BE.Y - BB.Y);
|
||||
FLines[BB.Y - 1] := TempString;
|
||||
end;
|
||||
UpdateMarks := TRUE;
|
||||
FCaret.LineCharPos := NewCaretXY;
|
||||
end;
|
||||
smColumn:
|
||||
begin
|
||||
// swap X if needed
|
||||
if BB.X > BE.X then
|
||||
{$IFDEF SYN_COMPILER_3_UP}
|
||||
SwapInt(BB.X, BE.X);
|
||||
{$ELSE}
|
||||
begin
|
||||
x := BB.X;
|
||||
BB.X := BE.X;
|
||||
BE.X := x;
|
||||
end;
|
||||
{$ENDIF}
|
||||
NewCaretXY := FLines.LogicalToPhysicalPos(Point(BB.X, FEndLinePos));
|
||||
for x := BB.Y - 1 to BE.Y - 1 do begin
|
||||
TempString := FLines[x];
|
||||
{$IFNDEF SYN_MBCSSUPPORT}
|
||||
Delete(TempString, BB.X, BE.X - BB.X);
|
||||
{$ELSE}
|
||||
l := BB.X;
|
||||
r := BE.X;
|
||||
MBCSGetSelRangeInLineWhenColumnSelectionMode(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;
|
||||
// FLines never get deleted completely, so keep caret at end.
|
||||
FCaret.LineCharPos := NewCaretXY;
|
||||
// Column deletion never removes a line entirely, so no mark
|
||||
// updating is needed here.
|
||||
end;
|
||||
smLine:
|
||||
begin
|
||||
if BE.Y = FLines.Count then begin
|
||||
FLines[BE.Y - 1] := '';
|
||||
for x := BE.Y - 2 downto BB.Y - 1 do
|
||||
FLines.Delete(x);
|
||||
end else
|
||||
for x := BE.Y - 1 downto BB.Y - 1 do
|
||||
FLines.Delete(x);
|
||||
// smLine deletion always resets to first column.
|
||||
FCaret.LineCharPos := Point(1, BB.Y);
|
||||
UpdateMarks := TRUE;
|
||||
MarkOffset := 1;
|
||||
end;
|
||||
end;
|
||||
// Update marks
|
||||
if UpdateMarks then
|
||||
FLinesDeletedMethod(BB.Y, BE.Y - BB.Y + MarkOffset);
|
||||
end;
|
||||
|
||||
procedure InsertText;
|
||||
|
||||
function CountLines(p: PChar): integer;
|
||||
begin
|
||||
Result := 0;
|
||||
while p^ <> #0 do begin
|
||||
if p^ = #13 then
|
||||
Inc(p);
|
||||
if p^ = #10 then
|
||||
Inc(p);
|
||||
Inc(Result);
|
||||
p := GetEOL(p);
|
||||
end;
|
||||
end;
|
||||
|
||||
function InsertNormal: Integer;
|
||||
var
|
||||
sLeftSide: string;
|
||||
sRightSide: string;
|
||||
Str: string;
|
||||
Start: PChar;
|
||||
P: PChar;
|
||||
LogCaretXY: TPoint;
|
||||
PhysicalLineEndPos: LongInt;
|
||||
begin
|
||||
Result := 0;
|
||||
LogCaretXY := FLines.PhysicalToLogicalPos(FCaret.LineCharPos);
|
||||
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,
|
||||
FLines.TabWidth,
|
||||
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);
|
||||
P := GetEOL(Start);
|
||||
if P^ <> #0 then begin
|
||||
SetString(Str, Value, P - Start);
|
||||
TSynEditStrings(FLines).InsertLines(FCaret.LinePos - 1, CountLines(P));
|
||||
FLines[FCaret.LinePos - 1] := sLeftSide + Str;
|
||||
end else begin
|
||||
FLines[FCaret.LinePos - 1] := sLeftSide + Value + sRightSide;
|
||||
FCaret.CharPos := FLines.LogicalToPhysicalPos(
|
||||
Point(1 + Length(sLeftSide + Value),
|
||||
FCaret.LinePos)).X;
|
||||
end;
|
||||
// step2: insert left lines of Value
|
||||
while P^ <> #0 do begin
|
||||
if P^ = #13 then
|
||||
Inc(P);
|
||||
if P^ = #10 then
|
||||
Inc(P);
|
||||
FCaret.LinePos := FCaret.LinePos + 1;
|
||||
Start := P;
|
||||
P := GetEOL(Start);
|
||||
if P = Start then begin
|
||||
if p^ <> #0 then
|
||||
FLines[FCaret.LinePos - 1] := ''
|
||||
else
|
||||
FLines[FCaret.LinePos - 1] := sRightSide;
|
||||
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;
|
||||
if p^=#0 then
|
||||
FCaret.CharPos := FLines.LogicalToPhysicalPos(
|
||||
Point(1 + Length(FLines[FCaret.LinePos - 1]) - Length(sRightSide),
|
||||
FCaret.LinePos)).X;
|
||||
Inc(Result);
|
||||
end;
|
||||
// StatusChanged([scCaretX]);
|
||||
end;
|
||||
|
||||
function InsertColumn: Integer;
|
||||
var
|
||||
Str: string;
|
||||
Start: PChar;
|
||||
P: PChar;
|
||||
Len: Integer;
|
||||
InsertPos: Integer;
|
||||
LogicalInsertPos: Integer;
|
||||
begin
|
||||
// Insert string at current position
|
||||
InsertPos := FCaret.CharPos;
|
||||
Start := PChar(Value);
|
||||
repeat
|
||||
P := GetEOL(Start);
|
||||
if P <> Start then begin
|
||||
SetLength(Str, P - Start);
|
||||
Move(Start^, Str[1], P - Start);
|
||||
if FCaret.LinePos > FLines.Count then
|
||||
FLines.Add(StringOfChar(' ', InsertPos - 1) + Str)
|
||||
else begin
|
||||
TempString := FLines[FCaret.LinePos - 1];
|
||||
Len := Length(TempString);
|
||||
LogicalInsertPos := FLines.PhysicalToLogicalCol(TempString,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;
|
||||
if ATag <> nil then
|
||||
ATag^ := P - Start;
|
||||
if p^ in [#10,#13] then begin
|
||||
if (p[1] in [#10,#13]) and (p[1]<>p^) then
|
||||
inc(p,2)
|
||||
else
|
||||
Inc(P);
|
||||
FCaret.LinePos := FCaret.LinePos + 1;
|
||||
end;
|
||||
Start := P;
|
||||
until P^ = #0;
|
||||
FCaret.CharPos:= FCaret.CharPos + Length(Str);
|
||||
Result := 0;
|
||||
end;
|
||||
|
||||
function InsertLine: Integer;
|
||||
var
|
||||
Start: PChar;
|
||||
P: PChar;
|
||||
Str: string;
|
||||
n: Integer;
|
||||
begin
|
||||
Result := 0;
|
||||
FCaret.CharPos := 1;
|
||||
// Insert string before current line
|
||||
Start := PChar(Value);
|
||||
repeat
|
||||
P := GetEOL(Start);
|
||||
if P <> Start then begin
|
||||
SetLength(Str, P - Start);
|
||||
Move(Start^, Str[1], P - Start);
|
||||
end else
|
||||
Str := '';
|
||||
if (P^ = #0) then begin // Not a full line?
|
||||
n := FLines.Count;
|
||||
if (n >= FCaret.LinePos) then
|
||||
FLines[FCaret.LinePos - 1] := Str + FLines[FCaret.LinePos - 1]
|
||||
else
|
||||
FLines.Add(Str);
|
||||
FCaret.CharPos := 1 + Length(Str);
|
||||
end else begin
|
||||
FLines.Insert(FCaret.LinePos - 1, Str);
|
||||
FCaret.LinePos := FCaret.LinePos + 1;
|
||||
Inc(Result);
|
||||
if P^ = #13 then
|
||||
Inc(P);
|
||||
if P^ = #10 then
|
||||
Inc(P);
|
||||
Start := P;
|
||||
end;
|
||||
until P^ = #0;
|
||||
// StatusChanged([scCaretX]);
|
||||
end;
|
||||
|
||||
var
|
||||
StartLine: Integer;
|
||||
InsertedLines: Integer;
|
||||
begin
|
||||
if Value = '' then
|
||||
Exit;
|
||||
if FLines.Count = 0 then
|
||||
FLines.Add('');
|
||||
|
||||
// Using a TStringList to do this would be easier, but if we're dealing
|
||||
// with a large block of text, it would be very inefficient. Consider:
|
||||
// Assign Value parameter to TStringList.Text: that parses through it and
|
||||
// creates a copy of the string for each line it finds. That copy is passed
|
||||
// to the Add method, which in turn creates a copy. Then, when you actually
|
||||
// use an item in the list, that creates a copy to return to you. That's
|
||||
// 3 copies of every string vs. our one copy below. I'd prefer no copies,
|
||||
// but we aren't set up to work with PChars that well.
|
||||
|
||||
StartLine := FCaret.LinePos;
|
||||
case PasteMode of
|
||||
smNormal:
|
||||
InsertedLines := InsertNormal;
|
||||
smColumn:
|
||||
InsertedLines := InsertColumn;
|
||||
smLine:
|
||||
InsertedLines := InsertLine;
|
||||
else
|
||||
InsertedLines := 0;
|
||||
end;
|
||||
// We delete selected based on the current selection mode, but paste
|
||||
// what's on the clipboard according to what it was when copied.
|
||||
// Update marks
|
||||
if InsertedLines > 0 then
|
||||
FLinesInsertedMethod(StartLine, InsertedLines);
|
||||
// Force caret reset
|
||||
// CaretXY := CaretXY;
|
||||
end;
|
||||
|
||||
begin
|
||||
FLines.BeginUpdate;
|
||||
try
|
||||
// BB is lower than BE
|
||||
BB := FirstLineBytePos;
|
||||
BE := LastLineBytePos;
|
||||
if SelAvail then
|
||||
DeleteSelection;
|
||||
if (Value <> nil) and (Value[0] <> #0) then
|
||||
InsertText;
|
||||
finally
|
||||
FLines.EndUpdate; // May reset Block Begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSynEditSelection.GetStartLineBytePos : TPoint;
|
||||
begin
|
||||
Result.y := FStartLinePos;
|
||||
Result.x := FStartBytePos;
|
||||
end;
|
||||
|
||||
procedure TSynEditSelection.SetEnabled(const Value : Boolean);
|
||||
begin
|
||||
if FEnabled = Value then exit;
|
||||
FEnabled := Value;
|
||||
if not Enabled then SetStartLineBytePos(EndLineBytePos);
|
||||
end;
|
||||
|
||||
procedure TSynEditSelection.SetStartLineBytePos(Value : TPoint);
|
||||
// logical position (byte)
|
||||
var
|
||||
nInval1, nInval2: integer;
|
||||
SelChanged: boolean;
|
||||
begin
|
||||
Value.x := MinMax(Value.x, 1, fMaxLeftChar);
|
||||
Value.y := MinMax(Value.y, 1, fLines.Count);
|
||||
if (SelectionMode = smNormal) then
|
||||
if (Value.y >= 1) and (Value.y <= FLines.Count) then
|
||||
Value.x := AdjustBytePosToCharacterStart(Value.y,Value.x)
|
||||
else
|
||||
Value.x := 1;
|
||||
if SelAvail then begin
|
||||
if FStartLinePos < FEndLinePos then begin
|
||||
nInval1 := Min(Value.Y, FStartLinePos);
|
||||
nInval2 := Max(Value.Y, FEndLinePos);
|
||||
end else begin
|
||||
nInval1 := Min(Value.Y, FEndLinePos);
|
||||
nInval2 := Max(Value.Y, FStartLinePos);
|
||||
end;
|
||||
FInvalidateLinesMethod(nInval1, nInval2);
|
||||
SelChanged := TRUE;
|
||||
end else begin
|
||||
SelChanged := (FStartBytePos <> Value.X) or (FStartLinePos <> Value.Y) or
|
||||
(FEndBytePos <> Value.X) or (FEndLinePos <> Value.Y);
|
||||
end;
|
||||
FStartLinePos := Value.Y;
|
||||
FStartBytePos := Value.X;
|
||||
FEndLinePos := Value.Y;
|
||||
FEndBytePos := Value.X;
|
||||
if SelChanged then
|
||||
fOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
|
||||
function TSynEditSelection.GetEndLineBytePos : TPoint;
|
||||
begin
|
||||
Result.y := FEndLinePos;
|
||||
Result.x := FEndBytePos;
|
||||
end;
|
||||
|
||||
procedure TSynEditSelection.SetEndLineBytePos(Value : TPoint);
|
||||
var
|
||||
nLine: integer;
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
s: string;
|
||||
{$ENDIF}
|
||||
begin
|
||||
if FEnabled then begin
|
||||
Value.x := MinMax(Value.x, 1, fMaxLeftChar);
|
||||
Value.y := MinMax(Value.y, 1, fLines.Count);
|
||||
if (SelectionMode = smNormal) then
|
||||
if (Value.y >= 1) and (Value.y <= fLines.Count) then
|
||||
Value.x := AdjustBytePosToCharacterStart(Value.y,Value.x)
|
||||
else
|
||||
Value.x := 1;
|
||||
if (Value.X <> FEndBytePos) or (Value.Y <> FEndLinePos) then begin
|
||||
{$IFDEF SYN_MBCSSUPPORT}
|
||||
if Value.Y <= fLines.Count then begin
|
||||
s := fLines[Value.Y - 1];
|
||||
if (Length(s) >= Value.X) and (mbTrailByte = ByteType(s, Value.X)) then
|
||||
Dec(Value.X);
|
||||
end;
|
||||
{$ENDIF}
|
||||
if (Value.X <> FEndBytePos) or (Value.Y <> FEndLinePos) then begin
|
||||
if (SelectionMode = smColumn) and (Value.X <> FEndBytePos) then begin
|
||||
FInvalidateLinesMethod(
|
||||
Min(FStartLinePos, Min(FEndLinePos, Value.Y)),
|
||||
Max(FStartLinePos, Max(FEndLinePos, Value.Y)));
|
||||
FEndLinePos := Value.Y;
|
||||
FEndBytePos := Value.X;
|
||||
end else begin
|
||||
nLine := FEndLinePos;
|
||||
FEndLinePos := Value.Y;
|
||||
FEndBytePos := Value.X;
|
||||
if (SelectionMode <> smColumn) or (FStartBytePos <> FEndBytePos) then
|
||||
FInvalidateLinesMethod(nLine, FEndLinePos);
|
||||
end;
|
||||
FOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TSynEditSelection.SetSelectionMode(const Value: TSynSelectionMode);
|
||||
begin
|
||||
if FSelectionMode <> Value then begin
|
||||
FSelectionMode := Value;
|
||||
if SelAvail then
|
||||
FInvalidateLinesMethod(-1, -1);
|
||||
FOnChangeList.CallNotifyEvents(self);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSynEditSelection.AdjustBytePosToCharacterStart(Line : integer; BytePos : integer) : integer;
|
||||
var
|
||||
s: string;
|
||||
begin
|
||||
Result := BytePos;
|
||||
if Result < 1 then
|
||||
Result := 1
|
||||
else if (Line >= 1) and (Line <= FLines.Count) then begin
|
||||
s := FLines[Line-1];
|
||||
if (Result <= length(s)) and FLines.IsUtf8 then
|
||||
Result:=UTF8FindNearestCharStart(PChar(Pointer(s)),length(s),Result);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSynEditSelection.GetFirstLineBytePos: TPoint;
|
||||
begin
|
||||
if IsBackwardSel then
|
||||
Result := EndLineBytePos
|
||||
else
|
||||
Result := StartLineBytePos;
|
||||
end;
|
||||
|
||||
function TSynEditSelection.GetLastLineBytePos: TPoint;
|
||||
begin
|
||||
if IsBackwardSel then
|
||||
Result := StartLineBytePos
|
||||
else
|
||||
Result := EndLineBytePos;
|
||||
end;
|
||||
|
||||
function TSynEditSelection.SelAvail : Boolean;
|
||||
begin
|
||||
Result := (FStartBytePos <> FEndBytePos) or
|
||||
((FStartLinePos <> FEndLinePos) and (FSelectionMode <> smColumn));
|
||||
end;
|
||||
|
||||
function TSynEditSelection.IsBackwardSel: Boolean;
|
||||
begin
|
||||
Result := (FStartLinePos > FEndLinePos)
|
||||
or ((FStartLinePos = FEndLinePos) and (FStartBytePos > FEndBytePos));
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -26,14 +26,20 @@ unit SynEditTextBase;
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, SynEditTypes;
|
||||
|
||||
Classes, SysUtils, LCLProc, SynEditTypes;
|
||||
|
||||
type
|
||||
|
||||
{ TSynEditStrings }
|
||||
|
||||
TSynEditStrings = class(TStrings)
|
||||
protected
|
||||
FTabWidth: integer;
|
||||
FIsUtf8: Boolean;
|
||||
function GetIsUtf8 : Boolean; virtual;
|
||||
procedure SetIsUtf8(const AValue : Boolean); virtual;
|
||||
function GetTabWidth : integer; virtual;
|
||||
procedure SetTabWidth(const AValue : integer); virtual;
|
||||
function GetFoldEndLevel(Index: integer): integer; virtual; abstract;
|
||||
function GetFoldMinLevel(Index: integer): integer; virtual; abstract;
|
||||
procedure SetFoldEndLevel(Index: integer; const AValue: integer); virtual; abstract;
|
||||
@ -44,13 +50,29 @@ type
|
||||
function GetLengthOfLongestLine: integer; virtual; abstract;
|
||||
procedure SetTextStr(const Value: string); override;
|
||||
public
|
||||
constructor Create;
|
||||
procedure DeleteLines(Index, NumLines: integer); virtual; abstract;
|
||||
procedure InsertLines(Index, NumLines: integer); virtual; abstract;
|
||||
procedure InsertStrings(Index: integer; NewStrings: TStrings); virtual; abstract;
|
||||
procedure ClearRanges(ARange: TSynEditRange); virtual; abstract;
|
||||
public
|
||||
// Byte to Char
|
||||
function LogicalToPhysicalPos(const p: TPoint): TPoint;
|
||||
function LogicalToPhysicalCol(const Line: string;
|
||||
LogicalPos: integer): integer;
|
||||
function LogicalToPhysicalCol(Line: PChar; LineLen: integer;
|
||||
LogicalPos, StartBytePos, StartPhysicalPos: integer): integer;
|
||||
// Char to Byte
|
||||
function PhysicalToLogicalPos(const p: TPoint): TPoint;
|
||||
function PhysicalToLogicalCol(const Line: string;
|
||||
PhysicalPos: integer): integer;
|
||||
function PhysicalToLogicalCol(const Line: string;
|
||||
PhysicalPos, StartBytePos, StartPhysicalPos: integer): integer;
|
||||
public
|
||||
property ExpandedStrings[Index: integer]: string read GetExpandedString;
|
||||
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
|
||||
property TabWidth: integer read GetTabWidth write SetTabWidth;
|
||||
property IsUtf8: Boolean read GetIsUtf8 write SetIsUtf8;
|
||||
property Ranges[Index: integer]: TSynEditRange read GetRange write PutRange;
|
||||
property FoldMinLevel[Index: integer]: integer read GetFoldMinLevel
|
||||
write SetFoldMinLevel;
|
||||
@ -64,6 +86,33 @@ implementation
|
||||
|
||||
{ TSynEditStrings }
|
||||
|
||||
constructor TSynEditStrings.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
TabWidth := 8;
|
||||
IsUtf8 := True;
|
||||
end;
|
||||
|
||||
function TSynEditStrings.GetIsUtf8 : Boolean;
|
||||
begin
|
||||
Result := FIsUtf8;
|
||||
end;
|
||||
|
||||
function TSynEditStrings.GetTabWidth : integer;
|
||||
begin
|
||||
Result := FTabWidth;
|
||||
end;
|
||||
|
||||
procedure TSynEditStrings.SetIsUtf8(const AValue : Boolean);
|
||||
begin
|
||||
FIsUtf8 := AValue;
|
||||
end;
|
||||
|
||||
procedure TSynEditStrings.SetTabWidth(const AValue : integer);
|
||||
begin
|
||||
FTabWidth := AValue;
|
||||
end;
|
||||
|
||||
procedure TSynEditStrings.SetTextStr(const Value : string);
|
||||
var
|
||||
StartPos: Integer;
|
||||
@ -98,5 +147,96 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSynEditStrings.LogicalToPhysicalPos(const p : TPoint) : TPoint;
|
||||
begin
|
||||
Result := p;
|
||||
if Result.Y - 1 < Count then
|
||||
Result.X:=LogicalToPhysicalCol(self[Result.Y - 1],Result.X);
|
||||
end;
|
||||
|
||||
function TSynEditStrings.LogicalToPhysicalCol(const Line : string; LogicalPos : integer) : integer;
|
||||
begin
|
||||
Result := LogicalToPhysicalCol(PChar(Pointer(Line)),length(Line),LogicalPos,1,1);
|
||||
end;
|
||||
|
||||
function TSynEditStrings.LogicalToPhysicalCol(Line : PChar; LineLen : integer; LogicalPos, StartBytePos, StartPhysicalPos : integer) : integer;
|
||||
var
|
||||
BytePos, ByteLen: integer;
|
||||
ScreenPos: integer;
|
||||
begin
|
||||
ByteLen := LineLen;
|
||||
// map UTF8 and Tab chars
|
||||
ScreenPos := StartPhysicalPos;
|
||||
BytePos:= StartBytePos;
|
||||
while BytePos<LogicalPos do begin
|
||||
if (BytePos <= ByteLen) then begin
|
||||
if Line[BytePos-1] = #9 then begin
|
||||
inc(ScreenPos, TabWidth - ((ScreenPos-1) mod TabWidth));
|
||||
inc(BytePos);
|
||||
end else begin
|
||||
inc(ScreenPos);
|
||||
if IsUTF8 then
|
||||
inc(BytePos,UTF8CharacterLength(@Line[BytePos-1]))
|
||||
else
|
||||
inc(BytePos);
|
||||
end;
|
||||
end else begin
|
||||
// beyond end of line
|
||||
inc(ScreenPos,LogicalPos-BytePos);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
if (BytePos>LogicalPos) and (ScreenPos>StartPhysicalPos) then
|
||||
dec(ScreenPos);
|
||||
Result := ScreenPos;
|
||||
end;
|
||||
|
||||
function TSynEditStrings.PhysicalToLogicalPos(const p : TPoint) : TPoint;
|
||||
begin
|
||||
Result := p;
|
||||
if (Result.Y>=1) and (Result.Y <= Count) then
|
||||
Result.X:=PhysicalToLogicalCol(self[Result.Y - 1],Result.X,1,1);
|
||||
end;
|
||||
|
||||
function TSynEditStrings.PhysicalToLogicalCol(const Line : string; PhysicalPos : integer) : integer;
|
||||
begin
|
||||
Result:=PhysicalToLogicalCol(Line,PhysicalPos,1,1);
|
||||
end;
|
||||
|
||||
function TSynEditStrings.PhysicalToLogicalCol(const Line : string; PhysicalPos, StartBytePos, StartPhysicalPos : integer) : integer;
|
||||
var
|
||||
BytePos, ByteLen: integer;
|
||||
ScreenPos: integer;
|
||||
PLine: PChar;
|
||||
begin
|
||||
ByteLen := Length(Line);
|
||||
ScreenPos := StartPhysicalPos;
|
||||
BytePos := StartBytePos;
|
||||
PLine := PChar(Line);
|
||||
// map utf and tab chars
|
||||
while ScreenPos < PhysicalPos do begin
|
||||
if (BytePos <= ByteLen) then begin
|
||||
if (PLine[BytePos-1] <> #9) then begin
|
||||
inc(ScreenPos);
|
||||
if IsUTF8 then
|
||||
inc(BytePos,UTF8CharacterLength(@PLine[BytePos-1]))
|
||||
else
|
||||
inc(BytePos);
|
||||
end else begin
|
||||
inc(ScreenPos, TabWidth - ((ScreenPos-1) mod TabWidth));
|
||||
inc(BytePos);
|
||||
end;
|
||||
end else begin
|
||||
// beyond end of line
|
||||
inc(BytePos,PhysicalPos-ScreenPos);
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
if (ScreenPos>PhysicalPos) and (BytePos>1) and (BytePos-2<ByteLen)
|
||||
and (PLine[BytePos-2]=#9) then
|
||||
dec(BytePos);
|
||||
Result := BytePos;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
|
@ -109,7 +109,6 @@ type
|
||||
fSimulateConvertTabsProc: TSimulateConvertTabsProcEx;
|
||||
{$ENDIF}
|
||||
fIndexOfLongestLine: integer;
|
||||
fTabWidth: integer;
|
||||
{end} //mh 2000-10-19
|
||||
fOnChange: TNotifyEvent;
|
||||
fOnChanging: TNotifyEvent;
|
||||
@ -151,7 +150,7 @@ type
|
||||
procedure PutObject(Index: integer; AObject: TObject); override;
|
||||
procedure SetCapacity(NewCapacity: integer);
|
||||
{$IFDEF SYN_COMPILER_3_UP} override; {$ENDIF} //mh 2000-10-18
|
||||
procedure SetTabWidth(Value: integer); //mh 2000-10-19
|
||||
procedure SetTabWidth(const Value: integer); override;
|
||||
procedure SetUpdateState(Updating: Boolean); override;
|
||||
public
|
||||
constructor Create;
|
||||
@ -180,7 +179,6 @@ type
|
||||
property LengthOfLongestLine: integer read GetLengthOfLongestLine;
|
||||
{end} //mh 2000-10-19
|
||||
property Ranges[Index: integer]: TSynEditRange read GetRange write PutRange;
|
||||
property TabWidth: integer read fTabWidth write SetTabWidth; //mh 2000-10-19
|
||||
property OnAdded: TStringListIndexEvent read fOnAdded write fOnAdded;
|
||||
property OnChange: TNotifyEvent read fOnChange write fOnChange;
|
||||
property OnChanging: TNotifyEvent read fOnChanging write fOnChanging;
|
||||
@ -501,7 +499,6 @@ begin
|
||||
fDosFileFormat := TRUE;
|
||||
{begin} //mh 2000-10-19
|
||||
fIndexOfLongestLine := -1;
|
||||
TabWidth := 8;
|
||||
{end} //mh 2000-10-19
|
||||
end;
|
||||
|
||||
@ -1052,12 +1049,12 @@ begin
|
||||
end;
|
||||
|
||||
{begin} //mh 2000-10-19
|
||||
procedure TSynEditStringList.SetTabWidth(Value: integer);
|
||||
procedure TSynEditStringList.SetTabWidth(const Value: integer);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
if Value <> fTabWidth then begin
|
||||
fTabWidth := Value;
|
||||
if Value <> FTabWidth then begin
|
||||
Inherited SetTabWidth(Value);
|
||||
fConvertTabsProc := GetBestConvertTabsProcEx(fTabWidth);
|
||||
{$IFDEF SYN_LAZARUS}
|
||||
fSimulateConvertTabsProc := GetBestSimulateConvertTabsProcEx(fTabWidth);
|
||||
|
@ -28,7 +28,8 @@ interface
|
||||
|
||||
uses
|
||||
LCLProc,
|
||||
Classes, SysUtils, SynEditTypes, SynEditTextBase, SynEditTextBuffer, SynEditMiscClasses;
|
||||
Classes, SysUtils, SynEditTypes, SynEditTextBase, SynEditTextBuffer,
|
||||
SynEditMiscClasses, SynEditPointClasses;
|
||||
|
||||
type
|
||||
|
||||
@ -52,6 +53,10 @@ type
|
||||
procedure DoLinesChanged(Index, N: integer);
|
||||
procedure TrimAfterLock;
|
||||
protected
|
||||
function GetIsUtf8 : Boolean; override;
|
||||
procedure SetIsUtf8(const AValue : Boolean); override;
|
||||
function GetTabWidth : integer; override;
|
||||
procedure SetTabWidth(const AValue : integer); override;
|
||||
function GetFoldEndLevel(Index: integer): integer; override;
|
||||
function GetFoldMinLevel(Index: integer): integer; override;
|
||||
procedure SetFoldEndLevel(Index: integer; const AValue: integer); override;
|
||||
@ -104,7 +109,6 @@ implementation
|
||||
|
||||
constructor TSynEditStringTrimmingList.Create(ASynStringSource : TSynEditStrings; ACaret: TSynEditCaret);
|
||||
begin
|
||||
Inherited Create;
|
||||
fSynStrings := ASynStringSource;
|
||||
fCaret := ACaret;
|
||||
fCaret.AddChangeHandler(@DoCaretChanged);
|
||||
@ -112,6 +116,7 @@ begin
|
||||
fLineIndex:= -1;
|
||||
fSpaces := '';
|
||||
fEnabled:=false;
|
||||
Inherited Create;
|
||||
end;
|
||||
|
||||
destructor TSynEditStringTrimmingList.Destroy;
|
||||
@ -264,6 +269,26 @@ begin
|
||||
fLockList.Clear;
|
||||
end;
|
||||
|
||||
function TSynEditStringTrimmingList.GetIsUtf8 : Boolean;
|
||||
begin
|
||||
Result := FSynStrings.IsUtf8;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.SetIsUtf8(const AValue : Boolean);
|
||||
begin
|
||||
FSynStrings.IsUtf8 := AValue;
|
||||
end;
|
||||
|
||||
function TSynEditStringTrimmingList.GetTabWidth : integer;
|
||||
begin
|
||||
Result := FSynStrings.TabWidth;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.SetTabWidth(const AValue : integer);
|
||||
begin
|
||||
FSynStrings.TabWidth := AValue;
|
||||
end;
|
||||
|
||||
procedure TSynEditStringTrimmingList.ForceTrim;
|
||||
begin
|
||||
TrimAfterLock;
|
||||
|
Loading…
Reference in New Issue
Block a user