Стартовый пул
895
ATSynEdit/atsynedit/atstringproc.pas
Normal file
@@ -0,0 +1,895 @@
|
||||
unit ATStringProc;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
//{$define test_wide_char}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, StrUtils;
|
||||
|
||||
type
|
||||
atString = UnicodeString;
|
||||
atChar = WideChar;
|
||||
PatChar = PWideChar;
|
||||
|
||||
type
|
||||
TATIntArray = array of Longint;
|
||||
TATRealArray = array of real;
|
||||
TATPointArray = array of TPoint;
|
||||
|
||||
function SCharUpper(ch: atChar): atChar;
|
||||
function SCharLower(ch: atChar): atChar;
|
||||
function SCaseTitle(const S, SWordChars: atString): atString;
|
||||
function SCaseInvert(const S: atString): atString;
|
||||
function SCaseSentence(const S, SWordChars: atString): atString;
|
||||
|
||||
{$Z1}
|
||||
type
|
||||
TATLineEnds = (cEndNone, cEndWin, cEndUnix, cEndMac);
|
||||
const
|
||||
cLineEndStrings: array[TATLineEnds] of atString = ('', #13#10, #10, #13);
|
||||
cLineEndNiceNames: array[TATLineEnds] of string = ('', 'win', 'un', 'mac');
|
||||
|
||||
const
|
||||
cMaxTabPositionToExpand = 500; //no sense to expand too far tabs
|
||||
cCharScaleFullwidth_Default = 1.7; //width of CJK chars
|
||||
cCharScaleHex = 6.0; //width of hex show: "<NNNN>"
|
||||
cMinWordWrapOffset = 3;
|
||||
|
||||
var
|
||||
OptHexCharsDefault: UnicodeString = ''; //show these chars as "<NNNN>"
|
||||
OptHexCharsUser: UnicodeString = ''; //these too
|
||||
OptCommaCharsWrapWithWords: UnicodeString = '.,;:''"`~?!&%$';
|
||||
cCharScaleFullwidth: Single = cCharScaleFullwidth_Default;
|
||||
|
||||
|
||||
function IsCharEol(ch: atChar): boolean;
|
||||
function IsCharWord(ch: atChar; const AWordChars: atString): boolean;
|
||||
function IsCharSpace(ch: atChar): boolean;
|
||||
function IsCharAsciiControl(ch: atChar): boolean;
|
||||
function IsCharAccent(ch: atChar): boolean;
|
||||
function IsCharHex(ch: atChar): boolean;
|
||||
function IsStringWithUnicodeChars(const S: atString): boolean;
|
||||
|
||||
function SBeginsWith(const S, SubStr: atString): boolean;
|
||||
function SBeginsWith(const S, SubStr: string): boolean;
|
||||
function SEndsWith(const S, SubStr: atString): boolean;
|
||||
function SEndsWith(const S, SubStr: string): boolean;
|
||||
|
||||
function STrimRight(const S: atString): atString;
|
||||
function SGetIndentChars(const S: atString): integer;
|
||||
function SGetIndentExpanded(const S: atString; ATabSize: integer): integer;
|
||||
function SGetNonSpaceLength(const S: atString): integer;
|
||||
|
||||
function STabsToSpaces(const S: atString; ATabSize: integer): atString;
|
||||
function SSpacesToTabs(const S: atString; ATabSize: integer): atString;
|
||||
function SCharPosToColumnPos(const S: atString; APos, ATabSize: integer): integer;
|
||||
function SColumnPosToCharPos(const S: atString; AColumn, ATabSize: integer): integer;
|
||||
|
||||
type
|
||||
TATCommentAction = (
|
||||
cCommentAdd_AtNonspace,
|
||||
cCommentAdd_AtNonespace_IfNone,
|
||||
cCommentAdd_AtStart,
|
||||
cCommentAdd_AtStart_IfNone,
|
||||
cCommentRemove,
|
||||
cCommentToggle_AtNonspace,
|
||||
cCommentToggle_AtStart
|
||||
);
|
||||
|
||||
function SCommentLineAction(L: TStringList; const AComment: atString; Act: TATCommentAction): boolean;
|
||||
|
||||
function SRemoveNewlineChars(const S: atString): atString;
|
||||
function SRemoveHexChars(const S: atString): atString;
|
||||
function SRemoveAsciiControlChars(const S: atString): atString;
|
||||
|
||||
procedure SCalcCharOffsets(const S: atString; var AList: TATRealArray;
|
||||
ATabSize: integer; ACharsSkipped: integer = 0);
|
||||
function SFindWordWrapOffset(const S: atString; AColumns, ATabSize: integer;
|
||||
const AWordChars: atString; AWrapIndented: boolean): integer;
|
||||
function SFindClickedPosition(const Str: atString;
|
||||
APixelsFromLeft, ACharSize, ATabSize: integer;
|
||||
AAllowVirtualPos: boolean;
|
||||
out AEndOfLinePos: boolean): integer;
|
||||
procedure SFindOutputSkipOffset(const S: atString; ATabSize, AScrollPos: integer;
|
||||
out ACharsSkipped: integer; out ASpacesSkipped: real);
|
||||
|
||||
function SIndentUnindent(const Str: atString; ARight: boolean;
|
||||
AIndentSize, ATabSize: integer): atString;
|
||||
function SGetItem(var S: string; const sep: Char = ','): string;
|
||||
function SGetItemAtEnd(var S: string; const sep: Char = ','): string;
|
||||
function SSwapEndian(const S: UnicodeString): UnicodeString;
|
||||
function SWithBreaks(const S: atString): boolean;
|
||||
procedure SAddStringToHistory(const S: string; List: TStrings; MaxItems: integer);
|
||||
|
||||
function BoolToPlusMinusOne(b: boolean): integer;
|
||||
procedure TrimStringList(L: TStringList);
|
||||
|
||||
type
|
||||
TATDecodeRec = record SFrom, STo: UnicodeString; end;
|
||||
function SDecodeRecords(const S: UnicodeString; const Decode: array of TATDecodeRec): UnicodeString;
|
||||
|
||||
procedure SReplaceAll(var s: string; const SFrom, STo: string);
|
||||
procedure SReplaceAllPercentChars(var S: string);
|
||||
procedure SDeleteFrom(var s: string; const SFrom: string);
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Dialogs, Math;
|
||||
|
||||
function IsCharEol(ch: atChar): boolean;
|
||||
begin
|
||||
Result:= (ch=#10) or (ch=#13);
|
||||
end;
|
||||
|
||||
function IsCharWord(ch: atChar; const AWordChars: atString): boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
|
||||
case Ord(ch) of
|
||||
//Eng
|
||||
Ord('0')..Ord('9'),
|
||||
Ord('a')..Ord('z'),
|
||||
Ord('A')..Ord('Z'),
|
||||
Ord('_'),
|
||||
//German
|
||||
$E4, $C4, $E9, $F6, $D6, $FC, $DC, $DF,
|
||||
//Rus
|
||||
$0430..$044F, //a..z
|
||||
$0410..$042F, //A..Z
|
||||
$0451, $0401, //yo, Yo
|
||||
//Greek
|
||||
$0391..$03A9,
|
||||
$03B1..$03C9:
|
||||
begin Result:= true; Exit end;
|
||||
end;
|
||||
|
||||
if AWordChars<>'' then
|
||||
if Pos(ch, AWordChars)>0 then
|
||||
Result:= true;
|
||||
end;
|
||||
|
||||
function IsCharSpace(ch: atChar): boolean;
|
||||
begin
|
||||
Result:= (ch=' ') or (ch=#9);
|
||||
end;
|
||||
|
||||
function IsCharAsciiControl(ch: atChar): boolean;
|
||||
begin
|
||||
Result:= (ch<>#9) and (AnsiChar(ch)<' ');
|
||||
end;
|
||||
|
||||
function IsCharHex(ch: atChar): boolean;
|
||||
begin
|
||||
Result:= Pos(ch, OptHexCharsDefault+OptHexCharsUser)>0;
|
||||
end;
|
||||
|
||||
function IsStringWithUnicodeChars(const S: atString): boolean;
|
||||
var
|
||||
i, N: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
for i:= 1 to Length(S) do
|
||||
begin
|
||||
N:= Ord(S[i]);
|
||||
if (N<32) or (N>126) then exit(true);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure DoDebugOffsets(const List: TATRealArray);
|
||||
var
|
||||
i: integer;
|
||||
s: string;
|
||||
begin
|
||||
s:= '';
|
||||
for i:= Low(List) to High(List) do
|
||||
s:= s+FloatToStr(List[i])+' ';
|
||||
showmessage('Offsets'#13+s);
|
||||
end;
|
||||
|
||||
function SFindWordWrapOffset(const S: atString; AColumns, ATabSize: integer;
|
||||
const AWordChars: atString; AWrapIndented: boolean): integer;
|
||||
//
|
||||
//override IsCharWord to check also commas,dots,quotes
|
||||
//to wrap them with wordchars
|
||||
function _IsWord(ch: atChar): boolean;
|
||||
begin
|
||||
Result:= IsCharWord(ch, AWordChars+OptCommaCharsWrapWithWords);
|
||||
end;
|
||||
//
|
||||
var
|
||||
N, NMin, NAvg: integer;
|
||||
List: TATRealArray;
|
||||
begin
|
||||
if S='' then
|
||||
begin Result:= 0; Exit end;
|
||||
if AColumns<cMinWordWrapOffset then
|
||||
begin Result:= AColumns; Exit end;
|
||||
|
||||
SetLength(List, Length(S));
|
||||
SCalcCharOffsets(S, List, ATabSize);
|
||||
|
||||
if List[High(List)]<=AColumns then
|
||||
begin
|
||||
Result:= Length(S);
|
||||
Exit
|
||||
end;
|
||||
|
||||
//NAvg is average wrap offset, we use it if no correct offset found
|
||||
N:= Length(S)-1;
|
||||
while (N>0) and (List[N]>AColumns+1) do Dec(N);
|
||||
NAvg:= N;
|
||||
if NAvg<cMinWordWrapOffset then
|
||||
begin Result:= cMinWordWrapOffset; Exit end;
|
||||
|
||||
//find correct offset: not allowed at edge
|
||||
//a) 2 wordchars,
|
||||
//b) space as 2nd char (not nice look for Python src)
|
||||
NMin:= SGetIndentChars(S)+1;
|
||||
while (N>NMin) and
|
||||
((_IsWord(S[N]) and _IsWord(S[N+1])) or
|
||||
(AWrapIndented and IsCharSpace(S[N+1])))
|
||||
do Dec(N);
|
||||
|
||||
//use correct of avg offset
|
||||
if N>NMin then
|
||||
Result:= N
|
||||
else
|
||||
Result:= NAvg;
|
||||
end;
|
||||
|
||||
function SGetIndentChars(const S: atString): integer;
|
||||
begin
|
||||
Result:= 0;
|
||||
while (Result<Length(S)) and IsCharSpace(S[Result+1]) do
|
||||
Inc(Result);
|
||||
end;
|
||||
|
||||
function SGetNonSpaceLength(const S: atString): integer;
|
||||
begin
|
||||
Result:= Length(S);
|
||||
while (Result>0) and IsCharSpace(S[Result]) do Dec(Result);
|
||||
if Result=0 then
|
||||
Result:= Length(S);
|
||||
end;
|
||||
|
||||
function SGetIndentExpanded(const S: atString; ATabSize: integer): integer;
|
||||
var
|
||||
SIndent: atString;
|
||||
begin
|
||||
SIndent:= Copy(S, 1, SGetIndentChars(S));
|
||||
SIndent:= STabsToSpaces(SIndent, ATabSize);
|
||||
Result:= Length(SIndent);
|
||||
end;
|
||||
|
||||
function SSwapEndian(const S: UnicodeString): UnicodeString;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= S;
|
||||
for i:= 1 to Length(Result) do
|
||||
Result[i]:= WideChar(SwapEndian(Ord(Result[i])));
|
||||
end;
|
||||
|
||||
function SCalcTabulationSize(const ATabSize, APos: integer): integer;
|
||||
begin
|
||||
Result:= 1;
|
||||
if APos>cMaxTabPositionToExpand then Exit;
|
||||
while (APos+Result-1) mod ATabSize <> 0 do
|
||||
Inc(Result);
|
||||
end;
|
||||
|
||||
function STabsToSpaces(const S: atString; ATabSize: integer): atString;
|
||||
var
|
||||
N, NSize: integer;
|
||||
begin
|
||||
Result:= S;
|
||||
repeat
|
||||
N:= Pos(#9, Result);
|
||||
if N=0 then Break;
|
||||
NSize:= SCalcTabulationSize(ATabSize, N);
|
||||
if NSize<=1 then
|
||||
Result[N]:= ' '
|
||||
else
|
||||
begin
|
||||
Delete(Result, N, 1);
|
||||
Insert(StringOfChar(' ', NSize), Result, N);
|
||||
end;
|
||||
until false;
|
||||
end;
|
||||
|
||||
{
|
||||
http://en.wikipedia.org/wiki/Combining_character
|
||||
Combining Diacritical Marks (0300–036F), since version 1.0, with modifications in subsequent versions down to 4.1
|
||||
Combining Diacritical Marks Extended (1AB0–1AFF), version 7.0
|
||||
Combining Diacritical Marks Supplement (1DC0–1DFF), versions 4.1 to 5.2
|
||||
Combining Diacritical Marks for Symbols (20D0–20FF), since version 1.0, with modifications in subsequent versions down to 5.1
|
||||
Combining Half Marks (FE20–FE2F), versions 1.0, updates in 5.2
|
||||
}
|
||||
{
|
||||
http://www.unicode.org/charts/PDF/U0E80.pdf
|
||||
cannot render them ok anyway as accents:
|
||||
0EB1, 0EB4..0EBC, 0EC8..0ECD
|
||||
}
|
||||
function IsCharAccent(ch: atChar): boolean;
|
||||
begin
|
||||
case Ord(ch) of
|
||||
$0300..$036F,
|
||||
$1AB0..$1AFF,
|
||||
$1DC0..$1DFF,
|
||||
$20D0..$20FF,
|
||||
{$ifdef unix}
|
||||
$0EB1, $0EB4..$0EBC, $0EC8..$0ECD, //Lao accent chars
|
||||
{$endif}
|
||||
$FE20..$FE2F:
|
||||
Result:= true;
|
||||
else
|
||||
Result:= false;
|
||||
end;
|
||||
end;
|
||||
|
||||
function IsCharFullWidth(ch: atChar): boolean;
|
||||
begin
|
||||
case Ord(ch) of
|
||||
$1100..$115F,
|
||||
$2329..$232A,
|
||||
$2E80..$303E,
|
||||
$3041..$33FF,
|
||||
$3400..$4DB5,
|
||||
$4E00..$9FC3,
|
||||
$A000..$A4C6,
|
||||
$AC00..$D7A3,
|
||||
$F900..$FAD9,
|
||||
$FE10..$FE19,
|
||||
$FE30..$FE6B,
|
||||
$FF01..$FF60,
|
||||
$FFE0..$FFE6:
|
||||
Result:= true;
|
||||
else
|
||||
Result:= false;
|
||||
end;
|
||||
end;
|
||||
|
||||
{$ifdef test_wide_char}
|
||||
const
|
||||
cScaleTest = 1.9; //debug, for test code, commented
|
||||
{$endif}
|
||||
|
||||
procedure SCalcCharOffsets(const S: atString; var AList: TATRealArray;
|
||||
ATabSize: integer; ACharsSkipped: integer);
|
||||
var
|
||||
NSize, NTabSize, NCharsSkipped: integer;
|
||||
Scale: real;
|
||||
i: integer;
|
||||
begin
|
||||
if S='' then Exit;
|
||||
if Length(AList)<>Length(S) then
|
||||
raise Exception.Create('Bad list len: CalcCharOffsets');
|
||||
|
||||
NCharsSkipped:= ACharsSkipped;
|
||||
|
||||
for i:= 1 to Length(S) do
|
||||
begin
|
||||
Inc(NCharsSkipped);
|
||||
|
||||
Scale:= 1.0;
|
||||
if IsCharHex(S[i]) then
|
||||
Scale:= cCharScaleHex
|
||||
else
|
||||
if IsCharFullWidth(S[i]) then
|
||||
Scale:= cCharScaleFullwidth;
|
||||
|
||||
{$ifdef test_wide_char}
|
||||
if IsSpaceChar(S[i]) then
|
||||
Scale:= 1
|
||||
else
|
||||
Scale:= cScaleTest;
|
||||
{$endif}
|
||||
|
||||
if S[i]<>#9 then
|
||||
NSize:= 1
|
||||
else
|
||||
begin
|
||||
NTabSize:= SCalcTabulationSize(ATabSize, NCharsSkipped);
|
||||
NSize:= NTabSize;
|
||||
Inc(NCharsSkipped, NTabSize-1);
|
||||
end;
|
||||
|
||||
if (i<Length(S)) and IsCharAccent(S[i+1]) then
|
||||
NSize:= 0;
|
||||
|
||||
if i=1 then
|
||||
AList[i-1]:= NSize*Scale
|
||||
else
|
||||
AList[i-1]:= AList[i-2]+NSize*Scale;
|
||||
end;
|
||||
end;
|
||||
|
||||
function SFindClickedPosition(const Str: atString;
|
||||
APixelsFromLeft, ACharSize, ATabSize: integer;
|
||||
AAllowVirtualPos: boolean;
|
||||
out AEndOfLinePos: boolean): integer;
|
||||
var
|
||||
ListReal: TATRealArray;
|
||||
ListEnds, ListMid: TATIntArray;
|
||||
i: integer;
|
||||
begin
|
||||
AEndOfLinePos:= false;
|
||||
if Str='' then
|
||||
begin
|
||||
if AAllowVirtualPos then
|
||||
Result:= 1+APixelsFromLeft div ACharSize
|
||||
else
|
||||
Result:= 1;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
SetLength(ListReal, Length(Str));
|
||||
SetLength(ListEnds, Length(Str));
|
||||
SetLength(ListMid, Length(Str));
|
||||
SCalcCharOffsets(Str, ListReal, ATabSize);
|
||||
|
||||
//positions of each char end
|
||||
for i:= 0 to High(ListEnds) do
|
||||
ListEnds[i]:= Trunc(ListReal[i]*ACharSize);
|
||||
|
||||
//positions of each char middle
|
||||
for i:= 0 to High(ListEnds) do
|
||||
if i=0 then
|
||||
ListMid[i]:= ListEnds[i] div 2
|
||||
else
|
||||
ListMid[i]:= (ListEnds[i-1]+ListEnds[i]) div 2;
|
||||
|
||||
for i:= 0 to High(ListEnds) do
|
||||
if APixelsFromLeft<ListMid[i] then
|
||||
begin
|
||||
Result:= i+1;
|
||||
Exit
|
||||
end;
|
||||
|
||||
AEndOfLinePos:= true;
|
||||
if AAllowVirtualPos then
|
||||
Result:= Length(Str)+1 + (APixelsFromLeft - ListEnds[High(ListEnds)]) div ACharSize
|
||||
else
|
||||
Result:= Length(Str)+1;
|
||||
end;
|
||||
|
||||
procedure SFindOutputSkipOffset(const S: atString; ATabSize, AScrollPos: integer;
|
||||
out ACharsSkipped: integer; out ASpacesSkipped: real);
|
||||
var
|
||||
List: TATRealArray;
|
||||
begin
|
||||
ACharsSkipped:= 0;
|
||||
ASpacesSkipped:= 0;
|
||||
if (S='') or (AScrollPos=0) then Exit;
|
||||
|
||||
SetLength(List, Length(S));
|
||||
SCalcCharOffsets(S, List, ATabSize);
|
||||
|
||||
while (ACharsSkipped<Length(S)) and (List[ACharsSkipped]<AScrollPos) do
|
||||
Inc(ACharsSkipped);
|
||||
|
||||
if (ACharsSkipped>0) then
|
||||
ASpacesSkipped:= List[ACharsSkipped-1];
|
||||
end;
|
||||
|
||||
|
||||
function BoolToPlusMinusOne(b: boolean): integer;
|
||||
begin
|
||||
if b then Result:= 1 else Result:= -1;
|
||||
end;
|
||||
|
||||
function SGetItem(var S: string; const sep: Char = ','): string;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
i:= Pos(sep, s);
|
||||
if i=0 then i:= MaxInt;
|
||||
Result:= Copy(s, 1, i-1);
|
||||
Delete(s, 1, i);
|
||||
end;
|
||||
|
||||
function SGetItemAtEnd(var S: string; const sep: Char = ','): string;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= '';
|
||||
i:= Pos(sep, S);
|
||||
if i>0 then
|
||||
begin
|
||||
Result:= Copy(S, i+1, MaxInt);
|
||||
Delete(S, i, MaxInt);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TrimStringList(L: TStringList);
|
||||
begin
|
||||
//dont do "while", we need correct last empty lines
|
||||
if (L.Count>0) and (L[L.Count-1]='') then
|
||||
L.Delete(L.Count-1);
|
||||
end;
|
||||
|
||||
function SWithBreaks(const S: atString): boolean;
|
||||
begin
|
||||
Result:=
|
||||
(Pos(#13, S)>0) or
|
||||
(Pos(#10, S)>0);
|
||||
end;
|
||||
|
||||
function SSpacesToTabs(const S: atString; ATabSize: integer): atString;
|
||||
begin
|
||||
Result:= StringReplace(S, StringOfChar(' ', ATabSize), #9, [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
function SCharPosToColumnPos(const S: atString; APos, ATabSize: integer): integer;
|
||||
begin
|
||||
Result:= Length(STabsToSpaces(Copy(S, 1, APos), ATabSize));
|
||||
if APos>Length(S) then
|
||||
Inc(Result, APos-Length(S));
|
||||
end;
|
||||
|
||||
function SColumnPosToCharPos(const S: atString; AColumn, ATabSize: integer): integer;
|
||||
var
|
||||
size, i: integer;
|
||||
begin
|
||||
if AColumn=0 then exit(AColumn);
|
||||
if Pos(#9, S)=0 then exit(AColumn);
|
||||
|
||||
size:= 0;
|
||||
for i:= 1 to Length(S) do
|
||||
begin
|
||||
if S[i]<>#9 then
|
||||
Inc(size)
|
||||
else
|
||||
Inc(size, SCalcTabulationSize(ATabSize, size+1));
|
||||
if size>=AColumn then
|
||||
exit(i);
|
||||
end;
|
||||
|
||||
Result:= AColumn-Length(STabsToSpaces(S, ATabSize))+Length(S);
|
||||
end;
|
||||
|
||||
function SIndentUnindent(const Str: atString; ARight: boolean;
|
||||
AIndentSize, ATabSize: integer): atString;
|
||||
var
|
||||
StrIndent, StrText: atString;
|
||||
DecSpaces, N: integer;
|
||||
DoTabs: boolean;
|
||||
begin
|
||||
Result:= Str;
|
||||
|
||||
//indent<0 - use tabs
|
||||
if AIndentSize>=0 then
|
||||
begin
|
||||
StrIndent:= StringOfChar(' ', AIndentSize);
|
||||
DecSpaces:= AIndentSize;
|
||||
end
|
||||
else
|
||||
begin
|
||||
StrIndent:= StringOfChar(#9, Abs(AIndentSize));
|
||||
DecSpaces:= Abs(AIndentSize)*ATabSize;
|
||||
end;
|
||||
|
||||
if ARight then
|
||||
Result:= StrIndent+Str
|
||||
else
|
||||
begin
|
||||
N:= SGetIndentChars(Str);
|
||||
StrIndent:= Copy(Str, 1, N);
|
||||
StrText:= Copy(Str, N+1, MaxInt);
|
||||
DoTabs:= Pos(#9, StrIndent)>0;
|
||||
|
||||
StrIndent:= STabsToSpaces(StrIndent, ATabSize);
|
||||
if DecSpaces>Length(StrIndent) then
|
||||
DecSpaces:= Length(StrIndent);
|
||||
Delete(StrIndent, 1, DecSpaces);
|
||||
|
||||
if DoTabs then
|
||||
StrIndent:= SSpacesToTabs(StrIndent, ATabSize);
|
||||
Result:= StrIndent+StrText;
|
||||
end;
|
||||
end;
|
||||
|
||||
function SRemoveAsciiControlChars(const S: atString): atString;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= S;
|
||||
for i:= 1 to Length(Result) do
|
||||
if IsCharAsciiControl(Result[i]) then
|
||||
Result[i]:= '.';
|
||||
end;
|
||||
|
||||
function SRemoveHexChars(const S: atString): atString;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= S;
|
||||
for i:= 1 to Length(Result) do
|
||||
if IsCharHex(Result[i]) then
|
||||
Result[i]:= '?';
|
||||
end;
|
||||
|
||||
function SRemoveNewlineChars(const S: atString): atString;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= S;
|
||||
for i:= 1 to Length(Result) do
|
||||
if IsCharEol(Result[i]) then
|
||||
Result[i]:= ' ';
|
||||
end;
|
||||
|
||||
|
||||
{
|
||||
http://unicode.org/reports/tr9/#Directional_Formatting_Characters
|
||||
Implicit Directional Formatting Characters LRM, RLM, ALM
|
||||
Explicit Directional Embedding and Override Formatting Characters LRE, RLE, LRO, RLO, PDF
|
||||
Explicit Directional Isolate Formatting Characters LRI, RLI, FSI, PDI
|
||||
}
|
||||
const
|
||||
cDirCodes: UnicodeString =
|
||||
#$202A {LRE} + #$202B {RLE} + #$202D {LRO} + #$202E {RLO} + #$202C {PDF} +
|
||||
#$2066 {LRI} + #$2067 {RLI} + #$2068 {FSI} + #$2069 {PDI} +
|
||||
#$200E {LRM} + #$200F {RLM} + #$061C {ALM};
|
||||
|
||||
procedure _InitCharsHex;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
OptHexCharsDefault:= '';
|
||||
|
||||
for i:= 0 to 31 do
|
||||
if (i<>13) and (i<>10) and (i<>9) then
|
||||
OptHexCharsDefault:= OptHexCharsDefault+Chr(i);
|
||||
|
||||
OptHexCharsDefault:= OptHexCharsDefault + cDirCodes;
|
||||
end;
|
||||
|
||||
|
||||
function STrimRight(const S: atString): atString;
|
||||
var
|
||||
N: integer;
|
||||
begin
|
||||
N:= Length(S);
|
||||
while (N>0) and (S[N]=' ') do Dec(N);
|
||||
Result:= Copy(S, 1, N);
|
||||
end;
|
||||
|
||||
function SBeginsWith(const S, SubStr: atString): boolean;
|
||||
begin
|
||||
Result:= (SubStr<>'') and (Copy(S, 1, Length(SubStr))=SubStr);
|
||||
end;
|
||||
|
||||
function SBeginsWith(const S, SubStr: string): boolean;
|
||||
begin
|
||||
Result:= (SubStr<>'') and (Copy(S, 1, Length(SubStr))=SubStr);
|
||||
end;
|
||||
|
||||
function SEndsWith(const S, SubStr: atString): boolean;
|
||||
begin
|
||||
Result:= (SubStr<>'') and (Length(SubStr)<=Length(S)) and
|
||||
(Copy(S, Length(S)-Length(SubStr)+1, MaxInt)=SubStr);
|
||||
end;
|
||||
|
||||
function SEndsWith(const S, SubStr: string): boolean;
|
||||
begin
|
||||
Result:= (SubStr<>'') and (Length(SubStr)<=Length(S)) and
|
||||
(Copy(S, Length(S)-Length(SubStr)+1, MaxInt)=SubStr);
|
||||
end;
|
||||
|
||||
|
||||
function SCharUpper(ch: atChar): atChar;
|
||||
begin
|
||||
Result:= UnicodeUpperCase(ch)[1];
|
||||
end;
|
||||
|
||||
function SCharLower(ch: atChar): atChar;
|
||||
begin
|
||||
Result:= UnicodeLowerCase(ch)[1];
|
||||
end;
|
||||
|
||||
|
||||
function SCaseTitle(const S, SWordChars: atString): atString;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= S;
|
||||
for i:= 1 to Length(Result) do
|
||||
if (i=1) or not IsCharWord(S[i-1], SWordChars) then
|
||||
Result[i]:= SCharUpper(Result[i])
|
||||
else
|
||||
Result[i]:= SCharLower(Result[i]);
|
||||
end;
|
||||
|
||||
function SCaseInvert(const S: atString): atString;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= S;
|
||||
for i:= 1 to Length(Result) do
|
||||
if S[i]<>SCharUpper(S[i]) then
|
||||
Result[i]:= SCharUpper(Result[i])
|
||||
else
|
||||
Result[i]:= SCharLower(Result[i]);
|
||||
end;
|
||||
|
||||
function SCaseSentence(const S, SWordChars: atString): atString;
|
||||
var
|
||||
dot: boolean;
|
||||
i: Integer;
|
||||
begin
|
||||
Result:= S;
|
||||
dot:= True;
|
||||
for i:= 1 to Length(Result) do
|
||||
begin
|
||||
if IsCharWord(Result[i], SWordChars) then
|
||||
begin
|
||||
if dot then
|
||||
Result[i]:= SCharUpper(Result[i])
|
||||
else
|
||||
Result[i]:= SCharLower(Result[i]);
|
||||
dot:= False;
|
||||
end
|
||||
else
|
||||
if (Result[i] = '.') or (Result[i] = '!') or (Result[i] = '?') then
|
||||
dot:= True;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function SDecodeRecords(const S: UnicodeString; const Decode: array of TATDecodeRec): UnicodeString;
|
||||
var
|
||||
i, j: Integer;
|
||||
DoDecode: Boolean;
|
||||
begin
|
||||
Result := '';
|
||||
i := 1;
|
||||
repeat
|
||||
if i > Length(S) then Break;
|
||||
DoDecode := False;
|
||||
for j := Low(Decode) to High(Decode) do
|
||||
with Decode[j] do
|
||||
if SFrom = Copy(S, i, Length(SFrom)) then
|
||||
begin
|
||||
DoDecode := True;
|
||||
Result := Result + STo;
|
||||
Inc(i, Length(SFrom));
|
||||
Break
|
||||
end;
|
||||
if DoDecode then Continue;
|
||||
Result := Result + S[i];
|
||||
Inc(i);
|
||||
until False;
|
||||
end;
|
||||
|
||||
|
||||
function SCommentLineAction(L: TStringList;
|
||||
const AComment: atString; Act: TATCommentAction): boolean;
|
||||
var
|
||||
Str, Str0: atString;
|
||||
IndentThis, IndentAll, i: integer;
|
||||
IsCmtThis, IsCmtAll: boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
if L.Count=0 then exit;
|
||||
|
||||
IndentAll:= MaxInt;
|
||||
for i:= 0 to L.Count-1 do
|
||||
IndentAll:= Min(IndentAll, SGetIndentChars(L[i])+1);
|
||||
//no need Utf8decode
|
||||
|
||||
for i:= 0 to L.Count-1 do
|
||||
begin
|
||||
Str:= Utf8Decode(L[i]);
|
||||
Str0:= Str;
|
||||
|
||||
//IndentThis, IsCmtThis: regarding indent if this line
|
||||
//IndentAll, IsCmtAll: regarding minimal indent of block
|
||||
IndentThis:= SGetIndentChars(Str)+1;
|
||||
IsCmtThis:= Copy(Str, IndentThis, Length(AComment))=AComment;
|
||||
IsCmtAll:= Copy(Str, IndentAll, Length(AComment))=AComment;
|
||||
|
||||
case Act of
|
||||
cCommentAdd_AtNonspace:
|
||||
begin
|
||||
Insert(AComment, Str, IndentAll);
|
||||
end;
|
||||
cCommentAdd_AtNonespace_IfNone:
|
||||
begin
|
||||
if not IsCmtAll then
|
||||
Insert(AComment, Str, IndentAll);
|
||||
end;
|
||||
cCommentAdd_AtStart:
|
||||
begin
|
||||
Insert(AComment, Str, 1);
|
||||
end;
|
||||
cCommentAdd_AtStart_IfNone:
|
||||
begin
|
||||
if not IsCmtAll then
|
||||
Insert(AComment, Str, 1);
|
||||
end;
|
||||
cCommentRemove:
|
||||
begin
|
||||
if IsCmtAll then
|
||||
Delete(Str, IndentAll, Length(AComment))
|
||||
else
|
||||
if IsCmtThis then
|
||||
Delete(Str, IndentThis, Length(AComment))
|
||||
end;
|
||||
cCommentToggle_AtNonspace:
|
||||
begin
|
||||
if IsCmtAll then
|
||||
Delete(Str, IndentAll, Length(AComment))
|
||||
else
|
||||
Insert(AComment, Str, IndentAll);
|
||||
end;
|
||||
cCommentToggle_AtStart:
|
||||
begin
|
||||
if IsCmtAll then
|
||||
Delete(Str, IndentAll, Length(AComment))
|
||||
else
|
||||
Insert(AComment, Str, 1);
|
||||
end;
|
||||
end;
|
||||
|
||||
if Str<>Str0 then
|
||||
begin
|
||||
Result:= true; //modified
|
||||
L[i]:= Utf8Encode(Str);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure SReplaceAll(var s: string; const SFrom, STo: string);
|
||||
begin
|
||||
S:= StringReplace(S, SFrom, STo, [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
procedure SReplaceAllPercentChars(var S: string);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
for i:= $20 to $2F do
|
||||
SReplaceAll(S, '%'+IntToHex(i, 2), Chr(i));
|
||||
|
||||
i:= $7C;
|
||||
SReplaceAll(S, '%'+IntToHex(i, 2), Chr(i));
|
||||
end;
|
||||
|
||||
procedure SDeleteFrom(var s: string; const SFrom: string);
|
||||
var
|
||||
n: integer;
|
||||
begin
|
||||
n:= Pos(SFrom, S);
|
||||
if n>0 then
|
||||
Delete(S, n, MaxInt);
|
||||
end;
|
||||
|
||||
procedure SAddStringToHistory(const S: string; List: TStrings; MaxItems: integer);
|
||||
var
|
||||
n: integer;
|
||||
begin
|
||||
if s<>'' then
|
||||
begin
|
||||
n:= List.IndexOf(s);
|
||||
if n>=0 then
|
||||
List.Delete(n);
|
||||
List.Insert(0, s);
|
||||
end;
|
||||
|
||||
while List.Count>MaxItems do
|
||||
List.Delete(List.Count-1);
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
_InitCharsHex;
|
||||
|
||||
end.
|
||||
|
||||
81
ATSynEdit/atsynedit/atstringproc_htmlcolor.pas
Normal file
@@ -0,0 +1,81 @@
|
||||
unit ATStringProc_HtmlColor;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics;
|
||||
|
||||
//convert TColor -> HTML color string #rrggbb
|
||||
function SColorToHtmlColor(Color: TColor): string;
|
||||
//convert string which starts with HTML color token #rgb, #rrggbb -> TColor, get len of color-string
|
||||
function SHtmlColorToColor(s: string; out Len: integer; Default: TColor): TColor;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
function IsCharWord(ch: char): boolean;
|
||||
begin
|
||||
Result:= ch in ['a'..'z', 'A'..'Z', '_', '0'..'9'];
|
||||
end;
|
||||
|
||||
function IsCharHex(ch: char): boolean;
|
||||
begin
|
||||
Result:= ch in ['0'..'9', 'a'..'f', 'A'..'F'];
|
||||
end;
|
||||
|
||||
function SColorToHtmlColor(Color: TColor): string;
|
||||
var
|
||||
N: Longint;
|
||||
begin
|
||||
if Color=clNone then
|
||||
begin Result:= ''; exit end;
|
||||
N:= ColorToRGB(Color);
|
||||
Result:= '#'+
|
||||
IntToHex(Red(N), 2)+
|
||||
IntToHex(Green(N), 2)+
|
||||
IntToHex(Blue(N), 2);
|
||||
end;
|
||||
|
||||
function SHtmlColorToColor(s: string; out Len: integer; Default: TColor): TColor;
|
||||
var
|
||||
N1, N2, N3: integer;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= Default;
|
||||
Len:= 0;
|
||||
if (s<>'') and (s[1]='#') then Delete(s, 1, 1);
|
||||
if (s='') then exit;
|
||||
|
||||
//delete after first nonword char
|
||||
i:= 1;
|
||||
while (i<=Length(s)) and IsCharWord(s[i]) do Inc(i);
|
||||
Delete(s, i, Maxint);
|
||||
|
||||
//allow only #rgb, #rrggbb
|
||||
Len:= Length(s);
|
||||
if (Len<>3) and (Len<>6) then exit;
|
||||
|
||||
for i:= 1 to Len do
|
||||
if not IsCharHex(s[i]) then exit;
|
||||
|
||||
if Len=6 then
|
||||
begin
|
||||
N1:= StrToInt('$'+Copy(s, 1, 2));
|
||||
N2:= StrToInt('$'+Copy(s, 3, 2));
|
||||
N3:= StrToInt('$'+Copy(s, 5, 2));
|
||||
end
|
||||
else
|
||||
begin
|
||||
N1:= StrToInt('$'+s[1]+s[1]);
|
||||
N2:= StrToInt('$'+s[2]+s[2]);
|
||||
N3:= StrToInt('$'+s[3]+s[3]);
|
||||
end;
|
||||
|
||||
Result:= RGBToColor(N1, N2, N3);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
204
ATSynEdit/atsynedit/atstringproc_textbuffer.pas
Normal file
@@ -0,0 +1,204 @@
|
||||
unit ATStringProc_TextBuffer;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils,
|
||||
Dialogs,
|
||||
ATStringProc;
|
||||
|
||||
type
|
||||
TTextChangedEvent = procedure(Sender: TObject; Pos, Count, LineChange: integer) of object;
|
||||
|
||||
type
|
||||
{ TATStringBuffer }
|
||||
|
||||
TATStringBuffer = class
|
||||
private
|
||||
FStarts: TList; //contains offsets of lines
|
||||
FLenEol: integer;
|
||||
FOnChange: TTextChangedEvent;
|
||||
public
|
||||
FText: atString;
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
procedure Setup(const AText: atString; ALineLens: TList; ALenEol: integer);
|
||||
procedure SetupSlow(const AText: atString);
|
||||
procedure Clear;
|
||||
function CaretToStr(APnt: TPoint): integer;
|
||||
function StrToCaret(APos: integer): TPoint;
|
||||
function SubString(AFrom, ALen: integer): atString;
|
||||
function TextLength: integer;
|
||||
function LineIndex(N: integer): integer;
|
||||
function LineLength(N: integer): integer;
|
||||
function LineSpace(N: integer): integer;
|
||||
function Count: integer;
|
||||
property OnChange: TTextChangedEvent read FOnChange write FOnChange;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
const
|
||||
cInitListCapacity = 10000;
|
||||
|
||||
{ TATStringBuffer }
|
||||
|
||||
constructor TATStringBuffer.Create;
|
||||
begin
|
||||
FText:= '';
|
||||
FStarts:= TList.Create;
|
||||
FStarts.Capacity:= cInitListCapacity;
|
||||
FLenEol:= 1;
|
||||
end;
|
||||
|
||||
destructor TATStringBuffer.Destroy;
|
||||
begin
|
||||
FStarts.Clear;
|
||||
FreeAndNil(FStarts);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TATStringBuffer.Setup(const AText: atString; ALineLens: TList;
|
||||
ALenEol: integer);
|
||||
var
|
||||
Pos, NLen, i: integer;
|
||||
begin
|
||||
FText:= AText;
|
||||
FLenEol:= ALenEol;
|
||||
|
||||
FStarts.Clear;
|
||||
Pos:= 0;
|
||||
FStarts.Add(nil);
|
||||
for i:= 0 to ALineLens.Count-1 do
|
||||
begin
|
||||
NLen:= integer(ALineLens[i]);
|
||||
Inc(Pos, NLen+FLenEol);
|
||||
FStarts.Add(pointer(Pos));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStringBuffer.SetupSlow(const AText: atString);
|
||||
var
|
||||
STextFinal: atString;
|
||||
L: TStringList;
|
||||
Lens: TList;
|
||||
i: integer;
|
||||
begin
|
||||
if Trim(AText)='' then
|
||||
begin
|
||||
FText:= '';
|
||||
FStarts.Clear;
|
||||
Exit
|
||||
end;
|
||||
|
||||
L:= TStringList.Create;
|
||||
Lens:= TList.Create;
|
||||
try
|
||||
L.TextLineBreakStyle:= tlbsLF;
|
||||
L.Text:= UTF8Encode(AText);
|
||||
STextFinal:= UTF8Decode(L.Text); //this converts eol to LF
|
||||
for i:= 0 to L.Count-1 do
|
||||
Lens.Add(pointer(Length(UTF8Decode(L[i]))));
|
||||
Setup(STextFinal, Lens, 1);
|
||||
finally
|
||||
FreeAndNil(Lens);
|
||||
FreeAndNil(L);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStringBuffer.Clear;
|
||||
begin
|
||||
FText:= '';
|
||||
FStarts.Clear;
|
||||
end;
|
||||
|
||||
function TATStringBuffer.CaretToStr(APnt: TPoint): integer;
|
||||
var
|
||||
Len: integer;
|
||||
begin
|
||||
Result:= -1;
|
||||
if (APnt.Y<0) then Exit;
|
||||
if (APnt.X<0) then Exit;
|
||||
if (APnt.Y>=FStarts.Count) then Exit;
|
||||
|
||||
//handle caret pos after eol
|
||||
Len:= LineLength(APnt.Y);
|
||||
if APnt.X>Len then
|
||||
APnt.X:= Len;
|
||||
|
||||
Result:= integer(FStarts[APnt.Y])+APnt.X;
|
||||
end;
|
||||
|
||||
function TATStringBuffer.StrToCaret(APos: integer): TPoint;
|
||||
var
|
||||
a, b, m, dif: integer;
|
||||
begin
|
||||
Result.Y:= -1;
|
||||
Result.X:= 0;
|
||||
if APos<=0 then
|
||||
begin Result.Y:= 0; Exit end;
|
||||
|
||||
a:= 0;
|
||||
b:= FStarts.Count-1;
|
||||
if b<0 then Exit;
|
||||
|
||||
repeat
|
||||
dif:= integer(FStarts[a])-APos;
|
||||
if dif=0 then begin m:= a; Break end;
|
||||
|
||||
//middle, which is near b if not exact middle
|
||||
m:= (a+b+1) div 2;
|
||||
|
||||
dif:= integer(FStarts[m])-APos;
|
||||
if dif=0 then Break;
|
||||
|
||||
if Abs(a-b)<=1 then begin m:= a; Break end;
|
||||
if dif>0 then b:= m else a:= m;
|
||||
until false;
|
||||
|
||||
Result.Y:= m;
|
||||
Result.X:= APos-integer(FStarts[Result.Y]);
|
||||
end;
|
||||
|
||||
function TATStringBuffer.SubString(AFrom, ALen: integer): atString;
|
||||
begin
|
||||
Result:= Copy(FText, AFrom, ALen);
|
||||
end;
|
||||
|
||||
function TATStringBuffer.TextLength: integer;
|
||||
begin
|
||||
Result:= Length(FText);
|
||||
end;
|
||||
|
||||
function TATStringBuffer.LineIndex(N: integer): integer;
|
||||
begin
|
||||
if N<0 then Result:= 0
|
||||
else
|
||||
if N>=FStarts.Count then Result:= TextLength-1
|
||||
else
|
||||
Result:= integer(FStarts[N]);
|
||||
end;
|
||||
|
||||
function TATStringBuffer.LineLength(N: integer): integer;
|
||||
begin
|
||||
if N<0 then Result:= 0
|
||||
else
|
||||
if N>=FStarts.Count-1 then Result:= 0
|
||||
else
|
||||
Result:= integer(FStarts[N+1])-integer(FStarts[N])-FLenEol;
|
||||
end;
|
||||
|
||||
function TATStringBuffer.LineSpace(N: integer): integer;
|
||||
begin
|
||||
Result:= LineLength(N)+FLenEol;
|
||||
end;
|
||||
|
||||
function TATStringBuffer.Count: integer;
|
||||
begin
|
||||
Result:= FStarts.Count;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
68
ATSynEdit/atsynedit/atstringproc_utf8detect.pas
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
//Code by Christian Ghisler (ghisler.com)
|
||||
//Christian gave code to open-source at TCmd forum
|
||||
|
||||
unit atstringproc_utf8detect;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
//PartialAllowed must be set to true if the buffer is smaller than the file.
|
||||
function IsBufferUtf8(buf:PAnsiChar;PartialAllowed:boolean):boolean;
|
||||
|
||||
implementation
|
||||
|
||||
const bytesFromUTF8:array[AnsiChar] of byte = (
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 32
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 64
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 96
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //128
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //160
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, //192
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //224
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5); //256
|
||||
|
||||
function GetUtf8CharWidth(firstchar:AnsiChar):integer;
|
||||
begin
|
||||
result:=bytesFromUTF8[firstchar]+1;
|
||||
end;
|
||||
|
||||
function IsFirstUTF8Char(thechar:AnsiChar):boolean;
|
||||
{The remaining bytes in a multi-byte sequence have 10 as their two most significant bits.}
|
||||
begin
|
||||
result:=(byte(thechar) and (128+64))<>128;
|
||||
end;
|
||||
|
||||
function IsSecondaryUTF8Char(thechar:AnsiChar):boolean;
|
||||
{The remaining bytes in a multi-byte sequence have 10 as their two most significant bits.}
|
||||
begin
|
||||
result:=(byte(thechar) and (128+64))=128;
|
||||
end;
|
||||
|
||||
function IsBufferUtf8(buf:PAnsiChar;PartialAllowed:boolean):boolean;
|
||||
{Buffer contains only valid UTF-8 characters, no secondary alone,
|
||||
no primary without the correct nr of secondary}
|
||||
var p:PAnsiChar;
|
||||
utf8bytes:integer;
|
||||
hadutf8bytes:boolean;
|
||||
begin
|
||||
p:=buf;
|
||||
hadutf8bytes:=false;
|
||||
result:=false;
|
||||
utf8bytes:=0;
|
||||
while p[0]<>#0 do begin
|
||||
if utf8bytes>0 then begin {Expecting secondary AnsiChar}
|
||||
hadutf8bytes:=true;
|
||||
if not IsSecondaryUTF8Char(p[0]) then exit; {Fail!}
|
||||
dec(utf8bytes);
|
||||
end else if IsFirstUTF8Char(p[0]) then
|
||||
utf8bytes:=GetUtf8CharWidth(p[0])-1
|
||||
else if IsSecondaryUTF8Char(p[0]) then
|
||||
exit; {Fail!}
|
||||
inc(p);
|
||||
end;
|
||||
result:=hadutf8bytes and (PartialAllowed or (utf8bytes=0));
|
||||
end;
|
||||
|
||||
end.
|
||||
105
ATSynEdit/atsynedit/atstringproc_wordjump.pas
Normal file
@@ -0,0 +1,105 @@
|
||||
unit ATStringProc_WordJump;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils,
|
||||
ATStringProc;
|
||||
|
||||
function SFindWordOffset(const S: atString; AOffset: integer; ANext, ABigJump: boolean;
|
||||
const AWordChars: atString): integer;
|
||||
procedure SFindWordBounds(const S: atString; AOffset: integer; out AOffset1, AOffset2: integer;
|
||||
const AWordChars: atString);
|
||||
|
||||
implementation
|
||||
|
||||
const
|
||||
//no EOL here, we jump only inside line
|
||||
cCharsSp: atString = ' '#9;
|
||||
//no chars '@' (email) and '$' (used in php)
|
||||
cCharsSymb: atString = '!"#%&''()[]{}<>*+-/=,.:;?\^`|~‚„…‹›‘’“”–—¦«»±';
|
||||
|
||||
|
||||
type
|
||||
TCharGr = (cgSp, cgSymb, cgWord);
|
||||
|
||||
function SCharGr(ch: atChar; const AWordChars: atString): TCharGr;
|
||||
begin
|
||||
if (AWordChars<>'') and (Pos(ch, AWordChars)>0) then Result:= cgWord else
|
||||
if Pos(ch, cCharsSp)>0 then Result:= cgSp else
|
||||
if Pos(ch, cCharsSymb)>0 then Result:= cgSymb else
|
||||
Result:= cgWord;
|
||||
end;
|
||||
|
||||
function SFindWordOffset(const S: atString; AOffset: integer; ANext,
|
||||
ABigJump: boolean; const AWordChars: atString): integer;
|
||||
var
|
||||
n: integer;
|
||||
//------------
|
||||
procedure Next;
|
||||
var gr: TCharGr;
|
||||
begin
|
||||
if not ((n>=0) and (n<Length(s))) then Exit;
|
||||
gr:= SCharGr(s[n+1], AWordChars);
|
||||
repeat Inc(n)
|
||||
until
|
||||
(n>=Length(s)) or (SCharGr(s[n+1], AWordChars)<>gr);
|
||||
end;
|
||||
//------------
|
||||
procedure Home;
|
||||
var gr: TCharGr;
|
||||
begin
|
||||
if not ((n>0) and (n<Length(s))) then Exit;
|
||||
gr:= SCharGr(s[n+1], AWordChars);
|
||||
while (n>0) and (SCharGr(s[n], AWordChars)=gr) do
|
||||
Dec(n);
|
||||
end;
|
||||
//------------
|
||||
begin
|
||||
n:= AOffset;
|
||||
if ANext then
|
||||
begin
|
||||
Next;
|
||||
if ABigJump then
|
||||
if (n<Length(s)) and (SCharGr(s[n+1], AWordChars)= cgSp) then
|
||||
Next;
|
||||
end
|
||||
else
|
||||
begin
|
||||
//if we at word middle, jump to word start
|
||||
if (n>0) and (n<Length(s)) and (SCharGr(s[n], AWordChars)=SCharGr(s[n+1], AWordChars)) then
|
||||
Home
|
||||
else
|
||||
begin
|
||||
//jump lefter, then jump to prev word start
|
||||
if (n>0) then
|
||||
begin Dec(n); Home end;
|
||||
if ABigJump then
|
||||
if (n>0) and (SCharGr(s[n+1], AWordChars)= cgSp) then
|
||||
begin Dec(n); Home end;
|
||||
end
|
||||
end;
|
||||
Result:= n;
|
||||
end;
|
||||
|
||||
procedure SFindWordBounds(const S: atString; AOffset: integer; out AOffset1,
|
||||
AOffset2: integer; const AWordChars: atString);
|
||||
begin
|
||||
AOffset1:= AOffset;
|
||||
AOffset2:= AOffset;
|
||||
|
||||
if (AOffset>=0) and (AOffset<Length(S)) and
|
||||
IsCharWord(S[AOffset+1], AWordChars) then
|
||||
begin
|
||||
//jump left only if at middle of word
|
||||
if (AOffset>0) and IsCharWord(S[AOffset], AWordChars) then
|
||||
AOffset1:= SFindWordOffset(S, AOffset, false, false, AWordChars);
|
||||
//jump right always
|
||||
AOffset2:= SFindWordOffset(S, AOffset, true, false, AWordChars);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
1118
ATSynEdit/atsynedit/atstrings.pas
Normal file
355
ATSynEdit/atsynedit/atstrings_editing.inc
Normal file
@@ -0,0 +1,355 @@
|
||||
{$ifdef none}begin end;{$endif}
|
||||
|
||||
procedure TATStrings.TextInsert(AX, AY: integer; const AText: atString; AOverwrite: boolean; out AShift, APosAfter: TPoint);
|
||||
var
|
||||
Str, StrLead, StrTail: atString;
|
||||
List: TATStrings;
|
||||
bWithEol, bInsertAtEnd: boolean;
|
||||
begin
|
||||
AShift.X:= 0;
|
||||
AShift.Y:= 0;
|
||||
APosAfter.X:= AX;
|
||||
APosAfter.Y:= AY;
|
||||
|
||||
if not IsIndexValid(AY) then Exit;
|
||||
if AX<0 then Exit;
|
||||
if AText='' then Exit;
|
||||
|
||||
Str:= Lines[AY];
|
||||
bInsertAtEnd:= AX>=Length(Str);
|
||||
|
||||
if not bInsertAtEnd then
|
||||
begin
|
||||
StrLead:= Copy(Str, 1, AX);
|
||||
StrTail:= Copy(Str, AX+1, MaxInt);
|
||||
end
|
||||
else
|
||||
begin
|
||||
StrLead:= Str+StringOfChar(' ', AX-Length(Str));
|
||||
StrTail:= '';
|
||||
end;
|
||||
|
||||
if AOverwrite then
|
||||
Delete(StrTail, 1, Length(AText));
|
||||
|
||||
//------------------
|
||||
//Insert single line
|
||||
|
||||
if not SWithBreaks(AText) then
|
||||
begin
|
||||
Lines[AY]:= StrLead+AText+StrTail;
|
||||
if not AOverwrite then
|
||||
AShift.X:= Length(AText);
|
||||
APosAfter.X:= AX+Length(AText);
|
||||
Exit
|
||||
end;
|
||||
|
||||
//----------------------
|
||||
//Insert multi-line text
|
||||
|
||||
List:= TATStrings.Create;
|
||||
BeginUndoGroup;
|
||||
try
|
||||
List.LoadFromString(StrLead+AText);
|
||||
List.ActionDeleteFakeLine;
|
||||
if List.Count=0 then Exit;
|
||||
|
||||
if StrTail<>'' then
|
||||
Lines[AY]:= StrTail
|
||||
else
|
||||
if Lines[AY]<>'' then
|
||||
LineDelete(AY);
|
||||
|
||||
bWithEol:= SEndsWith(AText, #10) or
|
||||
SEndsWith(AText, #13) or
|
||||
bInsertAtEnd //need for (paste N lines, no final eol, at end of line)
|
||||
;
|
||||
LineInsertStrings(AY, List, bWithEol);
|
||||
|
||||
if bWithEol then
|
||||
begin
|
||||
APosAfter.X:= 0;
|
||||
APosAfter.Y:= AY+List.Count;
|
||||
end
|
||||
else
|
||||
begin
|
||||
APosAfter.X:= Length(List.Lines[List.Count-1]);
|
||||
APosAfter.Y:= AY+List.Count-1;
|
||||
end;
|
||||
AShift.Y:= APosAfter.Y-AY;
|
||||
|
||||
finally
|
||||
FreeAndNil(List);
|
||||
EndUndoGroup;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.TextDeleteLeft(AX, AY: integer; ALen: integer; out AShift,
|
||||
APosAfter: TPoint);
|
||||
var
|
||||
Str, StrPrev: atString;
|
||||
begin
|
||||
AShift.X:= 0;
|
||||
AShift.Y:= 0;
|
||||
APosAfter.X:= AX;
|
||||
APosAfter.Y:= AY;
|
||||
if not IsIndexValid(AY) then Exit;
|
||||
Str:= Lines[AY];
|
||||
|
||||
//handle spec case: caret on last fake line, BkSp pressed:
|
||||
//delete fake line,
|
||||
//delete EOL at prev line
|
||||
if (AX=0) and (AY=Count-1) and (AY>0) and IsLastLineFake then
|
||||
begin
|
||||
LineDelete(AY, false);
|
||||
LinesEnds[AY-1]:= cEndNone;
|
||||
AShift.Y:= -1;
|
||||
APosAfter.X:= Length(Lines[AY-1]);
|
||||
APosAfter.Y:= AY-1;
|
||||
exit
|
||||
end;
|
||||
|
||||
if AX>0 then
|
||||
begin
|
||||
if AX<=Length(Str) then
|
||||
begin
|
||||
System.Delete(Str, Max(1, AX+1-ALen), ALen);
|
||||
Lines[AY]:= Str;
|
||||
end;
|
||||
AShift.X:= -Min(AX, ALen);
|
||||
APosAfter.X:= Max(0, AX-ALen);
|
||||
end
|
||||
else
|
||||
if AY>0 then
|
||||
begin
|
||||
StrPrev:= Lines[AY-1];
|
||||
Lines[AY-1]:= StrPrev+Str;
|
||||
LineDelete(AY);
|
||||
AShift.Y:= -1;
|
||||
APosAfter.X:= Length(StrPrev);
|
||||
APosAfter.Y:= AY-1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.TextDeleteRight(AX, AY: integer; ALen: integer; out AShift,
|
||||
APosAfter: TPoint; ACanDelEol: boolean = true);
|
||||
var
|
||||
Str: atString;
|
||||
DelEol: boolean;
|
||||
begin
|
||||
AShift.X:= 0;
|
||||
AShift.Y:= 0;
|
||||
APosAfter.X:= AX;
|
||||
APosAfter.Y:= AY;
|
||||
if not IsIndexValid(AY) then Exit;
|
||||
Str:= Lines[AY];
|
||||
|
||||
//special case: last fake line
|
||||
if (AY=Count-1) and (Str='') and (LinesEnds[AY]=cEndNone) then
|
||||
Exit;
|
||||
|
||||
DelEol:= false;
|
||||
if AX<Length(Str) then
|
||||
begin
|
||||
System.Delete(Str, AX+1, ALen);
|
||||
Lines[AY]:= Str;
|
||||
AShift.X:= -ALen;
|
||||
end
|
||||
else
|
||||
DelEol:= ACanDelEol;
|
||||
|
||||
if DelEol then
|
||||
if Str='' then //handle for simpler line-states
|
||||
begin
|
||||
AShift.Y:= -1;
|
||||
if (AY>0) and (AY=Count-1) then
|
||||
begin
|
||||
APosAfter.X:= 0;
|
||||
APosAfter.Y:= AY-1;
|
||||
end;
|
||||
LineDelete(AY);
|
||||
end
|
||||
else
|
||||
begin
|
||||
//add spaces if we are after eol
|
||||
if AX>=Length(Str) then
|
||||
Str:= Str+StringOfChar(' ', AX-Length(Str));
|
||||
|
||||
//not last: del next line
|
||||
if AY+1<Count then
|
||||
begin
|
||||
Lines[AY]:= Str+Lines[AY+1];
|
||||
LineDelete(AY+1, false{not force});
|
||||
//maybe also eol
|
||||
if AY=Count-1 then
|
||||
LinesEnds[AY]:= cEndNone;
|
||||
end
|
||||
else
|
||||
//last line: del eol
|
||||
LinesEnds[AY]:= cEndNone;
|
||||
|
||||
AShift.Y:= -1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.TextDeleteRange(AFromX, AFromY, AToX, AToY: integer;
|
||||
out AShift, APosAfter: TPoint);
|
||||
var
|
||||
Str: atString;
|
||||
bDelEmpty: boolean;
|
||||
bNoEol: boolean;
|
||||
i: integer;
|
||||
begin
|
||||
AShift.X:= 0;
|
||||
AShift.Y:= 0;
|
||||
APosAfter.X:= AFromX;
|
||||
APosAfter.Y:= AFromY;
|
||||
if not IsIndexValid(AFromY) then Exit;
|
||||
if not IsIndexValid(AToY) then Exit;
|
||||
if (AFromX=AToX) and (AFromY=AToY) then Exit;
|
||||
if (AFromY>AToY) then Exit;
|
||||
|
||||
if AFromY=AToY then
|
||||
begin
|
||||
//delete range in one line
|
||||
Str:= Lines[AFromY];
|
||||
Delete(Str, AFromX+1, AToX-AFromX);
|
||||
Lines[AFromY]:= Str;
|
||||
|
||||
AShift.X:= -(AToX-AFromX);
|
||||
end
|
||||
else
|
||||
begin
|
||||
bDelEmpty:= false;
|
||||
//correct AToX/AToY to not del extra empty line
|
||||
if (AToX=0) and (Lines[AToY]='') then //for empty last line
|
||||
begin
|
||||
AToY:= Max(0, AToY-1);
|
||||
AToX:= Length(Lines[AToY]);
|
||||
bDelEmpty:= true;
|
||||
end;
|
||||
|
||||
//remember no final Eol
|
||||
bNoEol:= (AToY=Count-1) and (LinesEnds[AToY]=cEndNone);
|
||||
|
||||
//place ramaining parts of 1st+last lines
|
||||
Str:= Copy(Lines[AFromY], 1, AFromX) + Copy(Lines[AToY], AToX+1, MaxInt);
|
||||
Lines[AFromY]:= Str;
|
||||
|
||||
//del middle lines
|
||||
for i:= AToY downto AFromY+1 do
|
||||
LineDelete(i);
|
||||
|
||||
//del empty line?
|
||||
if bDelEmpty then
|
||||
if Str='' then
|
||||
LineDelete(AFromY);
|
||||
|
||||
if bNoEol then
|
||||
begin
|
||||
ActionDeleteFakeLine;
|
||||
if Count>0 then
|
||||
LinesEnds[Count-1]:= cEndNone;
|
||||
end;
|
||||
|
||||
AShift.Y:= -(AToY-AFromY);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATStrings.TextInsertColumnBlock(AX, AY: integer; ABlock: TATStrings; AOverwrite: boolean);
|
||||
var
|
||||
Shift, PosAfter: TPoint;
|
||||
i: integer;
|
||||
begin
|
||||
for i:= 0 to ABlock.Count-1 do
|
||||
begin
|
||||
TextInsert(AX, AY+i, ABlock.Lines[i], AOverwrite, Shift, PosAfter);
|
||||
LinesEnds[AY+i]:= Endings; //force eol
|
||||
if not IsIndexValid(AY+i+1) then
|
||||
LineAddRaw('', cEndNone);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.TextInsertEol(AX, AY: integer; AKeepCaret: boolean; const AStrIndent: atString; out AShift, APosAfter: TPoint);
|
||||
var
|
||||
Str, StrMove: atString;
|
||||
NewEnd: TATLineEnds;
|
||||
begin
|
||||
AShift.X:= 0;
|
||||
AShift.Y:= 0;
|
||||
APosAfter.X:= AX;
|
||||
APosAfter.Y:= AY;
|
||||
if not IsIndexValid(AY) then Exit;
|
||||
|
||||
Str:= Lines[AY];
|
||||
StrMove:= '';
|
||||
|
||||
//special case AX=0: just insert empty line
|
||||
//(less changes in undo)
|
||||
if AX=0 then
|
||||
begin
|
||||
LineInsertRaw(AY, '', Endings);
|
||||
end
|
||||
else
|
||||
begin
|
||||
BeginUndoGroup;
|
||||
|
||||
if (AX<Length(Str)) then
|
||||
begin
|
||||
StrMove:= Copy(Str, AX+1, MaxInt);
|
||||
Delete(Str, AX+1, MaxInt);
|
||||
Lines[AY]:= Str;
|
||||
end;
|
||||
|
||||
//handle situation when we at non-eol line, this must give
|
||||
//inserted line also w/o eol
|
||||
NewEnd:= LinesEnds[AY];
|
||||
LinesEnds[AY]:= Endings; //force eol to cur line
|
||||
LineInsertRaw(AY+1, AStrIndent+StrMove, NewEnd);
|
||||
|
||||
EndUndoGroup;
|
||||
end;
|
||||
|
||||
if not AKeepCaret then
|
||||
begin
|
||||
APosAfter.X:= Length(AStrIndent);
|
||||
APosAfter.Y:= AY+1;
|
||||
AShift.Y:= 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.TextDeleteLine(AX, AY: integer; out AShift, APosAfter: TPoint);
|
||||
begin
|
||||
AShift.X:= 0;
|
||||
AShift.Y:= 0;
|
||||
APosAfter.X:= AX;
|
||||
APosAfter.Y:= AY;
|
||||
if not IsIndexValid(AY) then Exit;
|
||||
|
||||
AShift.Y:= -1;
|
||||
APosAfter.X:= 0;
|
||||
|
||||
LineDelete(AY);
|
||||
if AY>=Count then
|
||||
LineAddEx('', cEndNone);
|
||||
end;
|
||||
|
||||
procedure TATStrings.TextDuplicateLine(AX, AY: integer; out AShift, APosAfter: TPoint);
|
||||
begin
|
||||
AShift.X:= 0;
|
||||
AShift.Y:= 0;
|
||||
APosAfter.X:= AX;
|
||||
APosAfter.Y:= AY;
|
||||
if not IsIndexValid(AY) then Exit;
|
||||
|
||||
LineInsert(AY+1, Lines[AY]);
|
||||
|
||||
if LinesEnds[AY]<>Endings then
|
||||
LinesEnds[AY]:= Endings;
|
||||
LinesEnds[AY+1]:= Endings;
|
||||
|
||||
AShift.Y:= 1;
|
||||
end;
|
||||
|
||||
|
||||
299
ATSynEdit/atsynedit/atstrings_load.inc
Normal file
@@ -0,0 +1,299 @@
|
||||
{$ifdef nn}begin end;{$endif}
|
||||
|
||||
function IsStreamWithSignature(Stream: TStream; const Sign: AnsiString): boolean;
|
||||
var
|
||||
Buf: AnsiString;
|
||||
begin
|
||||
Result:= false;
|
||||
if Stream.Size<Length(Sign) then Exit;
|
||||
SetLength(Buf, Length(Sign));
|
||||
Stream.Position:= 0;
|
||||
Stream.ReadBuffer(Buf[1], Length(Sign));
|
||||
Stream.Position:= 0;
|
||||
Result:= Buf=Sign;
|
||||
end;
|
||||
|
||||
function IsStreamWithUt8NoBom(Stream: TStream; BufSizeKb: word): boolean;
|
||||
const
|
||||
cMinLen = 15;
|
||||
var
|
||||
Buf: PChar;
|
||||
Size: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
if Stream.Size<cMinLen then Exit;
|
||||
|
||||
if BufSizeKb=0 then BufSizeKb:= 1;
|
||||
Size:= BufSizeKb*1024;
|
||||
GetMem(Buf, Size);
|
||||
try
|
||||
FillChar(Buf^, Size, 0);
|
||||
Stream.Position:= 0;
|
||||
Stream.Read(Buf^, Size-1{trail zero});
|
||||
Stream.Position:= 0;
|
||||
Result:= IsBufferUtf8(Buf, true);
|
||||
finally
|
||||
FreeMem(Buf);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DoDetectStreamEncoding(Stream: TStream;
|
||||
out Enc: TATFileEncoding;
|
||||
out SignLen: integer;
|
||||
out EncWithBom: boolean;
|
||||
BufSizeKb: integer);
|
||||
begin
|
||||
Enc:= cEncAnsi;
|
||||
EncWithBom:= true;
|
||||
SignLen:= 0;
|
||||
|
||||
if IsStreamWithSignature(Stream, cSignUTF8) then
|
||||
begin
|
||||
Enc:= cEncUTF8;
|
||||
SignLen:= Length(cSignUTF8);
|
||||
Exit
|
||||
end;
|
||||
|
||||
if IsStreamWithSignature(Stream, cSignWideLE) then
|
||||
begin
|
||||
Enc:= cEncWideLE;
|
||||
SignLen:= Length(cSignWideLE);
|
||||
Exit
|
||||
end;
|
||||
|
||||
if IsStreamWithSignature(Stream, cSignWideBE) then
|
||||
begin
|
||||
Enc:= cEncWideBE;
|
||||
SignLen:= Length(cSignWideBE);
|
||||
Exit
|
||||
end;
|
||||
|
||||
if IsStreamWithUt8NoBom(Stream, BufSizeKb) then
|
||||
begin
|
||||
Enc:= cEncUTF8;
|
||||
EncWithBom:= false;
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATStrings.LoadFromString(const AText: atString);
|
||||
var
|
||||
MS: TMemoryStream;
|
||||
i: integer;
|
||||
begin
|
||||
if ReadOnly then exit;
|
||||
Clear;
|
||||
if AText='' then
|
||||
begin
|
||||
ActionAddFakeLineIfNeeded;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
MS:= TMemoryStream.Create;
|
||||
try
|
||||
MS.Write(AText[1], Length(AText)*SizeOf(atChar));
|
||||
MS.Position:= 0;
|
||||
|
||||
Encoding:= cEncWideLE;
|
||||
EncodingDetect:= false;
|
||||
LoadFromStream(MS);
|
||||
ActionAddFakeLineIfNeeded;
|
||||
finally
|
||||
FreeAndNil(MS);
|
||||
end;
|
||||
|
||||
Modified:= true;
|
||||
for i:= 0 to Count-1 do
|
||||
SetLineState(i, cLineStateAdded);
|
||||
end;
|
||||
|
||||
procedure TATStrings.DoLoadFromStream(Stream: TStream);
|
||||
var
|
||||
Buf: PAnsiChar;
|
||||
BufSize: int64;
|
||||
CharSize: integer;
|
||||
|
||||
function _BufferCharCode(NPos: integer): Word;
|
||||
begin
|
||||
case FEncoding of
|
||||
cEncAnsi,
|
||||
cEncUTF8:
|
||||
Result:= PByte(Buf)[NPos];
|
||||
cEncWideLE:
|
||||
Result:= PByte(Buf)[NPos] + $100 * PByte(Buf)[NPos+1];
|
||||
cEncWideBE:
|
||||
Result:= PByte(Buf)[NPos+1] + $100 * PByte(Buf)[NPos];
|
||||
else
|
||||
DoEncError;
|
||||
end;
|
||||
end;
|
||||
|
||||
function _FindNextEol(NPos: integer): integer;
|
||||
begin
|
||||
Result:= NPos;
|
||||
while (Result<BufSize) and not IsCharEol(Widechar(_BufferCharCode(Result))) do
|
||||
Inc(Result, CharSize);
|
||||
end;
|
||||
|
||||
var
|
||||
NStart, NEnd, Len: integer;
|
||||
SA: AnsiString;
|
||||
SW: UnicodeString;
|
||||
LineEnd: TATLineEnds;
|
||||
bWithBom, bEncoded: boolean;
|
||||
NPercents: integer;
|
||||
begin
|
||||
Clear;
|
||||
|
||||
Len:= 0;
|
||||
if FEncodingDetect then
|
||||
begin
|
||||
DoDetectStreamEncoding(Stream, FEncoding, Len, bWithBom, FEncodingDetectBufSizeKb);
|
||||
case FEncoding of
|
||||
cEncUTF8: SaveSignUtf8:= bWithBom;
|
||||
cEncWideBE,
|
||||
cEncWideLE: SaveSignWide:= bWithBom;
|
||||
end;
|
||||
end;
|
||||
CharSize:= cEncodingSize[FEncoding];
|
||||
|
||||
BufSize:= Stream.Size-Len;
|
||||
if BufSize<=0 then Exit;
|
||||
|
||||
GetMem(Buf, BufSize);
|
||||
try
|
||||
Stream.Position:= Len;
|
||||
Stream.ReadBuffer(Buf^, BufSize);
|
||||
|
||||
NStart:= 0;
|
||||
repeat
|
||||
NEnd:= _FindNextEol(NStart);
|
||||
Len:= NEnd-NStart;
|
||||
|
||||
if Stream.Size>=cMinSizeForProgress then
|
||||
begin
|
||||
NPercents:= Int64(NEnd)*100 div Stream.Size;
|
||||
if Abs(NPercents-FProgress)>=cMinIncForProgress then
|
||||
begin
|
||||
FProgress:= NPercents;
|
||||
if Assigned(FOnProgress) then
|
||||
FOnProgress(Self);
|
||||
end;
|
||||
end;
|
||||
|
||||
//detect+skip Eol
|
||||
LineEnd:= cEndNone;
|
||||
if (Int64(NEnd)+CharSize<BufSize) and (_BufferCharCode(NEnd)=13) and (_BufferCharCode(NEnd+CharSize)=10) then
|
||||
begin
|
||||
LineEnd:= cEndWin;
|
||||
Inc(NEnd, CharSize*2);
|
||||
end
|
||||
else
|
||||
if (NEnd<BufSize) and (_BufferCharCode(NEnd)=10) then
|
||||
begin
|
||||
LineEnd:= cEndUnix;
|
||||
Inc(NEnd, CharSize);
|
||||
end
|
||||
else
|
||||
if (NEnd<BufSize) and (_BufferCharCode(NEnd)=13) then
|
||||
begin
|
||||
LineEnd:= cEndMac;
|
||||
Inc(NEnd, CharSize);
|
||||
end
|
||||
else
|
||||
Inc(NEnd, CharSize);
|
||||
|
||||
if Len=0 then
|
||||
LineAddRaw('', LineEnd)
|
||||
else
|
||||
begin
|
||||
case FEncoding of
|
||||
cEncAnsi:
|
||||
begin
|
||||
SA:= '';
|
||||
SetLength(SA, Len);
|
||||
Move(Buf[NStart], SA[1], Len);
|
||||
|
||||
//if codepage set, convert string->utf8->UnicodeString
|
||||
//else just string->UnicodeString
|
||||
if FEncodingCodepage='' then
|
||||
SW:= SA
|
||||
else
|
||||
begin
|
||||
{$ifdef laz15}
|
||||
SA:= ConvertEncodingToUTF8(SA, FEncodingCodepage, bEncoded);
|
||||
{$else}
|
||||
SA:= ConvertEncoding(SA, FEncodingCodepage, '');
|
||||
{$endif}
|
||||
SW:= UTF8Decode(SA);
|
||||
end;
|
||||
|
||||
LineAddRaw(SW, LineEnd);
|
||||
end;
|
||||
|
||||
cEncUTF8:
|
||||
begin
|
||||
SA:= '';
|
||||
SetLength(SA, Len);
|
||||
Move(Buf[NStart], SA[1], Len);
|
||||
SW:= UTF8Decode(SA);
|
||||
LineAddRaw(SW, LineEnd);
|
||||
end;
|
||||
|
||||
cEncWideLE,
|
||||
cEncWideBE:
|
||||
begin
|
||||
SW:= '';
|
||||
SetLength(SW, Len div 2);
|
||||
Move(Buf[NStart], SW[1], Len);
|
||||
if FEncoding=cEncWideBE then
|
||||
SW:= SSwapEndian(SW);
|
||||
LineAddRaw(SW, LineEnd);
|
||||
end;
|
||||
|
||||
else
|
||||
DoEncError;
|
||||
end;
|
||||
end;
|
||||
|
||||
NStart:= NEnd;
|
||||
if (NStart>=BufSize) then Break;
|
||||
until false;
|
||||
|
||||
finally
|
||||
FreeMem(Buf);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.LoadFromStream(Stream: TStream);
|
||||
begin
|
||||
DoClearUndo(true);
|
||||
DoLoadFromStream(Stream);
|
||||
DoFinalizeLoading;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATStrings.LoadFromFile(const Filename: string);
|
||||
var
|
||||
fs: TFileStream;
|
||||
begin
|
||||
fs:= TFileStream.Create(Filename, fmOpenRead);
|
||||
try
|
||||
LoadFromStream(fs);
|
||||
finally
|
||||
FreeAndNil(fs);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.DoFinalizeLoading;
|
||||
begin
|
||||
DoDetectEndings;
|
||||
ActionAddFakeLineIfNeeded;
|
||||
DoClearLineStates(false);
|
||||
DoClearUndo;
|
||||
Modified:= false;
|
||||
FProgress:= 0;
|
||||
end;
|
||||
|
||||
|
||||
102
ATSynEdit/atsynedit/atstrings_save.inc
Normal file
@@ -0,0 +1,102 @@
|
||||
{$ifdef nn}begin end;{$endif}
|
||||
|
||||
procedure TATStrings.SaveToStream(Stream: TStream; AEncoding: TATFileEncoding; AWithSignature: boolean);
|
||||
var
|
||||
i: integer;
|
||||
Item: TATStringItem;
|
||||
SA: AnsiString;
|
||||
SW: UnicodeString;
|
||||
Sign: AnsiString;
|
||||
bEncoded: boolean;
|
||||
begin
|
||||
if AWithSignature then
|
||||
begin
|
||||
Sign:= '';
|
||||
case FEncoding of
|
||||
cEncUTF8: Sign:= cSignUTF8;
|
||||
cEncWideLE: Sign:= cSignWideLE;
|
||||
cEncWideBE: Sign:= cSignWideBE;
|
||||
end;
|
||||
if Sign<>'' then
|
||||
Stream.WriteBuffer(Sign[1], Length(Sign));
|
||||
end;
|
||||
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
Item:= TATStringItem(FList[i]);
|
||||
SW:= Item.ItemString + cLineEndStrings[Item.ItemEnd];
|
||||
if SW<>'' then
|
||||
case AEncoding of
|
||||
cEncAnsi:
|
||||
begin
|
||||
//if codepage set, convert UnicodeString->utf8->Ansistring
|
||||
//else just UnicodeString->Ansistring
|
||||
if FEncodingCodepage='' then
|
||||
SA:= SW
|
||||
else
|
||||
begin
|
||||
SA:= UTF8Encode(SW);
|
||||
{$ifdef laz15}
|
||||
SA:= ConvertEncodingFromUTF8(SA, FEncodingCodepage, bEncoded);
|
||||
{$else}
|
||||
SA:= ConvertEncoding(SA, '', FEncodingCodepage);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
Stream.WriteBuffer(SA[1], Length(SA));
|
||||
end;
|
||||
|
||||
cEncUTF8:
|
||||
begin
|
||||
SA:= UTF8Encode(SW);
|
||||
Stream.WriteBuffer(SA[1], Length(SA));
|
||||
end;
|
||||
|
||||
cEncWideLE,
|
||||
cEncWideBE:
|
||||
begin
|
||||
if AEncoding=cEncWideBE then
|
||||
SW:= SSwapEndian(SW);
|
||||
Stream.WriteBuffer(SW[1], Length(SW)*2);
|
||||
end;
|
||||
|
||||
else
|
||||
DoEncError;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATStrings.SaveToFile(const AFilename: string);
|
||||
var
|
||||
fs: TFileStream;
|
||||
WithSign: boolean;
|
||||
begin
|
||||
WithSign:=
|
||||
((FEncoding in [cEncUTF8]) and FSaveSignUtf8) or
|
||||
((FEncoding in [cEncWideLE, cEncWideBE]) and FSaveSignWide);
|
||||
|
||||
fs:= TFileStream.Create(AFilename, fmCreate or fmOpenWrite);
|
||||
try
|
||||
SaveToStream(fs, FEncoding, WithSign);
|
||||
finally
|
||||
FreeAndNil(fs);
|
||||
end;
|
||||
|
||||
DoFinalizeSaving;
|
||||
end;
|
||||
|
||||
procedure TATStrings.DoFinalizeSaving;
|
||||
begin
|
||||
DoClearLineStates(true);
|
||||
|
||||
if not FUndoAfterSave then
|
||||
DoClearUndo
|
||||
else
|
||||
begin
|
||||
FUndoList.DeleteUnmodifiedMarks;
|
||||
FRedoList.DeleteUnmodifiedMarks;
|
||||
end;
|
||||
|
||||
Modified:= false;
|
||||
end;
|
||||
|
||||
273
ATSynEdit/atsynedit/atstrings_undo.pas
Normal file
@@ -0,0 +1,273 @@
|
||||
unit ATStrings_Undo;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, StrUtils,
|
||||
ATStringProc;
|
||||
|
||||
type
|
||||
TATEditAction = (
|
||||
cEditActionChange,
|
||||
cEditActionChangeEol,
|
||||
cEditActionInsert,
|
||||
cEditActionDelete,
|
||||
cEditActionClearModified
|
||||
);
|
||||
|
||||
const
|
||||
StrEditActionDescriptions: array[TATEditAction] of string = (
|
||||
'change',
|
||||
'change-eol',
|
||||
'insert',
|
||||
'delete',
|
||||
'clear-mod'
|
||||
);
|
||||
|
||||
type
|
||||
{ TATUndoItem }
|
||||
|
||||
TATUndoItem = class
|
||||
ItemAction: TATEditAction;
|
||||
ItemIndex: integer;
|
||||
ItemText: atString;
|
||||
ItemEnd: TATLineEnds;
|
||||
ItemCarets: TATPointArray;
|
||||
ItemSoftMark: boolean;
|
||||
ItemHardMark: boolean;
|
||||
constructor Create(AAction: TATEditAction; AIndex: integer;
|
||||
const AText: atString; AEnd: TATLineEnds; ASoftMark, AHardMark: boolean;
|
||||
const ACarets: TATPointArray); virtual;
|
||||
end;
|
||||
|
||||
type
|
||||
{ TATUndoList }
|
||||
|
||||
TATUndoList = class
|
||||
private
|
||||
FList: TList;
|
||||
FMaxCount: integer;
|
||||
FLocked: boolean;
|
||||
FSoftMark: boolean;
|
||||
FHardMark: boolean;
|
||||
function GetItem(N: integer): TATUndoItem;
|
||||
public
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
function IsIndexValid(N: integer): boolean;
|
||||
function IsItemsEqual(N1, N2: integer): boolean;
|
||||
function Count: integer;
|
||||
function Last: TATUndoItem;
|
||||
property Items[N: integer]: TATUndoItem read GetItem; default;
|
||||
property MaxCount: integer read FMaxCount write FMaxCount;
|
||||
property SoftMark: boolean read FSoftMark write FSoftMark;
|
||||
property HardMark: boolean read FHardMark write FHardMark;
|
||||
property Locked: boolean read FLocked write FLocked;
|
||||
procedure Clear;
|
||||
procedure Delete(N: integer);
|
||||
procedure DeleteLast;
|
||||
procedure DeleteUnmodifiedMarks;
|
||||
procedure Add(AAction: TATEditAction; AIndex: integer; const AText: atString;
|
||||
AEnd: TATLineEnds; const ACarets: TATPointArray);
|
||||
procedure AddUnmodifiedMark;
|
||||
procedure DebugShow;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math, Dialogs;
|
||||
|
||||
{ TATUndoItem }
|
||||
|
||||
constructor TATUndoItem.Create(AAction: TATEditAction; AIndex: integer;
|
||||
const AText: atString; AEnd: TATLineEnds; ASoftMark, AHardMark: boolean;
|
||||
const ACarets: TATPointArray);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
ItemAction:= AAction;
|
||||
ItemIndex:= AIndex;
|
||||
ItemText:= AText;
|
||||
ItemEnd:= AEnd;
|
||||
ItemSoftMark:= ASoftMark;
|
||||
ItemHardMark:= AHardMark;
|
||||
|
||||
SetLength(ItemCarets, Length(ACarets));
|
||||
for i:= 0 to High(ACarets) do
|
||||
begin
|
||||
ItemCarets[i]:= ACarets[i];
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TATUndoList }
|
||||
|
||||
function TATUndoList.GetItem(N: integer): TATUndoItem;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
Result:= TATUndoItem(FList[N])
|
||||
else
|
||||
Result:= nil;
|
||||
end;
|
||||
|
||||
constructor TATUndoList.Create;
|
||||
begin
|
||||
FList:= TList.Create;
|
||||
FMaxCount:= 5000;
|
||||
FSoftMark:= false;
|
||||
FHardMark:= false;
|
||||
FLocked:= false;
|
||||
end;
|
||||
|
||||
destructor TATUndoList.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
FreeAndNil(FList);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TATUndoList.Count: integer;
|
||||
begin
|
||||
Result:= FList.Count;
|
||||
end;
|
||||
|
||||
function TATUndoList.IsIndexValid(N: integer): boolean;
|
||||
begin
|
||||
Result:= (N>=0) and (N<Count);
|
||||
end;
|
||||
|
||||
function TATUndoList.IsItemsEqual(N1, N2: integer): boolean;
|
||||
var
|
||||
i1, i2: TATUndoItem;
|
||||
begin
|
||||
Result:= false;
|
||||
i1:= Items[N1];
|
||||
i2:= Items[N2];
|
||||
if not Assigned(i1) or not Assigned(i2) then Exit;
|
||||
Result:=
|
||||
(i1.ItemAction=cEditActionChange) and
|
||||
(i1.ItemAction=i2.ItemAction) and
|
||||
(i1.ItemIndex=i2.ItemIndex) and
|
||||
(i1.ItemText=i2.ItemText);
|
||||
end;
|
||||
|
||||
procedure TATUndoList.Delete(N: integer);
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
begin
|
||||
TObject(FList[N]).Free;
|
||||
FList.Delete(N);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATUndoList.DeleteLast;
|
||||
begin
|
||||
Delete(Count-1);
|
||||
end;
|
||||
|
||||
procedure TATUndoList.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= Count-1 downto 0 do
|
||||
Delete(i);
|
||||
end;
|
||||
|
||||
procedure TATUndoList.Add(AAction: TATEditAction; AIndex: integer;
|
||||
const AText: atString; AEnd: TATLineEnds;
|
||||
const ACarets: TATPointArray);
|
||||
var
|
||||
Item: TATUndoItem;
|
||||
begin
|
||||
if FLocked then Exit;
|
||||
|
||||
//not dup change?
|
||||
if (Count>0) and (AAction in [cEditActionChange, cEditActionChangeEol]) then
|
||||
begin
|
||||
Item:= Items[Count-1];
|
||||
if (Item.ItemAction=AAction) and
|
||||
(Item.ItemIndex=AIndex) and
|
||||
(Item.ItemText=AText) then
|
||||
Exit;
|
||||
end;
|
||||
|
||||
//not insert/delete same index?
|
||||
if (Count>0) and (AAction=cEditActionDelete) then
|
||||
begin
|
||||
Item:= Items[Count-1];
|
||||
if (Item.ItemAction=cEditActionInsert) and
|
||||
(Item.ItemIndex=AIndex) then
|
||||
begin
|
||||
DeleteLast;
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
|
||||
Item:= TATUndoItem.Create(AAction, AIndex, AText, AEnd, FSoftMark, FHardMark, ACarets);
|
||||
FList.Add(Item);
|
||||
FSoftMark:= false;
|
||||
|
||||
while Count>MaxCount do
|
||||
Delete(0);
|
||||
end;
|
||||
|
||||
|
||||
procedure TATUndoList.AddUnmodifiedMark;
|
||||
var
|
||||
Item: TATUndoItem;
|
||||
Carets: TATPointArray;
|
||||
begin
|
||||
//if FLocked then exit; //on load file called with Locked=true
|
||||
|
||||
//don't do two marks
|
||||
Item:= Last;
|
||||
if Assigned(Item) then
|
||||
if Item.ItemAction=cEditActionClearModified then exit;
|
||||
|
||||
SetLength(Carets, 0);
|
||||
Item:= TATUndoItem.Create(cEditActionClearModified, 0, '', cEndNone, false, false, Carets);
|
||||
FList.Add(Item);
|
||||
end;
|
||||
|
||||
procedure TATUndoList.DeleteUnmodifiedMarks;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= Count-1 downto 0 do
|
||||
if Items[i].ItemAction=cEditActionClearModified then
|
||||
Delete(i);
|
||||
end;
|
||||
|
||||
procedure TATUndoList.DebugShow;
|
||||
var
|
||||
i: integer;
|
||||
s: string;
|
||||
Item: TATUndoItem;
|
||||
begin
|
||||
s:= '';
|
||||
for i:= 0 to Min(40, Count)-1 do
|
||||
begin
|
||||
Item:= Items[i];
|
||||
s:= s+Format('%s, text "%s", %s, index %d', [
|
||||
StrEditActionDescriptions[Item.ItemAction],
|
||||
UTF8Encode(Item.ItemText),
|
||||
IfThen(Item.ItemEnd=cEndNone, 'no-eol', ''),
|
||||
Item.ItemIndex
|
||||
])+#13;
|
||||
end;
|
||||
ShowMessage('Undo list:'#13+s);
|
||||
end;
|
||||
|
||||
function TATUndoList.Last: TATUndoItem;
|
||||
begin
|
||||
if Count>0 then
|
||||
Result:= Items[Count-1]
|
||||
else
|
||||
Result:= nil;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
4433
ATSynEdit/atsynedit/atsynedit.pas
Normal file
1017
ATSynEdit/atsynedit/atsynedit_adapter_econtrol.pas
Normal file
66
ATSynEdit/atsynedit/atsynedit_adapters.pas
Normal file
@@ -0,0 +1,66 @@
|
||||
unit ATSynEdit_Adapters;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics,
|
||||
ATSynEdit_CanvasProc;
|
||||
|
||||
type
|
||||
{ TATAdapterHilite }
|
||||
|
||||
TATAdapterHilite = class(TComponent)
|
||||
public
|
||||
procedure OnEditorChange(Sender: TObject); virtual;
|
||||
procedure OnEditorCalcHilite(Sender: TObject;
|
||||
var AParts: TATLineParts;
|
||||
ALineIndex, ACharIndex, ALineLen: integer;
|
||||
var AColorAfterEol: TColor); virtual;
|
||||
procedure OnEditorCalcPosColor(Sender: TObject;
|
||||
AX, AY: integer; var AColor: TColor); virtual;
|
||||
procedure OnEditorCaretMove(Sender: TObject); virtual;
|
||||
procedure OnEditorScroll(Sender: TObject); virtual;
|
||||
procedure OnEditorBeforeCalcHilite(Sender: TObject); virtual;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TATAdapterHilite }
|
||||
|
||||
procedure TATAdapterHilite.OnEditorChange(Sender: TObject);
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
procedure TATAdapterHilite.OnEditorCalcHilite(Sender: TObject;
|
||||
var AParts: TATLineParts; ALineIndex, ACharIndex, ALineLen: integer;
|
||||
var AColorAfterEol: TColor);
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
procedure TATAdapterHilite.OnEditorCalcPosColor(Sender: TObject; AX,
|
||||
AY: integer; var AColor: TColor);
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
procedure TATAdapterHilite.OnEditorCaretMove(Sender: TObject);
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
procedure TATAdapterHilite.OnEditorScroll(Sender: TObject);
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
procedure TATAdapterHilite.OnEditorBeforeCalcHilite(Sender: TObject);
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
912
ATSynEdit/atsynedit/atsynedit_canvasproc.pas
Normal file
@@ -0,0 +1,912 @@
|
||||
unit ATSynEdit_CanvasProc;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
//{$define invert_pixels} //test Mac caret blinking
|
||||
{$ifdef darwin}
|
||||
{$define invert_pixels}
|
||||
{$endif}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics, Types,
|
||||
ATStringProc;
|
||||
|
||||
var
|
||||
OptUnprintedTabCharLength: integer = 1;
|
||||
OptUnprintedTabPointerScale: integer = 22;
|
||||
OptUnprintedSpaceDotScale: integer = 15;
|
||||
OptUnprintedEndDotScale: integer = 30;
|
||||
OptUnprintedEndFontScale: integer = 80;
|
||||
OptUnprintedEndFontDx: integer = 3;
|
||||
OptUnprintedEndFontDy: integer = 2;
|
||||
OptUnprintedEndArrowOrDot: boolean = true;
|
||||
OptUnprintedEndArrowLength: integer = 70;
|
||||
|
||||
type
|
||||
TATLineStyle = (
|
||||
cLineStyleNone,
|
||||
cLineStyleSolid,
|
||||
cLineStyleDash,
|
||||
cLineStyleSolid2px,
|
||||
cLineStyleDotted,
|
||||
cLineStyleRounded,
|
||||
cLineStyleWave
|
||||
);
|
||||
|
||||
type
|
||||
TATLinePart = record
|
||||
Offset, Len: integer;
|
||||
ColorFont, ColorBG, ColorBorder: TColor;
|
||||
FontBold, FontItalic, FontStrikeOut: boolean;
|
||||
BorderUp, BorderDown, BorderLeft, BorderRight: TATLineStyle;
|
||||
end;
|
||||
|
||||
type
|
||||
TATLinePartClass = class
|
||||
public
|
||||
Data: TATLinePart;
|
||||
end;
|
||||
|
||||
const
|
||||
cMaxLineParts = 1000; //big two monitors have total about 1000 chars (small font)
|
||||
type
|
||||
TATLineParts = array[0..cMaxLineParts-1] of TATLinePart;
|
||||
PATLineParts = ^TATLineParts;
|
||||
|
||||
type
|
||||
TATSynEditDrawLineEvent = procedure(Sender: TObject; C: TCanvas;
|
||||
AX, AY: integer; const AStr: atString; ACharSize: TPoint;
|
||||
const AExtent: TATIntArray) of object;
|
||||
|
||||
procedure CanvasLineEx(C: TCanvas; Color: TColor; Style: TATLineStyle;
|
||||
P1, P2: TPoint; AtDown: boolean);
|
||||
|
||||
procedure CanvasTextOut(C: TCanvas;
|
||||
PosX, PosY: integer;
|
||||
Str: atString;
|
||||
ATabSize: integer;
|
||||
ACharSize: TPoint;
|
||||
AMainText: boolean;
|
||||
AShowUnprintable: boolean;
|
||||
AColorUnprintable: TColor;
|
||||
AColorHex: TColor;
|
||||
out AStrWidth: integer;
|
||||
ACharsSkipped: integer;
|
||||
AParts: PATLineParts;
|
||||
ADrawEvent: TATSynEditDrawLineEvent;
|
||||
ATextOffsetFromLine: integer;
|
||||
AControlWidth: integer
|
||||
);
|
||||
|
||||
procedure CanvasTextOutMinimap(C: TCanvas;
|
||||
const AStr: atString;
|
||||
APos: TPoint;
|
||||
ACharSize: TPoint;
|
||||
ATabSize: integer;
|
||||
AParts: PATLineParts
|
||||
);
|
||||
|
||||
procedure DoPaintUnprintedEol(C: TCanvas;
|
||||
const AStrEol: atString;
|
||||
APoint: TPoint;
|
||||
ACharSize: TPoint;
|
||||
AColorFont, AColorBG: TColor;
|
||||
ADetails: boolean);
|
||||
|
||||
function CanvasTextSpaces(const S: atString; ATabSize: integer): real;
|
||||
function CanvasTextWidth(const S: atString; ATabSize: integer; ACharSize: TPoint): integer;
|
||||
|
||||
function CanvasFontSizes(C: TCanvas): TPoint;
|
||||
procedure CanvasInvertRect(C: TCanvas; const R: TRect; AColor: TColor);
|
||||
procedure CanvasDottedVertLine_Alt(C: TCanvas; Color: TColor; X1, Y1, Y2: integer);
|
||||
procedure CanvasDottedHorzVertLine(C: TCanvas; Color: TColor; P1, P2: TPoint);
|
||||
procedure CanvasWavyHorzLine(C: TCanvas; Color: TColor; P1, P2: TPoint; AtDown: boolean);
|
||||
|
||||
procedure CanvasPaintTriangleDown(C: TCanvas; AColor: TColor; ACoord: TPoint; ASize: integer);
|
||||
procedure CanvasPaintPlusMinus(C: TCanvas; AColorBorder, AColorBG: TColor; ACenter: TPoint; ASize: integer; APlus: boolean);
|
||||
|
||||
procedure DoPartFind(const AParts: TATLineParts; APos: integer; out AIndex, AOffsetLeft: integer);
|
||||
procedure DoPartInsert(var AParts: TATLineParts; const APart: TATLinePart; AKeepFontStyles: boolean);
|
||||
procedure DoPartSetColorBG(var AParts: TATLineParts; AColor: TColor; AForceColor: boolean);
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math,
|
||||
LCLType,
|
||||
LCLIntf;
|
||||
|
||||
var
|
||||
_Pen: TPen = nil;
|
||||
|
||||
type
|
||||
TATBorderSide = (cSideLeft, cSideRight, cSideUp, cSideDown);
|
||||
|
||||
|
||||
procedure DoPaintUnprintedSpace(C: TCanvas; const ARect: TRect; AScale: integer; AFontColor: TColor);
|
||||
const
|
||||
cMinDotSize = 2;
|
||||
var
|
||||
R: TRect;
|
||||
NSize: integer;
|
||||
begin
|
||||
NSize:= Max(cMinDotSize, (ARect.Bottom-ARect.Top) * AScale div 100);
|
||||
R.Left:= (ARect.Left+ARect.Right) div 2 - NSize div 2;
|
||||
R.Top:= (ARect.Top+ARect.Bottom) div 2 - NSize div 2;
|
||||
R.Right:= R.Left + NSize;
|
||||
R.Bottom:= R.Top + NSize;
|
||||
C.Pen.Color:= AFontColor;
|
||||
C.Brush.Color:= AFontColor;
|
||||
C.FillRect(R);
|
||||
end;
|
||||
|
||||
procedure DoPaintUnprintedTabulation(C: TCanvas;
|
||||
const ARect: TRect;
|
||||
AColorFont: TColor;
|
||||
ACharSizeX: integer);
|
||||
const
|
||||
cIndent = 1; //offset left/rt
|
||||
var
|
||||
XLeft, XRight, X1, X2, Y, Dx: integer;
|
||||
begin
|
||||
XLeft:= ARect.Left+cIndent;
|
||||
XRight:= ARect.Right-cIndent;
|
||||
|
||||
if OptUnprintedTabCharLength=0 then
|
||||
begin;
|
||||
X1:= XLeft;
|
||||
X2:= XRight;
|
||||
end
|
||||
else
|
||||
begin
|
||||
X1:= XLeft;
|
||||
X2:= Min(XRight, X1+OptUnprintedTabCharLength*ACharSizeX);
|
||||
end;
|
||||
|
||||
Y:= (ARect.Top+ARect.Bottom) div 2;
|
||||
Dx:= (ARect.Bottom-ARect.Top) * OptUnprintedTabPointerScale div 100;
|
||||
C.Pen.Color:= AColorFont;
|
||||
|
||||
C.MoveTo(X2, Y);
|
||||
C.LineTo(X1, Y);
|
||||
C.MoveTo(X2, Y);
|
||||
C.LineTo(X2-Dx, Y-Dx);
|
||||
C.MoveTo(X2, Y);
|
||||
C.LineTo(X2-Dx, Y+Dx);
|
||||
end;
|
||||
|
||||
|
||||
procedure DoPaintUnprintedArrowDown(C: TCanvas;
|
||||
const ARect: TRect;
|
||||
AColorFont: TColor);
|
||||
var
|
||||
Len, X, Y1, Y2, Dx: integer;
|
||||
begin
|
||||
X:= (ARect.Left+ARect.Right) div 2;
|
||||
Len:= (ARect.Bottom-ARect.Top) * OptUnprintedEndArrowLength div 100;
|
||||
Dx:= (ARect.Bottom-ARect.Top) * OptUnprintedTabPointerScale div 100;
|
||||
C.Pen.Color:= AColorFont;
|
||||
|
||||
Y1:= (ARect.Bottom+ARect.Top-Len) div 2;
|
||||
Y2:= Y1+Len;
|
||||
|
||||
C.MoveTo(X, Y1);
|
||||
C.LineTo(X, Y2);
|
||||
C.MoveTo(X, Y2);
|
||||
C.LineTo(X-Dx, Y2-Dx);
|
||||
C.MoveTo(X, Y2);
|
||||
C.LineTo(X+Dx, Y2-Dx);
|
||||
end;
|
||||
|
||||
procedure DoPaintUnprintedChars(C: TCanvas;
|
||||
const AString: atString;
|
||||
const AOffsets: TATIntArray;
|
||||
APoint: TPoint;
|
||||
ACharSize: TPoint;
|
||||
AColorFont: TColor);
|
||||
var
|
||||
R: TRect;
|
||||
i: integer;
|
||||
begin
|
||||
if AString='' then Exit;
|
||||
|
||||
for i:= 1 to Length(AString) do
|
||||
if (AString[i]=' ') or (AString[i]=#9) then
|
||||
begin
|
||||
R.Left:= APoint.X;
|
||||
R.Right:= APoint.X;
|
||||
if i>1 then
|
||||
Inc(R.Left, AOffsets[i-2]);
|
||||
Inc(R.Right, AOffsets[i-1]);
|
||||
|
||||
R.Top:= APoint.Y;
|
||||
R.Bottom:= R.Top+ACharSize.Y;
|
||||
|
||||
if AString[i]=' ' then
|
||||
DoPaintUnprintedSpace(C, R, OptUnprintedSpaceDotScale, AColorFont)
|
||||
else
|
||||
DoPaintUnprintedTabulation(C, R, AColorFont, ACharSize.X);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CanvasSimpleLine(C: TCanvas; P1, P2: TPoint);
|
||||
begin
|
||||
if P1.Y=P2.Y then
|
||||
C.Line(P1.X, P1.Y, P2.X+1, P2.Y)
|
||||
else
|
||||
C.Line(P1.X, P1.Y, P2.X, P2.Y+1);
|
||||
end;
|
||||
|
||||
procedure CanvasRoundedLine(C: TCanvas; Color: TColor; P1, P2: TPoint; AtDown: boolean);
|
||||
var
|
||||
cPixel: TColor;
|
||||
begin
|
||||
cPixel:= Color;
|
||||
C.Pen.Color:= Color;
|
||||
if P1.Y=P2.Y then
|
||||
begin
|
||||
C.Line(P1.X+2, P1.Y, P2.X-1, P2.Y);
|
||||
if AtDown then
|
||||
begin
|
||||
C.Pixels[P1.X+1, P1.Y-1]:= cPixel;
|
||||
C.Pixels[P2.X-1, P2.Y-1]:= cPixel;
|
||||
end
|
||||
else
|
||||
begin
|
||||
C.Pixels[P1.X+1, P1.Y+1]:= cPixel;
|
||||
C.Pixels[P2.X-1, P2.Y+1]:= cPixel;
|
||||
end
|
||||
end
|
||||
else
|
||||
begin
|
||||
C.Line(P1.X, P1.Y+2, P2.X, P2.Y-1);
|
||||
//don't draw pixels, other lines did it
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CanvasWavyHorzLine(C: TCanvas; Color: TColor; P1, P2: TPoint; AtDown: boolean);
|
||||
const
|
||||
cWavePeriod = 4;
|
||||
cWaveInc: array[0..cWavePeriod-1] of integer = (0, 1, 2, 1);
|
||||
var
|
||||
i, y, sign: integer;
|
||||
begin
|
||||
if AtDown then sign:= -1 else sign:= 1;
|
||||
for i:= P1.X to P2.X do
|
||||
begin
|
||||
y:= P2.Y + sign * cWaveInc[(i-P1.X) mod cWavePeriod];
|
||||
C.Pixels[i, y]:= Color;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CanvasDottedHorzVertLine(C: TCanvas; Color: TColor; P1, P2: TPoint);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
if P1.Y=P2.Y then
|
||||
begin
|
||||
for i:= P1.X to P2.X do
|
||||
if Odd(i-P1.X+1) then
|
||||
C.Pixels[i, P2.Y]:= Color;
|
||||
end
|
||||
else
|
||||
begin
|
||||
for i:= P1.Y to P2.Y do
|
||||
if Odd(i-P1.Y+1) then
|
||||
C.Pixels[P1.X, i]:= Color;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CanvasLineEx(C: TCanvas; Color: TColor; Style: TATLineStyle; P1, P2: TPoint; AtDown: boolean);
|
||||
begin
|
||||
case Style of
|
||||
cLineStyleSolid:
|
||||
begin
|
||||
C.Pen.Color:= Color;
|
||||
CanvasSimpleLine(C, P1, P2);
|
||||
end;
|
||||
|
||||
cLineStyleSolid2px:
|
||||
begin
|
||||
C.Pen.Color:= Color;
|
||||
CanvasSimpleLine(C, P1, P2);
|
||||
if P1.Y=P2.Y then
|
||||
begin
|
||||
if AtDown then
|
||||
begin Dec(P1.Y); Dec(P2.Y) end
|
||||
else
|
||||
begin Inc(P1.Y); Inc(P2.Y) end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if AtDown then
|
||||
begin Dec(P1.X); Dec(P2.X) end
|
||||
else
|
||||
begin Inc(P1.X); Inc(P2.X) end;
|
||||
end;
|
||||
CanvasSimpleLine(C, P1, P2);
|
||||
end;
|
||||
|
||||
cLineStyleDash:
|
||||
begin
|
||||
C.Pen.Color:= Color;
|
||||
C.Pen.Style:= psDot;
|
||||
CanvasSimpleLine(C, P1, P2);
|
||||
C.Pen.Style:= psSolid;
|
||||
end;
|
||||
|
||||
cLineStyleDotted:
|
||||
CanvasDottedHorzVertLine(C, Color, P1, P2);
|
||||
|
||||
cLineStyleRounded:
|
||||
CanvasRoundedLine(C, Color, P1, P2, AtDown);
|
||||
|
||||
cLineStyleWave:
|
||||
CanvasWavyHorzLine(C, Color, P1, P2, AtDown);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DoPaintBorder(C: TCanvas; Color: TColor; R: TRect; Side: TATBorderSide; Style: TATLineStyle);
|
||||
begin
|
||||
if Style=cLineStyleNone then Exit;
|
||||
Dec(R.Right);
|
||||
Dec(R.Bottom);
|
||||
|
||||
case Side of
|
||||
cSideDown:
|
||||
CanvasLineEx(C, Color, Style,
|
||||
Point(R.Left, R.Bottom),
|
||||
Point(R.Right, R.Bottom),
|
||||
true);
|
||||
cSideLeft:
|
||||
CanvasLineEx(C, Color, Style,
|
||||
Point(R.Left, R.Top),
|
||||
Point(R.Left, R.Bottom),
|
||||
false);
|
||||
cSideRight:
|
||||
CanvasLineEx(C, Color, Style,
|
||||
Point(R.Right, R.Top),
|
||||
Point(R.Right, R.Bottom),
|
||||
true);
|
||||
cSideUp:
|
||||
CanvasLineEx(C, Color, Style,
|
||||
Point(R.Left, R.Top),
|
||||
Point(R.Right, R.Top),
|
||||
false);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure DoPaintHexChars(C: TCanvas;
|
||||
const AString: atString;
|
||||
ADx: PIntegerArray;
|
||||
APoint: TPoint;
|
||||
ACharSize: TPoint;
|
||||
AColorFont,
|
||||
AColorBg: TColor);
|
||||
var
|
||||
Buf: string;
|
||||
R: TRect;
|
||||
i, j: integer;
|
||||
begin
|
||||
if AString='' then Exit;
|
||||
|
||||
for i:= 1 to Length(AString) do
|
||||
if IsCharHex(AString[i]) then
|
||||
begin
|
||||
R.Left:= APoint.X;
|
||||
R.Right:= APoint.X;
|
||||
|
||||
for j:= 0 to i-2 do
|
||||
Inc(R.Left, ADx^[j]);
|
||||
R.Right:= R.Left+ADx^[i-1];
|
||||
|
||||
R.Top:= APoint.Y;
|
||||
R.Bottom:= R.Top+ACharSize.Y;
|
||||
|
||||
C.Font.Color:= AColorFont;
|
||||
C.Brush.Color:= AColorBg;
|
||||
|
||||
Buf:= '<'+IntToHex(Ord(AString[i]), 4)+'>';
|
||||
ExtTextOut(C.Handle,
|
||||
R.Left, R.Top,
|
||||
ETO_CLIPPED+ETO_OPAQUE,
|
||||
@R,
|
||||
PChar(Buf),
|
||||
Length(Buf),
|
||||
nil);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DoPaintUnprintedEol(C: TCanvas;
|
||||
const AStrEol: atString;
|
||||
APoint: TPoint;
|
||||
ACharSize: TPoint;
|
||||
AColorFont, AColorBG: TColor;
|
||||
ADetails: boolean);
|
||||
var
|
||||
NPrevSize: integer;
|
||||
begin
|
||||
if AStrEol='' then Exit;
|
||||
|
||||
if ADetails then
|
||||
begin
|
||||
NPrevSize:= C.Font.Size;
|
||||
C.Font.Size:= C.Font.Size * OptUnprintedEndFontScale div 100;
|
||||
C.Font.Color:= AColorFont;
|
||||
C.Brush.Color:= AColorBG;
|
||||
C.TextOut(
|
||||
APoint.X+OptUnprintedEndFontDx,
|
||||
APoint.Y+OptUnprintedEndFontDy,
|
||||
AStrEol);
|
||||
C.Font.Size:= NPrevSize;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if OptUnprintedEndArrowOrDot then
|
||||
DoPaintUnprintedArrowDown(C,
|
||||
Rect(APoint.X, APoint.Y, APoint.X+ACharSize.X, APoint.Y+ACharSize.Y),
|
||||
AColorFont)
|
||||
else
|
||||
DoPaintUnprintedSpace(C,
|
||||
Rect(APoint.X, APoint.Y, APoint.X+ACharSize.X, APoint.Y+ACharSize.Y),
|
||||
OptUnprintedEndDotScale,
|
||||
AColorFont);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function CanvasFontSizes(C: TCanvas): TPoint;
|
||||
var
|
||||
Size: TSize;
|
||||
begin
|
||||
Size:= C.TextExtent('M');
|
||||
Result.X:= Size.cx;
|
||||
Result.Y:= Size.cy;
|
||||
end;
|
||||
|
||||
function CanvasTextSpaces(const S: atString; ATabSize: integer): real;
|
||||
var
|
||||
List: TATRealArray;
|
||||
begin
|
||||
Result:= 0;
|
||||
if S='' then Exit;
|
||||
SetLength(List, Length(S));
|
||||
SCalcCharOffsets(S, List, ATabSize);
|
||||
Result:= List[High(List)];
|
||||
end;
|
||||
|
||||
function CanvasTextWidth(const S: atString; ATabSize: integer; ACharSize: TPoint): integer;
|
||||
begin
|
||||
Result:= Trunc(CanvasTextSpaces(S, ATabSize)*ACharSize.X);
|
||||
end;
|
||||
|
||||
|
||||
function CanvasTextOutNeedsOffsets(const Str: atString): boolean;
|
||||
begin
|
||||
{$ifdef darwin} exit(true); {$endif}
|
||||
Result:= IsStringWithUnicodeChars(Str);
|
||||
end;
|
||||
|
||||
procedure CanvasTextOut(C: TCanvas; PosX, PosY: integer; Str: atString;
|
||||
ATabSize: integer; ACharSize: TPoint; AMainText: boolean;
|
||||
AShowUnprintable: boolean; AColorUnprintable: TColor; AColorHex: TColor; out
|
||||
AStrWidth: integer; ACharsSkipped: integer; AParts: PATLineParts;
|
||||
ADrawEvent: TATSynEditDrawLineEvent; ATextOffsetFromLine: integer;
|
||||
AControlWidth: integer);
|
||||
var
|
||||
ListReal: TATRealArray;
|
||||
ListInt: TATIntArray;
|
||||
Dx: TATIntArray;
|
||||
i, j: integer;
|
||||
PartStr: atString;
|
||||
PartOffset, PartLen,
|
||||
PixOffset1, PixOffset2: integer;
|
||||
PartPtr: ^TATLinePart;
|
||||
PartFontStyle: TFontStyles;
|
||||
PartRect: TRect;
|
||||
Buf: AnsiString;
|
||||
DxPointer: PInteger;
|
||||
begin
|
||||
if Str='' then Exit;
|
||||
|
||||
SetLength(ListReal, Length(Str));
|
||||
SetLength(ListInt, Length(Str));
|
||||
SetLength(Dx, Length(Str));
|
||||
|
||||
SCalcCharOffsets(Str, ListReal, ATabSize, ACharsSkipped);
|
||||
|
||||
for i:= 0 to High(ListReal) do
|
||||
ListInt[i]:= Trunc(ListReal[i]*ACharSize.X);
|
||||
|
||||
//truncate str, to not paint over screen
|
||||
for i:= 1 to High(ListInt) do
|
||||
if ListInt[i]>AControlWidth then
|
||||
begin
|
||||
SetLength(Str, i);
|
||||
break;
|
||||
end;
|
||||
|
||||
for i:= 0 to High(ListReal) do
|
||||
if i=0 then
|
||||
Dx[i]:= ListInt[i]
|
||||
else
|
||||
Dx[i]:= ListInt[i]-ListInt[i-1];
|
||||
|
||||
if AParts=nil then
|
||||
begin
|
||||
Buf:= UTF8Encode(SRemoveHexChars(Str));
|
||||
if CanvasTextOutNeedsOffsets(Str) then
|
||||
DxPointer:= @Dx[0]
|
||||
else
|
||||
DxPointer:= nil;
|
||||
ExtTextOut(C.Handle, PosX, PosY, 0, nil, PChar(Buf), Length(Buf), DxPointer);
|
||||
|
||||
DoPaintHexChars(C,
|
||||
Str,
|
||||
@Dx[0],
|
||||
Point(PosX, PosY),
|
||||
ACharSize,
|
||||
AColorHex,
|
||||
C.Brush.Color
|
||||
);
|
||||
end
|
||||
else
|
||||
for j:= 0 to High(TATLineParts) do
|
||||
begin
|
||||
PartPtr:= @AParts^[j];
|
||||
PartLen:= PartPtr^.Len;
|
||||
if PartLen=0 then Break;
|
||||
PartOffset:= PartPtr^.Offset;
|
||||
PartStr:= Copy(Str, PartOffset+1, PartLen);
|
||||
if PartStr='' then Break;
|
||||
|
||||
PartFontStyle:= [];
|
||||
if PartPtr^.FontBold then Include(PartFontStyle, fsBold);
|
||||
if PartPtr^.FontItalic then Include(PartFontStyle, fsItalic);
|
||||
if PartPtr^.FontStrikeOut then Include(PartFontStyle, fsStrikeOut);
|
||||
|
||||
if PartOffset>0 then
|
||||
PixOffset1:= ListInt[PartOffset-1]
|
||||
else
|
||||
PixOffset1:= 0;
|
||||
|
||||
i:= Min(PartOffset+PartLen, Length(Str));
|
||||
if i>0 then
|
||||
PixOffset2:= ListInt[i-1]
|
||||
else
|
||||
PixOffset2:= 0;
|
||||
|
||||
C.Font.Color:= PartPtr^.ColorFont;
|
||||
C.Brush.Color:= PartPtr^.ColorBG;
|
||||
C.Font.Style:= PartFontStyle;
|
||||
|
||||
PartRect:= Rect(
|
||||
PosX+PixOffset1,
|
||||
PosY,
|
||||
PosX+PixOffset2,
|
||||
PosY+ACharSize.Y);
|
||||
|
||||
Buf:= UTF8Encode(SRemoveHexChars(PartStr));
|
||||
if CanvasTextOutNeedsOffsets(PartStr) then
|
||||
DxPointer:= @Dx[PartOffset]
|
||||
else
|
||||
DxPointer:= nil;
|
||||
|
||||
ExtTextOut(C.Handle,
|
||||
PosX+PixOffset1,
|
||||
PosY+ATextOffsetFromLine,
|
||||
ETO_CLIPPED+ETO_OPAQUE,
|
||||
@PartRect,
|
||||
PChar(Buf),
|
||||
Length(Buf),
|
||||
DxPointer);
|
||||
|
||||
DoPaintHexChars(C,
|
||||
PartStr,
|
||||
@Dx[PartOffset],
|
||||
Point(PosX+PixOffset1, PosY),
|
||||
ACharSize,
|
||||
AColorHex,
|
||||
PartPtr^.ColorBG
|
||||
);
|
||||
|
||||
if AMainText then
|
||||
begin
|
||||
DoPaintBorder(C, PartPtr^.ColorBorder, PartRect, cSideDown, PartPtr^.BorderDown);
|
||||
DoPaintBorder(C, PartPtr^.ColorBorder, PartRect, cSideUp, PartPtr^.BorderUp);
|
||||
DoPaintBorder(C, PartPtr^.ColorBorder, PartRect, cSideLeft, PartPtr^.BorderLeft);
|
||||
DoPaintBorder(C, PartPtr^.ColorBorder, PartRect, cSideRight, PartPtr^.BorderRight);
|
||||
end;
|
||||
end;
|
||||
|
||||
if AShowUnprintable then
|
||||
DoPaintUnprintedChars(C, Str, ListInt, Point(PosX, PosY), ACharSize, AColorUnprintable);
|
||||
|
||||
AStrWidth:= ListInt[High(ListInt)];
|
||||
|
||||
if Str<>'' then
|
||||
if Assigned(ADrawEvent) then
|
||||
ADrawEvent(nil, C, PosX, PosY, Str, ACharSize, ListInt);
|
||||
end;
|
||||
|
||||
|
||||
{$ifdef invert_pixels}
|
||||
procedure CanvasInvertRect(C: TCanvas; const R: TRect; AColor: TColor);
|
||||
var
|
||||
i, j: integer;
|
||||
begin
|
||||
for j:= R.Top to R.Bottom-1 do
|
||||
for i:= R.Left to R.Right-1 do
|
||||
C.Pixels[i, j]:= C.Pixels[i, j] xor (not AColor and $ffffff);
|
||||
end;
|
||||
{$else}
|
||||
procedure CanvasInvertRect(C: TCanvas; const R: TRect; AColor: TColor);
|
||||
var
|
||||
X: integer;
|
||||
AM: TAntialiasingMode;
|
||||
begin
|
||||
AM:= C.AntialiasingMode;
|
||||
_Pen.Assign(C.Pen);
|
||||
|
||||
X:= (R.Left+R.Right) div 2;
|
||||
C.Pen.Mode:= pmNotXor;
|
||||
C.Pen.Style:= psSolid;
|
||||
C.Pen.Color:= AColor;
|
||||
C.AntialiasingMode:= amOff;
|
||||
C.Pen.EndCap:= pecFlat;
|
||||
C.Pen.Width:= R.Right-R.Left;
|
||||
|
||||
C.MoveTo(X, R.Top);
|
||||
C.LineTo(X, R.Bottom);
|
||||
|
||||
C.Pen.Assign(_Pen);
|
||||
C.AntialiasingMode:= AM;
|
||||
C.Rectangle(0, 0, 0, 0); //apply pen
|
||||
end;
|
||||
{$endif}
|
||||
|
||||
procedure CanvasDottedVertLine_Alt(C: TCanvas; Color: TColor; X1, Y1, Y2: integer);
|
||||
var
|
||||
j: integer;
|
||||
begin
|
||||
for j:= Y1 to Y2 do
|
||||
if Odd(j) then
|
||||
C.Pixels[X1, j]:= Color;
|
||||
end;
|
||||
|
||||
|
||||
procedure CanvasPaintTriangleDown(C: TCanvas; AColor: TColor; ACoord: TPoint; ASize: integer);
|
||||
begin
|
||||
C.Brush.Color:= AColor;
|
||||
C.Pen.Color:= AColor;
|
||||
C.Polygon([
|
||||
Point(ACoord.X, ACoord.Y),
|
||||
Point(ACoord.X+ASize*2, ACoord.Y),
|
||||
Point(ACoord.X+ASize, ACoord.Y+ASize)
|
||||
]);
|
||||
end;
|
||||
|
||||
procedure CanvasPaintPlusMinus(C: TCanvas; AColorBorder, AColorBG: TColor;
|
||||
ACenter: TPoint; ASize: integer; APlus: boolean);
|
||||
begin
|
||||
C.Brush.Color:= AColorBG;
|
||||
C.Pen.Color:= AColorBorder;
|
||||
C.Rectangle(ACenter.X-ASize, ACenter.Y-ASize, ACenter.X+ASize+1, ACenter.Y+ASize+1);
|
||||
C.Line(ACenter.X-ASize+2, ACenter.Y, ACenter.X+ASize-1, ACenter.Y);
|
||||
if APlus then
|
||||
C.Line(ACenter.X, ACenter.Y-ASize+2, ACenter.X, ACenter.Y+ASize-1);
|
||||
end;
|
||||
|
||||
|
||||
procedure DoPartFind(const AParts: TATLineParts; APos: integer; out
|
||||
AIndex, AOffsetLeft: integer);
|
||||
var
|
||||
iStart, iEnd, i: integer;
|
||||
begin
|
||||
AIndex:= -1;
|
||||
AOffsetLeft:= 0;
|
||||
|
||||
for i:= Low(AParts) to High(AParts)-1 do
|
||||
begin
|
||||
if AParts[i].Len=0 then
|
||||
begin
|
||||
//pos after last part?
|
||||
if i>Low(AParts) then
|
||||
if APos>=AParts[i-1].Offset+AParts[i-1].Len then
|
||||
AIndex:= i;
|
||||
Break;
|
||||
end;
|
||||
|
||||
iStart:= AParts[i].Offset;
|
||||
iEnd:= iStart+AParts[i].Len;
|
||||
|
||||
//pos at part begin?
|
||||
if APos=iStart then
|
||||
begin AIndex:= i; Break end;
|
||||
|
||||
//pos at part middle?
|
||||
if (APos>=iStart) and (APos<iEnd) then
|
||||
begin AIndex:= i; AOffsetLeft:= APos-iStart; Break end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure DoPartInsert(var AParts: TATLineParts; const APart: TATLinePart;
|
||||
AKeepFontStyles: boolean);
|
||||
var
|
||||
ResultParts: TATLineParts;
|
||||
ResultPartIndex: integer;
|
||||
//
|
||||
procedure AddPart(const P: TATLinePart);
|
||||
begin
|
||||
if P.Len>0 then
|
||||
begin
|
||||
Move(P, ResultParts[ResultPartIndex], SizeOf(P));
|
||||
Inc(ResultPartIndex);
|
||||
end;
|
||||
end;
|
||||
//
|
||||
var
|
||||
PartSelBegin, PartSelEnd: TATLinePart;
|
||||
nIndex1, nIndex2,
|
||||
nOffset1, nOffset2,
|
||||
newLen1, newLen2, newOffset2: integer;
|
||||
i: integer;
|
||||
begin
|
||||
DoPartFind(AParts, APart.Offset, nIndex1, nOffset1);
|
||||
DoPartFind(AParts, APart.Offset+APart.Len, nIndex2, nOffset2);
|
||||
if nIndex1<0 then Exit;
|
||||
if nIndex2<0 then Exit;
|
||||
|
||||
//these 2 parts are for edges of selection
|
||||
FillChar(PartSelBegin{%H-}, SizeOf(TATLinePart), 0);
|
||||
FillChar(PartSelEnd{%H-}, SizeOf(TATLinePart), 0);
|
||||
|
||||
PartSelBegin.ColorFont:= APart.ColorFont;
|
||||
PartSelBegin.ColorBG:= APart.ColorBG;
|
||||
PartSelBegin.Offset:= AParts[nIndex1].Offset+nOffset1;
|
||||
PartSelBegin.Len:= AParts[nIndex1].Len-nOffset1;
|
||||
PartSelBegin.FontBold:= AParts[nIndex1].FontBold;
|
||||
PartSelBegin.FontItalic:= AParts[nIndex1].FontItalic;
|
||||
PartSelBegin.FontStrikeOut:= AParts[nIndex1].FontStrikeOut;
|
||||
|
||||
PartSelEnd.ColorFont:= APart.ColorFont;
|
||||
PartSelEnd.ColorBG:= APart.ColorBG;
|
||||
PartSelEnd.Offset:= AParts[nIndex2].Offset;
|
||||
PartSelEnd.Len:= nOffset2;
|
||||
PartSelEnd.FontBold:= AParts[nIndex2].FontBold;
|
||||
PartSelEnd.FontItalic:= AParts[nIndex2].FontItalic;
|
||||
PartSelEnd.FontStrikeOut:= AParts[nIndex2].FontStrikeOut;
|
||||
|
||||
with AParts[nIndex1] do
|
||||
begin
|
||||
newLen1:= nOffset1;
|
||||
end;
|
||||
with AParts[nIndex2] do
|
||||
begin
|
||||
newLen2:= Len-nOffset2;
|
||||
newOffset2:= Offset+nOffset2;
|
||||
end;
|
||||
|
||||
FillChar(ResultParts, SizeOf(ResultParts), 0);
|
||||
ResultPartIndex:= 0;
|
||||
|
||||
//add parts before selection
|
||||
for i:= 0 to nIndex1-1 do
|
||||
AddPart(AParts[i]);
|
||||
if nOffset1>0 then
|
||||
begin
|
||||
AParts[nIndex1].Len:= newLen1;
|
||||
AddPart(AParts[nIndex1]);
|
||||
end;
|
||||
|
||||
//add middle (one APart of many parts)
|
||||
if not AKeepFontStyles then
|
||||
AddPart(APart)
|
||||
else
|
||||
begin
|
||||
AddPart(PartSelBegin);
|
||||
|
||||
for i:= nIndex1+1 to nIndex2-1 do
|
||||
begin
|
||||
AParts[i].ColorFont:= APart.ColorFont;
|
||||
AParts[i].ColorBG:= APart.ColorBG;
|
||||
AddPart(AParts[i]);
|
||||
end;
|
||||
|
||||
if nIndex1<nIndex2 then
|
||||
AddPart(PartSelEnd);
|
||||
end;
|
||||
|
||||
//add parts after selection
|
||||
if nOffset2>0 then
|
||||
begin
|
||||
AParts[nIndex2].Len:= newLen2;
|
||||
AParts[nIndex2].Offset:= newOffset2;
|
||||
end;
|
||||
|
||||
for i:= nIndex2 to High(AParts) do
|
||||
begin
|
||||
if AParts[i].Len=0 then Break;
|
||||
AddPart(AParts[i]);
|
||||
end;
|
||||
|
||||
//application.mainform.caption:= format('n1 %d, n2 %d, of len %d %d',
|
||||
// [nindex1, nindex2, aparts[nindex2].offset, aparts[nindex2].len]);
|
||||
|
||||
//copy result
|
||||
Move(ResultParts, AParts, SizeOf(AParts));
|
||||
end;
|
||||
|
||||
|
||||
procedure DoPartSetColorBG(var AParts: TATLineParts; AColor: TColor;
|
||||
AForceColor: boolean);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= Low(AParts) to High(AParts) do
|
||||
begin
|
||||
if AParts[i].Len=0 then Break;
|
||||
if AForceColor or (AParts[i].ColorBG=clNone) then
|
||||
AParts[i].ColorBG:= AColor;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure CanvasTextOutMinimap(C: TCanvas; const AStr: atString; APos: TPoint;
|
||||
ACharSize: TPoint; ATabSize: integer; AParts: PATLineParts);
|
||||
const
|
||||
cLowChars = '.,:;_''-+`~=^*';
|
||||
var
|
||||
Offsets: TATIntArray;
|
||||
Part: ^TATLinePart;
|
||||
ch: Widechar;
|
||||
nPos, nCharSize: integer;
|
||||
i, j: integer;
|
||||
X1, Y1, Y2: integer;
|
||||
begin
|
||||
if AStr='' then exit;
|
||||
SetLength(Offsets, Length(AStr)+1);
|
||||
Offsets[0]:= 0;
|
||||
for i:= 2 to Length(AStr) do
|
||||
Offsets[i-1]:= Offsets[i-2]+IfThen(AStr[i-1]=#9, ATabSize, 1);
|
||||
|
||||
for i:= Low(TATLineParts) to High(TATLineParts) do
|
||||
begin
|
||||
Part:= @AParts^[i];
|
||||
if Part^.Len=0 then Break;
|
||||
for j:= 1 to Part^.Len do
|
||||
begin
|
||||
nPos:= Part^.Offset+j;
|
||||
if nPos>Length(AStr) then Continue;
|
||||
ch:= AStr[nPos];
|
||||
if IsCharSpace(ch) then Continue;
|
||||
|
||||
nCharSize:= ACharSize.Y;
|
||||
if Pos(ch, cLowChars)>0 then
|
||||
nCharSize:= nCharSize div 2;
|
||||
|
||||
X1:= APos.X+ACharSize.X*Offsets[nPos-1];
|
||||
Y2:= APos.Y+ACharSize.Y;
|
||||
Y1:= Y2-nCharSize;
|
||||
|
||||
C.Pen.Color:= Part^.ColorFont;
|
||||
C.Line(X1, Y1, X1, Y2);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
//------------------
|
||||
initialization
|
||||
_Pen:= TPen.Create;
|
||||
|
||||
finalization
|
||||
if Assigned(_Pen) then
|
||||
FreeAndNil(_Pen);
|
||||
|
||||
end.
|
||||
|
||||
659
ATSynEdit/atsynedit/atsynedit_carets.inc
Normal file
@@ -0,0 +1,659 @@
|
||||
{$ifdef nnnn}begin end;{$endif}
|
||||
|
||||
|
||||
function TATSynEdit.IsLineWithCaret(ALine: integer): boolean;
|
||||
begin
|
||||
Result:= FCarets.IsLineListed(ALine);
|
||||
end;
|
||||
|
||||
function TATSynEdit.IsLinePartWithCaret(ALine: integer; ACoordY: integer): boolean;
|
||||
var
|
||||
i: integer;
|
||||
Caret: TATCaretItem;
|
||||
Coord: TPoint;
|
||||
begin
|
||||
Result:= false;
|
||||
//like Carets.IsLineListed with more code
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
if Caret.PosY=ALine then
|
||||
begin
|
||||
Coord:= CaretPosToClientPos(Point(Caret.PosX, Caret.PosY));
|
||||
if Coord.Y=ACoordY then
|
||||
begin
|
||||
Result:= true;
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCaretAddToPoint(AX, AY: integer);
|
||||
var
|
||||
N: integer;
|
||||
begin
|
||||
N:= Carets.IndexOfPosXY(AX, AY);
|
||||
if Carets.IsIndexValid(N) then
|
||||
begin
|
||||
if Carets.Count>1 then
|
||||
Carets.Delete(N);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Carets.Add(AX, AY);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCaretsColumnToPoint(AX, AY: integer);
|
||||
var
|
||||
P, PM1, PM2: TPoint;
|
||||
EolPos: boolean;
|
||||
begin
|
||||
DoCaretSingleAsIs;
|
||||
with Carets[0] do
|
||||
begin P.X:= PosX; P.Y:= PosY; end;
|
||||
|
||||
PM1:= CaretPosToClientPos(P);
|
||||
PM2:= CaretPosToClientPos(Point(AX, AY));
|
||||
|
||||
//clicked above caret?
|
||||
if PM2.Y<PM1.Y then
|
||||
SwapInt(PM1.Y, PM2.Y);
|
||||
|
||||
Carets.Clear;
|
||||
P:= ClientPosToCaretPos(PM1, EolPos);
|
||||
if P.Y>=0 then
|
||||
Carets.Add(P.X, P.Y);
|
||||
|
||||
repeat
|
||||
Inc(PM1.Y, FCharSize.Y);
|
||||
P:= ClientPosToCaretPos(PM1, EolPos);
|
||||
if P.Y>=0 then
|
||||
if not Carets.IsLineListed(P.Y) then
|
||||
Carets.Add(P.X, P.Y);
|
||||
until PM1.Y>=PM2.Y;
|
||||
|
||||
if Carets.Count=0 then
|
||||
Carets.Add(AX, AY);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCaretsSort;
|
||||
begin
|
||||
Carets.Sort;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.UpdateCaretsCoords(AOnlyLast: boolean = false);
|
||||
var
|
||||
P: TPoint;
|
||||
NStart, i: integer;
|
||||
Caret: TATCaretItem;
|
||||
Marker: TATMarkerItem;
|
||||
begin
|
||||
if AOnlyLast then
|
||||
NStart:= Carets.Count-1
|
||||
else
|
||||
NStart:= 0;
|
||||
|
||||
for i:= NStart to Carets.Count-1 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
P.X:= Caret.PosX;
|
||||
P.Y:= Caret.PosY;
|
||||
if IsPosFolded(P.X, P.Y) then
|
||||
begin
|
||||
Caret.CoordX:= -1;
|
||||
Caret.CoordY:= -1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
P:= CaretPosToClientPos(P);
|
||||
Caret.CoordX:= P.X;
|
||||
Caret.CoordY:= P.Y;
|
||||
end;
|
||||
end;
|
||||
|
||||
for i:= 0 to Markers.Count-1 do
|
||||
begin
|
||||
Marker:= Markers[i];
|
||||
P.X:= Marker.PosX;
|
||||
P.Y:= Marker.PosY;
|
||||
if IsPosFolded(P.X, P.Y) then
|
||||
begin
|
||||
Marker.CoordX:= -1;
|
||||
Marker.CoordY:= -1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
P:= CaretPosToClientPos(P);
|
||||
Marker.CoordX:= P.X;
|
||||
Marker.CoordY:= P.Y;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function _DoCaretPosToClientPos(
|
||||
P: TPoint;
|
||||
AWrapInfo: TATSynWrapInfo;
|
||||
AStrings: TATStrings;
|
||||
ACharSize: TPoint;
|
||||
ATabSize: integer;
|
||||
const ARect: TRect;
|
||||
const AScrollHorz, AScrollVert: TATSynScrollInfo;
|
||||
APreferLeftSide: boolean): TPoint;
|
||||
var
|
||||
Item: TATSynWrapItem;
|
||||
NIndex1, NIndex2, i: integer;
|
||||
NFromStart: integer;
|
||||
Str: atString;
|
||||
begin
|
||||
Result.X:= -1;
|
||||
Result.Y:= -1;
|
||||
|
||||
AWrapInfo.FindIndexesOfLineNumber(P.Y, NIndex1, NIndex2);
|
||||
if NIndex1<0 then Exit;
|
||||
|
||||
for i:= NIndex1 to NIndex2 do
|
||||
begin
|
||||
Item:= AWrapInfo.Items[i];
|
||||
|
||||
if (P.X<Item.NCharIndex-1) then Continue;
|
||||
if (Item.NFinal=cWrapItemMiddle) then
|
||||
if (P.X>Item.NCharIndex-1+Item.NLength) or
|
||||
((P.X=Item.NCharIndex-1+Item.NLength) and APreferLeftSide) then
|
||||
Continue;
|
||||
|
||||
NFromStart:= P.X+1-Item.NCharIndex;
|
||||
Str:= Copy(AStrings.Lines[P.Y], Item.NCharIndex, Min(NFromStart, Item.NLength));
|
||||
|
||||
Result.X:= CanvasTextWidth(Str, ATabSize, ACharSize);
|
||||
if NFromStart>Item.NLength then
|
||||
Inc(Result.X, (NFromStart-Item.NLength)*ACharSize.X);
|
||||
|
||||
Inc(Result.X, (Item.NIndent-AScrollHorz.NPos)*ACharSize.X);
|
||||
Result.Y:= (i-AScrollVert.NPos)*ACharSize.Y;
|
||||
|
||||
Inc(Result.X, ARect.Left);
|
||||
Inc(Result.Y, ARect.Top);
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
|
||||
function _DoClientPosToCaretPos(
|
||||
P: TPoint;
|
||||
AWrapInfo: TATSynWrapInfo;
|
||||
AStrings: TATStrings;
|
||||
ACharSize: TPoint;
|
||||
ATabSize: integer;
|
||||
const ARect: TRect;
|
||||
const AScrollHorz, AScrollVert: TATSynScrollInfo;
|
||||
AVirtualPos: boolean;
|
||||
out AWrappedEnd: boolean): TPoint;
|
||||
var
|
||||
NPixels, NIndex: integer;
|
||||
Item: TATSynWrapItem;
|
||||
Str: atString;
|
||||
AllowVirtual: boolean;
|
||||
begin
|
||||
AWrappedEnd:= false;
|
||||
Result.X:= 0;
|
||||
Result.Y:= -1;
|
||||
if (ACharSize.X<=0) or (ACharSize.Y<=0) then Exit;
|
||||
|
||||
P.X:= Max(P.X, ARect.Left);
|
||||
NIndex:= (P.Y-ARect.Top) div ACharSize.Y + AScrollVert.NPos;
|
||||
|
||||
if NIndex<0 then
|
||||
//click above all text
|
||||
begin
|
||||
Result.X:= 0;
|
||||
Result.Y:= 0;
|
||||
end
|
||||
else
|
||||
if not AWrapInfo.IsIndexValid(NIndex) then
|
||||
//click below all text
|
||||
begin
|
||||
NIndex:= AWrapInfo.Count-1;
|
||||
if AWrapInfo.IsIndexValid(NIndex) then
|
||||
begin
|
||||
Item:= AWrapInfo.Items[NIndex];
|
||||
Result.Y:= Item.NLineIndex;
|
||||
Result.X:= Item.NCharIndex+Item.NLength-1;
|
||||
end;
|
||||
end
|
||||
else
|
||||
//click in text
|
||||
begin
|
||||
Item:= AWrapInfo.Items[NIndex];
|
||||
Result.Y:= Item.NLineIndex;
|
||||
|
||||
Str:= Copy(AStrings.Lines[Result.Y], Item.NCharIndex, Item.NLength);
|
||||
|
||||
AllowVirtual:= AVirtualPos and (Item.NFinal=cWrapItemFinal);
|
||||
NPixels:= P.X-ARect.Left + ACharSize.X*(AScrollHorz.NPos-Item.NIndent);
|
||||
Result.X:= SFindClickedPosition(Str, NPixels, ACharSize.X, ATabSize, AllowVirtual, AWrappedEnd) + Item.NCharIndex - 2;
|
||||
|
||||
if Item.NFinal=cWrapItemFinal then //don't set AWrappedEnd for real eol
|
||||
AWrappedEnd:= false;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.CaretPosToClientPos(P: TPoint): TPoint;
|
||||
begin
|
||||
Result:= _DoCaretPosToClientPos(P,
|
||||
FWrapInfo,
|
||||
Strings,
|
||||
FCharSize,
|
||||
FTabSize,
|
||||
FRectMain,
|
||||
FScrollHorz,
|
||||
FScrollVert,
|
||||
FCaretSpecPos or FOptCaretPreferLeftSide
|
||||
);
|
||||
end;
|
||||
|
||||
function TATSynEdit.ClientPosToCaretPos(P: TPoint; out AEndOfLinePos: boolean): TPoint;
|
||||
begin
|
||||
Result:= _DoClientPosToCaretPos(P,
|
||||
FWrapInfo,
|
||||
Strings,
|
||||
FCharSize,
|
||||
FTabSize,
|
||||
FRectMain,
|
||||
FScrollHorz,
|
||||
FScrollVert,
|
||||
FCaretVirtual,
|
||||
AEndOfLinePos);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.SetCaretShapeIns(AValue: TATSynCaretShape);
|
||||
begin
|
||||
if FCaretShapeIns=AValue then Exit;
|
||||
DoPaintModeStatic;
|
||||
FCaretShapeIns:= AValue;
|
||||
DoPaintModeBlinking;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.SetCaretShapeOvr(AValue: TATSynCaretShape);
|
||||
begin
|
||||
if FCaretShapeOvr=AValue then Exit;
|
||||
DoPaintModeStatic;
|
||||
FCaretShapeOvr:= AValue;
|
||||
DoPaintModeBlinking;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.SetCaretShapeRO(AValue: TATSynCaretShape);
|
||||
begin
|
||||
if FCaretShapeRO=AValue then Exit;
|
||||
DoPaintModeStatic;
|
||||
FCaretShapeRO:= AValue;
|
||||
DoPaintModeBlinking;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.SetCaretBlinkEnabled(AValue: boolean);
|
||||
begin
|
||||
if FCaretBlinkEnabled=AValue then Exit;
|
||||
FCaretBlinkEnabled:= AValue;
|
||||
DoPaintModeStatic;
|
||||
DoPaintModeBlinking;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoGotoPos(APnt: TPoint; AIndentHorz, AIndentVert: integer);
|
||||
var
|
||||
NIndex, NVisLines, NChars: integer;
|
||||
begin
|
||||
if IsPosFolded(APnt.X, APnt.Y) then Exit;
|
||||
|
||||
NVisLines:= GetVisibleLines;
|
||||
APnt:= CaretPosToClientPos(APnt);
|
||||
NIndex:= GetWrapInfoIndex(APnt);
|
||||
if NIndex<0 then Exit;
|
||||
|
||||
//for y
|
||||
//negative AIndentVert: indent always from top
|
||||
//positive: from top (goto up) or bottom (goto down)
|
||||
if (NIndex<FScrollVert.NPos) then
|
||||
begin
|
||||
FScrollVert.NPos:= Max(0, NIndex-Abs(AIndentVert));
|
||||
UpdateScrollbars;
|
||||
end
|
||||
else
|
||||
if (NIndex>FScrollVert.NPos+NVisLines-1) then
|
||||
begin
|
||||
if AIndentVert<0 then
|
||||
FScrollVert.NPos:= Max(0, NIndex-Abs(AIndentVert))
|
||||
else
|
||||
FScrollVert.NPos:= Max(0, NIndex-NVisLines+1+Abs(AIndentVert));
|
||||
UpdateScrollbars;
|
||||
end;
|
||||
|
||||
//for x
|
||||
if APnt.X<FRectMain.Left then
|
||||
begin
|
||||
NChars:= (FRectMain.Left-APnt.X) div FCharSize.X + 1 + AIndentHorz;
|
||||
FScrollHorz.NPos:= Max(0, FScrollHorz.NPos-NChars);
|
||||
UpdateScrollbars;
|
||||
end
|
||||
else
|
||||
if APnt.X>FRectMain.Right-FCharSize.X then
|
||||
begin
|
||||
NChars:= (APnt.X-FRectMain.Right) div FCharSize.X + 2 + AIndentHorz;
|
||||
Inc(FScrollHorz.NPos, NChars);
|
||||
UpdateScrollbars;
|
||||
end;
|
||||
|
||||
UpdateCaretsCoords();
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoGotoCaret(AEdge: TATCaretEdge);
|
||||
begin
|
||||
DoGotoPos(Carets.CaretAtEdge(AEdge), cScrollIndentCaretHorz, cScrollIndentCaretVert);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoGotoPos_AndUnfold(APnt: TPoint;
|
||||
AIndentHorz, AIndentVert: integer);
|
||||
begin
|
||||
if not Strings.IsIndexValid(APnt.Y) then Exit;
|
||||
|
||||
if IsLineFolded(APnt.Y, true) then
|
||||
begin
|
||||
DoUnfoldLine(APnt.Y);
|
||||
Update;
|
||||
end;
|
||||
|
||||
DoCaretSingle(APnt.X, APnt.Y);
|
||||
DoEventCarets;
|
||||
DoGotoPos(APnt, AIndentHorz, AIndentVert);
|
||||
Update;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCaretsDeleteOnSameLines;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= Carets.Count-1 downto 1{!} do
|
||||
begin
|
||||
if Carets[i].PosY=Carets[i-1].PosY then
|
||||
Carets.Delete(i);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCaretSingleAsIs;
|
||||
begin
|
||||
FSelRect:= cRectEmpty;
|
||||
|
||||
if Carets.Count=0 then
|
||||
Carets.Add(0, 0);
|
||||
|
||||
while Carets.Count>1 do
|
||||
Carets.Delete(Carets.Count-1);
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCaretSingle(APosX, APosY, AEndX, AEndY: integer; AUseEndXY: boolean);
|
||||
begin
|
||||
FSelRect:= cRectEmpty;
|
||||
|
||||
if Carets.Count=0 then
|
||||
Carets.Add(0, 0);
|
||||
|
||||
while Carets.Count>1 do
|
||||
Carets.Delete(Carets.Count-1);
|
||||
|
||||
with Carets[0] do
|
||||
begin
|
||||
PosX:= APosX;
|
||||
PosY:= APosY;
|
||||
if AUseEndXY then
|
||||
begin
|
||||
EndX:= AEndX;
|
||||
EndY:= AEndY;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCaretSingle(AX, AY: integer; AClearSelection: boolean);
|
||||
begin
|
||||
DoCaretSingle(AX, AY, -1, -1, AClearSelection);
|
||||
end;
|
||||
|
||||
function TATSynEdit.GetCaretSelectionIndex(P: TPoint): integer;
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
X1, Y1, X2, Y2, i: integer;
|
||||
bSel: boolean;
|
||||
begin
|
||||
Result:= -1;
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Item:= Carets[i];
|
||||
Item.GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then Continue;
|
||||
if IsPosInRange(P.X, P.Y, X1, Y1, X2, Y2)=cRelateInside then
|
||||
begin
|
||||
Result:= i;
|
||||
Break
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCaretSwapEdge(AMoveLeft: boolean): boolean;
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
bSel, bAtLeft: boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
if Carets.Count<>1 then Exit;
|
||||
|
||||
Item:= Carets[0];
|
||||
Item.GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then Exit;
|
||||
Result:= true;
|
||||
|
||||
bAtLeft:= IsPosSorted(Item.PosX, Item.PosY, Item.EndX, Item.EndY, true);
|
||||
|
||||
//Left/Rt pressed at left/rt side of selection?
|
||||
//yes: cancel selection, don't move caret
|
||||
if bAtLeft=AMoveLeft then
|
||||
begin
|
||||
Item.EndX:= -1;
|
||||
Item.EndY:= -1;
|
||||
Exit
|
||||
end;
|
||||
|
||||
//else swap edge
|
||||
SwapInt(Item.PosX, Item.EndX);
|
||||
SwapInt(Item.PosY, Item.EndY);
|
||||
|
||||
if not FOptKeyLeftRightSwapSelAndSelect then
|
||||
begin
|
||||
Item.EndX:= -1;
|
||||
Item.EndY:= -1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.GetCaretsArray: TATPointArray;
|
||||
begin
|
||||
if Assigned(FCarets) then
|
||||
Result:= FCarets.SaveToArray;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.SetCaretsArray(const L: TATPointArray);
|
||||
begin
|
||||
if Assigned(FCarets) then
|
||||
FCarets.LoadFromArray(L);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCaretsExtend(ADown: boolean; ALines: integer);
|
||||
var
|
||||
MoreCarets: TATCarets;
|
||||
X, Y: integer;
|
||||
i, j: integer;
|
||||
begin
|
||||
MoreCarets:= TATCarets.Create;
|
||||
try
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
with Carets[i] do
|
||||
begin
|
||||
for j:= 1 to ALines do
|
||||
begin
|
||||
X:= PosX;
|
||||
Y:= PosY+BoolToPlusMinusOne(ADown)*j;
|
||||
if (Y<0) or (Y>=Strings.Count) then Break;
|
||||
MoreCarets.Add(X, Y);
|
||||
end;
|
||||
end;
|
||||
|
||||
for i:= 0 to MoreCarets.Count-1 do
|
||||
with MoreCarets[i] do
|
||||
Carets.Add(PosX, PosY);
|
||||
Carets.Sort;
|
||||
finally
|
||||
FreeAndNil(MoreCarets);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCaretsAssign(NewCarets: TATCarets);
|
||||
begin
|
||||
Carets.Clear;
|
||||
if NewCarets.Count>0 then
|
||||
Carets.Assign(NewCarets)
|
||||
else
|
||||
DoCaretSingle(0, 0);
|
||||
end;
|
||||
|
||||
function TATSynEdit.IsCaretBlocked: boolean;
|
||||
begin
|
||||
Result:= FCaretStopUnfocused and not Focused;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.UpdateIncorrectCaretPositions;
|
||||
begin
|
||||
Carets.UpdateIncorrectPositions(Strings.Count-1);
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCaretsShift_CaretItem(Caret: TATCaretItem;
|
||||
APosX, APosY, AShiftX, AShiftY, AShiftBelowX: integer);
|
||||
begin
|
||||
//carets below src, apply ShiftY/ShiftBelowX
|
||||
if Caret.PosY>APosY then
|
||||
begin
|
||||
if AShiftY=0 then exit;
|
||||
|
||||
if Caret.PosY=APosY+1 then
|
||||
Inc(Caret.PosX, AShiftBelowX);
|
||||
|
||||
Inc(Caret.PosY, AShiftY);
|
||||
end
|
||||
else
|
||||
//carets on same line as src, apply ShiftX
|
||||
begin
|
||||
if Caret.PosX>APosX then
|
||||
Inc(Caret.PosX, AShiftX);
|
||||
end;
|
||||
|
||||
//same, but for EndX/EndY
|
||||
if Caret.EndY>APosY then
|
||||
begin
|
||||
if Caret.EndY=APosY+1 then
|
||||
Inc(Caret.EndX, AShiftBelowX);
|
||||
|
||||
Inc(Caret.EndY, AShiftY);
|
||||
end
|
||||
else
|
||||
begin
|
||||
if Caret.EndX>APosX then
|
||||
Inc(Caret.EndX, AShiftX);
|
||||
end;
|
||||
|
||||
if Caret.PosX<0 then Caret.PosX:= 0;
|
||||
if Caret.PosY<0 then Caret.PosY:= 0;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCaretsShift_MarkerItem(Mark: TATMarkerItem;
|
||||
APosX, APosY, AShiftX, AShiftY, AShiftBelowX: integer;
|
||||
APosAfter: TPoint);
|
||||
begin
|
||||
//marker below src, apply ShiftY/ShiftBelowX
|
||||
if Mark.PosY>APosY then
|
||||
begin
|
||||
if AShiftY=0 then exit;
|
||||
|
||||
if Mark.PosY=APosY+1 then
|
||||
Inc(Mark.PosX, AShiftBelowX);
|
||||
|
||||
Inc(Mark.PosY, AShiftY);
|
||||
end
|
||||
else
|
||||
//marker on same line as src
|
||||
if Mark.PosY=APosY then
|
||||
begin
|
||||
if Mark.PosX=APosX then
|
||||
begin
|
||||
Mark.PosX:= APosAfter.X;
|
||||
Mark.PosY:= APosAfter.Y;
|
||||
end
|
||||
else
|
||||
if Mark.PosX>=APosX then
|
||||
if AShiftY=0 then
|
||||
Inc(Mark.PosX, AShiftX)
|
||||
else
|
||||
begin
|
||||
Inc(Mark.PosX, -APosX+APosAfter.X);
|
||||
Inc(Mark.PosY, AShiftY);
|
||||
end;
|
||||
end;
|
||||
|
||||
if Mark.PosX<0 then Mark.PosX:= 0;
|
||||
if Mark.PosY<0 then Mark.PosY:= 0;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCaretsShift(APosX, APosY: integer; AShiftX,
|
||||
AShiftY: integer; APosAfter: TPoint; AShiftBelowX: integer);
|
||||
var
|
||||
NStart, i: integer;
|
||||
{$ifdef debug_markers_shift}
|
||||
S: string;
|
||||
{$endif}
|
||||
begin
|
||||
if APosX<0 then Exit;
|
||||
if APosY<0 then Exit;
|
||||
|
||||
//adjust carets
|
||||
//(optimized, from index NStart, to fast do on 200 carets)
|
||||
NStart:= Carets.IndexOfPosYAvg(APosY);
|
||||
if NStart>=0 then
|
||||
for i:= NStart to Carets.Count-1 do
|
||||
DoCaretsShift_CaretItem(Carets[i],
|
||||
APosX, APosY, AShiftX, AShiftY, AShiftBelowX);
|
||||
|
||||
{$ifdef debug_markers_shift}
|
||||
S:= '';
|
||||
for i:= 0 to Markers.Count-1 do
|
||||
S:= S+Format('mark[%d] %d %d, ', [i, Markers[i].PosX, Markers[i].PosY]);
|
||||
Application.Mainform.Caption:= S+' -- '+Format(
|
||||
'pos %d %d, shift %d %d, posafter %d %d',
|
||||
[APosX, APosY, AShiftX, AShiftY, APosAfter.X, APosAfter.Y]);
|
||||
{$endif}
|
||||
|
||||
//adjust markers
|
||||
//(cannot optimize, markers not sorted)
|
||||
for i:= 0 to Markers.Count-1 do
|
||||
DoCaretsShift_MarkerItem(Markers[i],
|
||||
APosX, APosY, AShiftX, AShiftY, AShiftBelowX, APosAfter);
|
||||
|
||||
for i:= 0 to Attribs.Count-1 do
|
||||
DoCaretsShift_MarkerItem(Attribs[i],
|
||||
APosX, APosY, AShiftX, AShiftY, AShiftBelowX, APosAfter);
|
||||
|
||||
for i:= 0 to FMarkedRange.Count-1 do
|
||||
DoCaretsShift_MarkerItem(FMarkedRange[i],
|
||||
APosX, APosY, AShiftX, AShiftY, AShiftBelowX, APosAfter);
|
||||
end;
|
||||
|
||||
586
ATSynEdit/atsynedit/atsynedit_carets.pas
Normal file
@@ -0,0 +1,586 @@
|
||||
unit ATSynEdit_Carets;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils,
|
||||
ATStringProc;
|
||||
|
||||
type
|
||||
TATPosRelation = (cRelateBefore, cRelateInside, cRelateAfter);
|
||||
|
||||
procedure SwapInt(var n1, n2: integer);
|
||||
function IsPosSorted(X1, Y1, X2, Y2: integer; AllowEq: boolean): boolean;
|
||||
function IsPosInRange(X, Y, X1, Y1, X2, Y2: integer): TATPosRelation;
|
||||
|
||||
|
||||
type
|
||||
{ TATCaretItem }
|
||||
|
||||
TATCaretItem = class
|
||||
public
|
||||
PosX, PosY, //caret blinking pos
|
||||
EndX, EndY: integer; //end of selection or -1
|
||||
CoordX, CoordY: integer; //screen coords
|
||||
CoordColumn: integer; //saved CoordX to use in keys Up/Down cmd
|
||||
procedure SelectToPoint(AX, AY: integer);
|
||||
procedure GetRange(out AX1, AY1, AX2, AY2: integer; out ASel: boolean);
|
||||
procedure GetSelLines(out AFrom, ATo: integer; AllowNoSel: boolean=false);
|
||||
end;
|
||||
|
||||
type
|
||||
TATCaretEdge = (
|
||||
cEdgeTop,
|
||||
cEdgeBottom,
|
||||
cEdgeLeft,
|
||||
cEdgeRight
|
||||
);
|
||||
|
||||
type
|
||||
{ TATCarets }
|
||||
|
||||
TATCarets = class
|
||||
private
|
||||
FList: TList;
|
||||
FManyAllowed: boolean;
|
||||
FOneLine: boolean;
|
||||
function GetItem(N: integer): TATCaretItem;
|
||||
procedure DeleteDups;
|
||||
function IsJoinNeeded(N1, N2: integer;
|
||||
out OutPosX, OutPosY, OutEndX, OutEndY: integer): boolean;
|
||||
public
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
procedure Clear;
|
||||
procedure Delete(N: integer);
|
||||
function Count: integer;
|
||||
function IsIndexValid(N: integer): boolean;
|
||||
property Items[N: integer]: TATCaretItem read GetItem; default;
|
||||
procedure Add(APosX, APosY: integer);
|
||||
procedure Add(XFrom, YFrom, XTo, YTo: integer);
|
||||
procedure Sort;
|
||||
procedure Assign(Obj: TATCarets);
|
||||
function IndexOfPosXY(APosX, APosY: integer; AUseEndXY: boolean= false): integer;
|
||||
function IndexOfPosYAvg(APosY: integer): integer;
|
||||
function IndexOfLeftRight(ALeft: boolean): integer;
|
||||
function IsLineListed(APosY: integer): boolean;
|
||||
function IsSelection: boolean;
|
||||
function IsPosSelected(AX, AY: integer): boolean;
|
||||
function CaretAtEdge(AEdge: TATCaretEdge): TPoint;
|
||||
function DebugText: string;
|
||||
property ManyAllowed: boolean read FManyAllowed write FManyAllowed;
|
||||
property OneLine: boolean read FOneLine write FOneLine;
|
||||
function SaveToArray: TATPointArray;
|
||||
procedure LoadFromArray(const L: TATPointArray);
|
||||
procedure UpdateColumnCoord(ASaveColumn: boolean);
|
||||
procedure UpdateIncorrectPositions(AMaxLine: integer);
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math{%H-};
|
||||
|
||||
function IsPosSorted(X1, Y1, X2, Y2: integer; AllowEq: boolean): boolean;
|
||||
begin
|
||||
if Y1<>Y2 then
|
||||
Result:= Y1<Y2
|
||||
else
|
||||
Result:= (X1<X2) or (AllowEq and (X1=X2));
|
||||
end;
|
||||
|
||||
procedure GetPositionMinOrMax(X1, Y1, X2, Y2: integer; AMaximal: boolean; out OutX, OutY: integer);
|
||||
begin
|
||||
if IsPosSorted(X1, Y1, X2, Y2, true) xor AMaximal then
|
||||
begin
|
||||
OutX:= X1;
|
||||
OutY:= Y1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
OutX:= X2;
|
||||
OutY:= Y2;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function IsPosInRange(X, Y, X1, Y1, X2, Y2: integer): TATPosRelation;
|
||||
var
|
||||
b1, b2: boolean;
|
||||
begin
|
||||
b1:= IsPosSorted(X, Y, X1, Y1, false);
|
||||
b2:= IsPosSorted(X, Y, X2, Y2, false);
|
||||
if b1 then Result:= cRelateBefore else
|
||||
if b2 then Result:= cRelateInside else
|
||||
Result:= cRelateAfter;
|
||||
end;
|
||||
|
||||
procedure SwapInt(var n1, n2: integer);
|
||||
var
|
||||
n: integer;
|
||||
begin
|
||||
n:= n1;
|
||||
n1:= n2;
|
||||
n2:= n;
|
||||
end;
|
||||
|
||||
{ TATCaretItem }
|
||||
|
||||
procedure TATCaretItem.GetRange(out AX1, AY1, AX2, AY2: integer; out ASel: boolean);
|
||||
begin
|
||||
AX1:= PosX;
|
||||
AY1:= PosY;
|
||||
AX2:= EndX;
|
||||
AY2:= EndY;
|
||||
ASel:= false;
|
||||
|
||||
if (AX2<0) or (AY2<0) then Exit;
|
||||
if (AX1=AX2) and (AY1=AY2) then Exit;
|
||||
|
||||
ASel:= true;
|
||||
if IsPosSorted(AX2, AY2, AX1, AY1, false) then
|
||||
begin
|
||||
SwapInt(AX1, AX2);
|
||||
SwapInt(AY1, AY2);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATCaretItem.GetSelLines(out AFrom, ATo: integer;
|
||||
AllowNoSel: boolean = false);
|
||||
var
|
||||
X1, Y1, X2, Y2: integer;
|
||||
bSel: boolean;
|
||||
begin
|
||||
AFrom:= -1;
|
||||
ATo:= -1;
|
||||
|
||||
GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then
|
||||
begin
|
||||
if AllowNoSel then
|
||||
begin AFrom:= PosY; ATo:= PosY; end;
|
||||
Exit
|
||||
end;
|
||||
|
||||
AFrom:= Y1;
|
||||
ATo:= Y2;
|
||||
//sel ended at line-start?
|
||||
if (X2=0) and (Y2>0) then Dec(ATo);
|
||||
end;
|
||||
|
||||
procedure TATCaretItem.SelectToPoint(AX, AY: integer);
|
||||
begin
|
||||
if EndX<0 then EndX:= PosX;
|
||||
if EndY<0 then EndY:= PosY;
|
||||
PosX:= AX;
|
||||
PosY:= AY;
|
||||
end;
|
||||
|
||||
{ TATCarets }
|
||||
|
||||
function TATCarets.GetItem(N: integer): TATCaretItem;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
Result:= TATCaretItem(FList[N])
|
||||
else
|
||||
Result:= nil;
|
||||
end;
|
||||
|
||||
constructor TATCarets.Create;
|
||||
begin
|
||||
inherited;
|
||||
FList:= TList.Create;
|
||||
FManyAllowed:= true;
|
||||
FOneLine:= false;
|
||||
end;
|
||||
|
||||
destructor TATCarets.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
FreeAndNil(FList);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TATCarets.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= FList.Count-1 downto 0 do
|
||||
Delete(i);
|
||||
end;
|
||||
|
||||
procedure TATCarets.Delete(N: integer);
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
begin
|
||||
TObject(FList[N]).Free;
|
||||
FList.Delete(N);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATCarets.Count: integer;
|
||||
begin
|
||||
Result:= FList.Count;
|
||||
end;
|
||||
|
||||
function TATCarets.IsIndexValid(N: integer): boolean;
|
||||
begin
|
||||
Result:= (N>=0) and (N<FList.Count);
|
||||
end;
|
||||
|
||||
procedure TATCarets.Add(APosX, APosY: integer);
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
begin
|
||||
if (not FManyAllowed) and (Count>=1) then Exit;
|
||||
|
||||
if FOneLine then APosY:= 0;
|
||||
|
||||
Item:= TATCaretItem.Create;
|
||||
Item.PosX:= APosX;
|
||||
Item.PosY:= APosY;
|
||||
Item.EndX:= -1;
|
||||
Item.EndY:= -1;
|
||||
|
||||
FList.Add(Item);
|
||||
end;
|
||||
|
||||
function _ListCaretsCompare(Item1, Item2: Pointer): Integer;
|
||||
var
|
||||
Obj1, Obj2: TATCaretItem;
|
||||
begin
|
||||
Obj1:= TATCaretItem(Item1);
|
||||
Obj2:= TATCaretItem(Item2);
|
||||
Result:= Obj1.PosY-Obj2.PosY;
|
||||
if Result=0 then
|
||||
Result:= Obj1.PosX-Obj2.PosX;
|
||||
end;
|
||||
|
||||
procedure TATCarets.Sort;
|
||||
begin
|
||||
FList.Sort(@_ListCaretsCompare);
|
||||
DeleteDups;
|
||||
end;
|
||||
|
||||
procedure TATCarets.DeleteDups;
|
||||
var
|
||||
i: integer;
|
||||
Item1, Item2: TATCaretItem;
|
||||
OutPosX, OutPosY, OutEndX, OutEndY: integer;
|
||||
begin
|
||||
for i:= Count-1 downto 1 do
|
||||
begin
|
||||
Item1:= GetItem(i);
|
||||
Item2:= GetItem(i-1);
|
||||
|
||||
if (Item1.PosY=Item2.PosY) and (Item1.PosX=Item2.PosX) then
|
||||
Delete(i);
|
||||
|
||||
if IsJoinNeeded(i, i-1, OutPosX, OutPosY, OutEndX, OutEndY) then
|
||||
begin
|
||||
Delete(i);
|
||||
Item2.PosX:= OutPosX;
|
||||
Item2.PosY:= OutPosY;
|
||||
Item2.EndX:= OutEndX;
|
||||
Item2.EndY:= OutEndY;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATCarets.Assign(Obj: TATCarets);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Clear;
|
||||
for i:= 0 to Obj.Count-1 do
|
||||
begin
|
||||
Add(0, 0);
|
||||
with Items[Count-1] do
|
||||
begin
|
||||
PosX:= Obj[i].PosX;
|
||||
PosY:= Obj[i].PosY;
|
||||
EndX:= Obj[i].EndX;
|
||||
EndY:= Obj[i].EndY;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATCarets.IndexOfPosXY(APosX, APosY: integer; AUseEndXY: boolean = false): integer;
|
||||
var
|
||||
iStart, i: integer;
|
||||
Item: TATCaretItem;
|
||||
XUse, YUse: integer;
|
||||
begin
|
||||
Result:= -1;
|
||||
|
||||
iStart:= 0;
|
||||
//todo--fix for case called from TimerScrollTick, dont work for cScrollUp
|
||||
//iStart:= IndexOfPosYAvg(APosY);
|
||||
//if iStart<0 then Exit;
|
||||
|
||||
for i:= iStart to Count-1 do
|
||||
begin
|
||||
Item:= Items[i];
|
||||
|
||||
if AUseEndXY and (Item.EndY>=0) then
|
||||
begin XUse:= Item.EndX; YUse:= Item.EndY; end
|
||||
else
|
||||
begin XUse:= Item.PosX; YUse:= Item.PosY; end;
|
||||
|
||||
if (YUse>APosY) then Break;
|
||||
if (XUse=APosX) and (YUse=APosY) then
|
||||
begin Result:= i; Break end;
|
||||
end;
|
||||
end;
|
||||
|
||||
//todo-- binary search
|
||||
function TATCarets.IndexOfPosYAvg(APosY: integer): integer;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= -1;
|
||||
for i:= 0 to FList.Count-1 do
|
||||
if TATCaretItem(FList[i]).PosY>=APosY then
|
||||
begin Result:= i; Exit end;
|
||||
end;
|
||||
|
||||
function TATCarets.IndexOfLeftRight(ALeft: boolean): integer;
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
i, NPos: integer;
|
||||
Upd: boolean;
|
||||
begin
|
||||
Result:= -1;
|
||||
if Count>0 then
|
||||
NPos:= Items[0].PosX;
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
Item:= Items[i];
|
||||
if ALeft then
|
||||
Upd:= Item.PosX<=NPos
|
||||
else
|
||||
Upd:= Item.PosX>=NPos;
|
||||
if Upd then
|
||||
begin
|
||||
Result:= i;
|
||||
NPos:= Item.PosX;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATCarets.IsLineListed(APosY: integer): boolean;
|
||||
var
|
||||
i: integer;
|
||||
Item: TATCaretItem;
|
||||
begin
|
||||
Result:= false;
|
||||
for i:= 0 to FList.Count-1 do
|
||||
begin
|
||||
Item:= TATCaretItem(FList[i]);
|
||||
if Item.PosY=APosY then
|
||||
begin
|
||||
Result:= true;
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATCarets.IsSelection: boolean;
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
Item:= Items[i];
|
||||
if (Item.EndX<0) or (Item.EndY<0) then Continue;
|
||||
if (Item.PosX<>Item.EndX) or (Item.PosY<>Item.EndY) then
|
||||
begin Result:= true; Exit end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATCarets.IsPosSelected(AX, AY: integer): boolean;
|
||||
var
|
||||
X1, Y1, X2, Y2: integer;
|
||||
bSel: boolean;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
Items[i].GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then Continue;
|
||||
|
||||
//carets sorted: can stop
|
||||
if Y1>AY then Exit;
|
||||
|
||||
if IsPosInRange(AX, AY, X1, Y1, X2, Y2)=cRelateInside then
|
||||
begin Result:= true; Break end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TATCarets.CaretAtEdge(AEdge: TATCaretEdge): TPoint;
|
||||
var
|
||||
N: integer;
|
||||
begin
|
||||
Result:= Point(0, 0);
|
||||
case AEdge of
|
||||
cEdgeTop: N:= 0;
|
||||
cEdgeBottom: N:= Count-1;
|
||||
cEdgeLeft: N:= IndexOfLeftRight(true);
|
||||
cEdgeRight: N:= IndexOfLeftRight(false);
|
||||
end;
|
||||
if IsIndexValid(N) then
|
||||
with Items[N] do
|
||||
Result:= Point(PosX, PosY);
|
||||
end;
|
||||
|
||||
function TATCarets.IsJoinNeeded(N1, N2: integer;
|
||||
out OutPosX, OutPosY, OutEndX, OutEndY: integer): boolean;
|
||||
var
|
||||
Item1, Item2: TATCaretItem;
|
||||
XMin1, XMin2, YMin1, YMin2, XMax1, XMax2, YMax1, YMax2: integer;
|
||||
Sel1, Sel2: boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
if not IsIndexValid(N1) then Exit;
|
||||
if not IsIndexValid(N2) then Exit;
|
||||
|
||||
Item1:= Items[N1];
|
||||
Item2:= Items[N2];
|
||||
Item1.GetRange(XMin1, YMin1, XMax1, YMax1, Sel1);
|
||||
Item2.GetRange(XMin2, YMin2, XMax2, YMax2, Sel2);
|
||||
|
||||
//caret1 w/out selection inside caret2 selection?
|
||||
if not Sel1 and not Sel2 then Exit;
|
||||
if not Sel1 then
|
||||
begin
|
||||
Result:= IsPosInRange(Item1.PosX, Item1.PosY, XMin2, YMin2, XMax2, YMax2)=cRelateInside;
|
||||
if Result then
|
||||
begin OutPosX:= Item2.PosX; OutPosY:= Item2.PosY; OutEndX:= Item2.EndX; OutEndY:= Item2.EndY; end;
|
||||
Exit
|
||||
end;
|
||||
if not Sel2 then
|
||||
begin
|
||||
Result:= IsPosInRange(Item2.PosX, Item2.PosY, XMin1, YMin1, XMax1, YMax1)=cRelateInside;
|
||||
if Result then
|
||||
begin OutPosX:= Item1.PosX; OutPosY:= Item1.PosY; OutEndX:= Item1.EndX; OutEndY:= Item1.EndY; end;
|
||||
Exit
|
||||
end;
|
||||
|
||||
//calc join-result, needed only for Result=true
|
||||
//minimal point
|
||||
GetPositionMinOrMax(XMin1, YMin1, XMin2, YMin2, false, OutPosX, OutPosY);
|
||||
//maximal point
|
||||
GetPositionMinOrMax(XMax1, YMax1, XMax2, YMax2, true, OutEndX, OutEndY);
|
||||
|
||||
//swap points?
|
||||
if not IsPosSorted(Item1.PosX, Item1.PosY, Item1.EndX, Item1.EndY, false) then
|
||||
begin
|
||||
SwapInt(OutPosX, OutEndX);
|
||||
SwapInt(OutPosY, OutEndY);
|
||||
end;
|
||||
|
||||
if IsPosSorted(XMax1, YMax1, XMin2, YMin2, false) then Exit; //ranges not overlap [x1, y1]...[x2, y2]
|
||||
if IsPosSorted(XMax2, YMax2, XMin1, YMin1, false) then Exit; //ranges not overlap [x2, y2]...[x1, y1]
|
||||
Result:= true; //ranges overlap
|
||||
end;
|
||||
|
||||
function TATCarets.DebugText: string;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= '';
|
||||
for i:= 0 to Count-1 do
|
||||
with Items[i] do
|
||||
Result:= Result+Format('caret[%d] pos %d:%d end %d:%d', [
|
||||
i, posy, posx, endy, endx
|
||||
])+sLineBreak;
|
||||
end;
|
||||
|
||||
function TATCarets.SaveToArray: TATPointArray;
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
i: integer;
|
||||
begin
|
||||
SetLength(Result, Count*2);
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
Item:= Items[i];
|
||||
Result[i*2].X:= Item.PosX;
|
||||
Result[i*2].Y:= Item.PosY;
|
||||
Result[i*2+1].X:= Item.EndX;
|
||||
Result[i*2+1].Y:= Item.EndY;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATCarets.LoadFromArray(const L: TATPointArray);
|
||||
var
|
||||
i: integer;
|
||||
Item: TATCaretItem;
|
||||
begin
|
||||
Clear;
|
||||
for i:= 0 to Length(L) div 2 - 1 do
|
||||
begin
|
||||
Add(0, 0);
|
||||
Item:= Items[Count-1];
|
||||
Item.PosX:= L[i*2].X;
|
||||
Item.PosY:= L[i*2].Y;
|
||||
Item.EndX:= L[i*2+1].X;
|
||||
Item.EndY:= L[i*2+1].Y;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATCarets.Add(XFrom, YFrom, XTo, YTo: integer);
|
||||
begin
|
||||
if (XFrom=XTo) and (YFrom=YTo) then Exit;
|
||||
Add(0, 0);
|
||||
with Items[Count-1] do
|
||||
begin
|
||||
PosX:= XTo;
|
||||
PosY:= YTo;
|
||||
EndX:= XFrom;
|
||||
EndY:= YFrom;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATCarets.UpdateColumnCoord(ASaveColumn: boolean);
|
||||
var
|
||||
i: integer;
|
||||
Caret: TATCaretItem;
|
||||
begin
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
Caret:= Items[i];
|
||||
if ASaveColumn then
|
||||
begin
|
||||
if Caret.CoordColumn=0 then
|
||||
Caret.CoordColumn:= Caret.CoordX;
|
||||
end
|
||||
else
|
||||
Caret.CoordColumn:= 0
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATCarets.UpdateIncorrectPositions(AMaxLine: integer);
|
||||
var
|
||||
i: integer;
|
||||
Caret: TATCaretItem;
|
||||
begin
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
Caret:= Items[i];
|
||||
if Caret.PosY>AMaxLine then Caret.PosY:= AMaxLine;
|
||||
if Caret.EndY>AMaxLine then Caret.EndY:= AMaxLine;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
166
ATSynEdit/atsynedit/atsynedit_cmd_clipboard.inc
Normal file
@@ -0,0 +1,166 @@
|
||||
{$ifdef nn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.DoCommand_ClipboardPaste(AKeepCaret, ASelectThen: boolean): TATCommandResults;
|
||||
var
|
||||
Str: atString;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
//column block
|
||||
if Clipboard.HasFormat(cATClipboardFormatId) then
|
||||
begin
|
||||
if ModeOneLine then Exit;
|
||||
Result:= DoCommand_ClipboardPasteColumnBlock(AKeepCaret);
|
||||
Exit
|
||||
end;
|
||||
|
||||
//usual text
|
||||
Str:= UTF8Decode(Clipboard.AsText);
|
||||
if ModeOneLine then
|
||||
Str:= SRemoveNewlineChars(Str);
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
DoCommand_TextDeleteSelection;
|
||||
Result:= DoCommand_TextInsertAtCarets(Str,
|
||||
AKeepCaret,
|
||||
FOverwrite and FOptOverwriteAllowedOnPaste,
|
||||
ASelectThen);
|
||||
Strings.EndUndoGroup;
|
||||
|
||||
//workaround: paste on last line end
|
||||
if Carets.Count>0 then
|
||||
if Carets[0].PosY>=Strings.Count then
|
||||
Strings.ActionAddFakeLineIfNeeded;
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_ClipboardPasteColumnBlock(AKeepCaret: boolean): TATCommandResults;
|
||||
var
|
||||
Str: atString;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
Str:= UTF8Decode(Clipboard.AsText);
|
||||
Strings.BeginUndoGroup;
|
||||
Result:= DoCommand_TextInsertColumnBlockOnce(Str, AKeepCaret);
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_ClipboardCut: TATCommandResults;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
if Carets.IsSelection then
|
||||
begin
|
||||
DoCommand_ClipboardCopy;
|
||||
Result:= DoCommand_TextDeleteSelection;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if FOptCutLinesIfNoSel then
|
||||
begin
|
||||
DoCommand_ClipboardCopy;
|
||||
Result:= DoCommand_TextDeleteLines;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ClipboardCopy(Append: boolean): TATCommandResults;
|
||||
begin
|
||||
if not IsSelRectEmpty then
|
||||
begin
|
||||
Clipboard.AsText:= GetTextForClipboard;
|
||||
Clipboard.AddFormat(cATClipboardFormatId, cATClipboardSignatureColBlock, SizeOf(cATClipboardSignatureColBlock));
|
||||
end
|
||||
else
|
||||
begin
|
||||
if Append then
|
||||
Clipboard.AsText:= Clipboard.AsText+GetTextForClipboard
|
||||
else
|
||||
Clipboard.AsText:= GetTextForClipboard;
|
||||
end;
|
||||
|
||||
Result:= [];
|
||||
end;
|
||||
|
||||
function TATSynEdit.GetTextForClipboard: string;
|
||||
var
|
||||
ListNum: TList;
|
||||
ListStr: TStringList;
|
||||
Caret: TATCaretItem;
|
||||
i, NLen, X1, Y1, X2, Y2: integer;
|
||||
bSel: boolean;
|
||||
Str: atString;
|
||||
begin
|
||||
Result:= '';
|
||||
|
||||
if not IsSelRectEmpty then
|
||||
begin
|
||||
for i:= FSelRect.Top to FSelRect.Bottom do
|
||||
begin
|
||||
Str:= Strings.Lines[i];
|
||||
X1:= SColumnPosToCharPos(Str, FSelRect.Left, OptTabSize);
|
||||
X2:= SColumnPosToCharPos(Str, FSelRect.Right, OptTabSize);
|
||||
Str:= Strings.TextSubstring(X1, i, X2, i);
|
||||
NLen:= X2-X1-Length(Str);
|
||||
if NLen>0 then
|
||||
Str:= Str+StringOfChar(' ', NLen);
|
||||
Result:= Result+UTF8Encode(Str)+sLineBreak;
|
||||
end;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
ListNum:= TList.Create;
|
||||
ListStr:= TStringList.Create;
|
||||
|
||||
try
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
if ListNum.IndexOf(pointer{%H-}(Caret.PosY))<0 then
|
||||
ListNum.Add(pointer{%H-}(Caret.PosY));
|
||||
end;
|
||||
|
||||
//no selections-- copy entire lines
|
||||
if not Carets.IsSelection then
|
||||
begin
|
||||
if FOptCopyLinesIfNoSel then
|
||||
begin
|
||||
for i:= 0 to ListNum.Count-1 do
|
||||
begin
|
||||
Str:= Strings.Lines[NativeInt{%H-}(ListNum[i])];
|
||||
if Str<>'' then
|
||||
ListStr.Add(UTF8Encode(Str));
|
||||
end;
|
||||
Result:= ListStr.Text; //always use Text, need eol
|
||||
end;
|
||||
end
|
||||
else
|
||||
//selections-- copy selected ranges
|
||||
begin
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then Continue;
|
||||
|
||||
Str:= Strings.TextSubstring(X1, Y1, X2, Y2);
|
||||
if Str<>'' then
|
||||
ListStr.Add(UTF8Encode(Str));
|
||||
end;
|
||||
|
||||
if ListStr.Count=1 then
|
||||
Result:= ListStr[0] //don't use Text to skip eol
|
||||
else
|
||||
Result:= ListStr.Text;
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(ListStr);
|
||||
FreeAndNil(ListNum);
|
||||
end;
|
||||
end;
|
||||
|
||||
771
ATSynEdit/atsynedit/atsynedit_cmd_editing.inc
Normal file
@@ -0,0 +1,771 @@
|
||||
{$ifdef nnn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.DoCommand_TextTabulation: TATCommandResults;
|
||||
var
|
||||
N1, N2: integer;
|
||||
begin
|
||||
//multiline selection?
|
||||
//instead of tabulation, do indent
|
||||
if FOptKeyTabIndents then
|
||||
if Carets.Count=1 then
|
||||
begin
|
||||
Carets[0].GetSelLines(N1, N2);
|
||||
if (N1>=0) and (N2>N1) then
|
||||
begin
|
||||
Result:= DoCommand_TextIndentUnindent(true);
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
|
||||
if FOptTabSpaces then
|
||||
Result:= DoCommand_TextInsertTabSpacesAtCarets(FOverwrite)
|
||||
else
|
||||
Result:= DoCommand_TextInsertAtCarets(#9, false, FOverwrite, false);
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_TextInsertAtCarets(const AText: atString;
|
||||
AKeepCaret, AOvrMode, ASelectThen: boolean): TATCommandResults;
|
||||
var
|
||||
List: TStringList;
|
||||
//
|
||||
function TextItem(i: integer): atString;
|
||||
begin
|
||||
if Assigned(List) and (Carets.Count=List.Count) and (i>=0) and (i<List.Count) then
|
||||
Result:= UTF8Decode(List[i])
|
||||
else
|
||||
Result:= AText;
|
||||
end;
|
||||
//
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Shift, PosAfter: TPoint;
|
||||
bNeedGroup: boolean;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
List:= nil;
|
||||
|
||||
bNeedGroup:= (Carets.Count>1) or (Carets.IsSelection);
|
||||
if bNeedGroup then Strings.BeginUndoGroup;
|
||||
|
||||
DoSelectionDeleteOrReset;
|
||||
|
||||
//list allows to insert each clip-line into one caret
|
||||
if (AText<>sLineBreak) and (Length(AText)>1) and (Carets.Count>1) then
|
||||
begin
|
||||
List:= TStringList.Create;
|
||||
List.Text:= UTF8Encode(AText);
|
||||
end;
|
||||
|
||||
try
|
||||
for i:= Carets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
Strings.TextInsert(Caret.PosX, Caret.PosY, TextItem(i), AOvrMode, Shift, PosAfter);
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter);
|
||||
|
||||
if not AKeepCaret then
|
||||
begin
|
||||
Caret.EndX:= IfThen(ASelectThen, Caret.PosX, -1);
|
||||
Caret.EndY:= IfThen(ASelectThen, Caret.PosY, -1);
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
if Assigned(List) then
|
||||
FreeAndNil(List);
|
||||
end;
|
||||
|
||||
if bNeedGroup then Strings.EndUndoGroup;
|
||||
|
||||
Result:= [cResultText, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextInsertTabSpacesAtCarets(AOvrMode: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Shift, PosAfter: TPoint;
|
||||
StrSpaces: atString;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
try
|
||||
DoSelectionDeleteOrReset;
|
||||
|
||||
for i:= Carets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
StrSpaces:= StringOfChar(' ', FTabSize - Caret.PosX mod FTabSize);
|
||||
Strings.TextInsert(Caret.PosX, Caret.PosY, StrSpaces, AOvrMode, Shift, PosAfter);
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter);
|
||||
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
Caret.EndX:= -1;
|
||||
Caret.EndY:= -1;
|
||||
end;
|
||||
finally
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCalcIndentCharsFromPrevLines(AX, AY: integer): integer;
|
||||
var
|
||||
Str: atString;
|
||||
NIndent, i: integer;
|
||||
begin
|
||||
Result:= -1;
|
||||
if not Strings.IsIndexValid(AY) then Exit;
|
||||
|
||||
//allow smart unindent only if caret on 1st nonspace char
|
||||
//(else Bksp must delete 1 char)
|
||||
Str:= Strings.Lines[AY];
|
||||
NIndent:= SGetIndentChars(Str);
|
||||
if not ((AX=NIndent) and (NIndent>0)) then Exit;
|
||||
|
||||
//calc indent of N prev lines.
|
||||
//if indent<AX then ok
|
||||
for i:= 1 to FOptMaxLinesToCountUnindent do
|
||||
begin
|
||||
Dec(AY);
|
||||
if not Strings.IsIndexValid(AY) then Exit;
|
||||
|
||||
Str:= Strings.Lines[AY];
|
||||
NIndent:= SGetIndentChars(Str);
|
||||
if NIndent<AX then
|
||||
Exit(NIndent);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteLeft(ALen: integer; AAllowUnindent: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Shift, PosAfter: TPoint;
|
||||
NIndent, NDeleteLen: integer;
|
||||
bNeedGroup: boolean;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
//selection? delete it, exit.
|
||||
if Carets.IsSelection then
|
||||
begin
|
||||
Result:= DoCommand_TextDeleteSelection;
|
||||
Exit
|
||||
end;
|
||||
|
||||
bNeedGroup:= Carets.Count>1;
|
||||
if bNeedGroup then Strings.BeginUndoGroup;
|
||||
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
NDeleteLen:= ALen;
|
||||
|
||||
if AAllowUnindent then
|
||||
begin
|
||||
NIndent:= DoCalcIndentCharsFromPrevLines(Caret.PosX, Caret.PosY);
|
||||
if NIndent>=0 then
|
||||
if Caret.PosX>NIndent then
|
||||
NDeleteLen:= Caret.PosX-NIndent
|
||||
else
|
||||
NDeleteLen:= Caret.PosX;
|
||||
end;
|
||||
|
||||
Strings.TextDeleteLeft(Caret.PosX, Caret.PosY, NDeleteLen, Shift, PosAfter);
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter);
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
|
||||
if bNeedGroup then Strings.EndUndoGroup;
|
||||
|
||||
Result:= [cResultText, cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDelete: TATCommandResults;
|
||||
var
|
||||
bColBlock: boolean;
|
||||
begin
|
||||
bColBlock:= not IsSelRectEmpty;
|
||||
if bColBlock then
|
||||
if FSelRect.Left=FSelRect.Right then
|
||||
begin
|
||||
DoSelect_None;
|
||||
bColBlock:= false;
|
||||
end;
|
||||
|
||||
if bColBlock or Carets.IsSelection then
|
||||
Result:= DoCommand_TextDeleteSelection
|
||||
else
|
||||
Result:= DoCommand_TextDeleteRight(1);
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteSelection: TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Shift, PosAfter: TPoint;
|
||||
i: integer;
|
||||
AX1, AY1, AX2, AY2: integer;
|
||||
bSel: boolean;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
if not IsSelRectEmpty and not OptCaretManyAllowed then
|
||||
begin
|
||||
DoSelectionDeleteColumnBlock;
|
||||
Result:= [cResultText, cResultCaretTop];
|
||||
Exit
|
||||
end;
|
||||
|
||||
if not Carets.IsSelection then Exit;
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
try
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
Caret.GetRange(AX1, AY1, AX2, AY2, bSel);
|
||||
if not bSel then Continue;
|
||||
|
||||
Strings.TextDeleteRange(AX1, AY1, AX2, AY2, Shift, PosAfter);
|
||||
DoCaretsShift(AX1, AY1, Shift.X, Shift.Y, PosAfter);
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
Caret.EndX:= -1;
|
||||
Caret.EndY:= -1;
|
||||
end;
|
||||
finally
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretTop];
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteRight(ALen: integer): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
i, Len, ShiftBelowX: integer;
|
||||
Shift, PosAfter: TPoint;
|
||||
bNeedGroup: boolean;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
//selection? delete it, exit.
|
||||
if Carets.IsSelection then
|
||||
begin
|
||||
Result:= DoCommand_TextDeleteSelection;
|
||||
exit
|
||||
end;
|
||||
|
||||
bNeedGroup:= Carets.Count>1;
|
||||
if bNeedGroup then Strings.BeginUndoGroup;
|
||||
|
||||
for i:= 0 to FCarets.Count-1 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
|
||||
//offsetX for carets in line[PosY+1]
|
||||
ShiftBelowX:= 0;
|
||||
Len:= Length(Strings.Lines[Caret.PosY]);
|
||||
if Caret.PosX=Len then
|
||||
ShiftBelowX:= Len;
|
||||
|
||||
Strings.TextDeleteRight(Caret.PosX, Caret.PosY, ALen, Shift, PosAfter);
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter, ShiftBelowX);
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
|
||||
if bNeedGroup then Strings.EndUndoGroup;
|
||||
|
||||
Result:= [cResultText, cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextInsertEol(AKeepCaret: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Shift, PosAfter: TPoint;
|
||||
Str: atString;
|
||||
bNeedGroup: boolean;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
if ModeOneLine then Exit;
|
||||
|
||||
bNeedGroup:= Carets.Count>1;
|
||||
if bNeedGroup then Strings.BeginUndoGroup;
|
||||
|
||||
DoSelectionDeleteOrReset;
|
||||
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
Str:= GetAutoIndentString(Caret.PosX, Caret.PosY);
|
||||
Strings.TextInsertEol(Caret.PosX, Caret.PosY, AKeepCaret, Str, Shift, PosAfter);
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter);
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
|
||||
if bNeedGroup then Strings.EndUndoGroup;
|
||||
|
||||
Result:= [cResultText, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteLines: TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Shift, PosAfter: TPoint;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
try
|
||||
DoCaretsDeleteOnSameLines;
|
||||
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
Strings.TextDeleteLine(Caret.PosX, Caret.PosY, Shift, PosAfter);
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter);
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
finally
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretTop];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDuplicateLine: TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Shift, PosAfter: TPoint;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
try
|
||||
DoCaretsDeleteOnSameLines;
|
||||
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
Strings.TextDuplicateLine(Caret.PosX, Caret.PosY, Shift, PosAfter);
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter);
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
finally
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteToLineBegin: TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Str: atString;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
DoCaretsDeleteOnSameLines;
|
||||
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
Str:= Strings.Lines[Caret.PosY];
|
||||
Delete(Str, 1, Caret.PosX);
|
||||
Strings.Lines[Caret.PosY]:= Str;
|
||||
Caret.PosX:= 0;
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretLeft];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteToLineEnd: TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Str: atString;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
DoCaretsDeleteOnSameLines;
|
||||
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
Str:= Strings.Lines[Caret.PosY];
|
||||
if Caret.PosX<Length(Str) then
|
||||
begin
|
||||
Delete(Str, Caret.PosX+1, MaxInt);
|
||||
Strings.Lines[Caret.PosY]:= Str;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteWord(ANext: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Str: atString;
|
||||
Shift, PosAfter: TPoint;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
if not Strings.IsIndexValid(Caret.PosY) then Continue;
|
||||
Str:= Strings.Lines[Caret.PosY];
|
||||
Shift.X:= 0;
|
||||
Shift.Y:= 0;
|
||||
PosAfter.X:= Caret.PosX;
|
||||
PosAfter.Y:= Caret.PosY;
|
||||
|
||||
//delete to prev line?
|
||||
if (Caret.PosX=0) and (not ANext) then
|
||||
begin
|
||||
Strings.TextDeleteLeft(Caret.PosX, Caret.PosY, 1, Shift, PosAfter);
|
||||
end
|
||||
else
|
||||
//delete to next line?
|
||||
if (Caret.PosX>=Length(Str)) and ANext then
|
||||
begin
|
||||
Strings.TextDeleteRight(Caret.PosX, Caret.PosY, 1, Shift, PosAfter);
|
||||
end
|
||||
else
|
||||
//jump from beyond eol to eol?
|
||||
if (Caret.PosX>Length(Str)) and (not ANext) then
|
||||
begin
|
||||
Caret.PosX:= Length(Str);
|
||||
end
|
||||
else
|
||||
//delete inside line?
|
||||
if (Caret.PosX<=Length(Str)) then
|
||||
begin
|
||||
PosAfter.X:= SFindWordOffset(Str, Caret.PosX, ANext, false, FOptWordChars);
|
||||
if PosAfter.X<>Caret.PosX then
|
||||
begin
|
||||
System.Delete(Str, Min(Caret.PosX, PosAfter.X)+1, Abs(Caret.PosX-PosAfter.X));
|
||||
Strings.Lines[Caret.PosY]:= Str;
|
||||
Shift.X:= -Abs(Caret.PosX-PosAfter.X);
|
||||
PosAfter.X:= Min(Caret.PosX, PosAfter.X);
|
||||
end;
|
||||
end;
|
||||
|
||||
DoCaretsShift(Caret.PosX, Caret.PosY, Shift.X, Shift.Y, PosAfter);
|
||||
|
||||
if ((Caret.PosX<>PosAfter.X) or (Caret.PosY<>PosAfter.Y)) and
|
||||
(FCarets.IndexOfPosXY(PosAfter.X, PosAfter.Y)>=0) then
|
||||
begin
|
||||
if FCarets.Count>1 then
|
||||
FCarets.Delete(i);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
end;
|
||||
|
||||
if ANext then
|
||||
Result:= [cResultText, cResultCaretBottom]
|
||||
else
|
||||
Result:= [cResultText, cResultCaretTop];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextIndentUnindent(ARight: boolean): TATCommandResults;
|
||||
var
|
||||
Y1, Y2: integer;
|
||||
NDecSpaces, NMinSpaces, i: integer;
|
||||
Str: atString;
|
||||
Caret: TATCaretItem;
|
||||
NShiftInit, NShift1, NShift2, NIndent1, NIndent2: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
DoCaretSingleAsIs;
|
||||
|
||||
Caret:= Carets[0];
|
||||
Caret.GetSelLines(Y1, Y2, true{Allow no sel});
|
||||
if Y1<0 then Exit;
|
||||
|
||||
if Caret.EndY<0 then
|
||||
begin
|
||||
Caret.EndX:= Caret.PosX;
|
||||
Caret.EndY:= Caret.PosY;
|
||||
end;
|
||||
|
||||
if FOptIndentSize>=0 then
|
||||
NDecSpaces:= FOptIndentSize
|
||||
else
|
||||
NDecSpaces:= Abs(FOptIndentSize)*FTabSize;
|
||||
|
||||
//calc minimal indent of all
|
||||
NMinSpaces:= MaxInt;
|
||||
for i:= Y1 to Y2 do
|
||||
begin
|
||||
Str:= Strings.Lines[i];
|
||||
if Trim(Str)='' then Continue;
|
||||
NMinSpaces:= Min(NMinSpaces, SGetIndentExpanded(Str, FTabSize));
|
||||
end;
|
||||
if NMinSpaces=MaxInt then Exit;
|
||||
|
||||
//consider "Unindent keeps align"
|
||||
if FOptIndentKeepsAlign then
|
||||
if (not ARight) and (NMinSpaces<NDecSpaces) then Exit;
|
||||
|
||||
//calc shifts (emulate Laz ide indent)
|
||||
NIndent1:= SGetIndentChars(Strings.Lines[Caret.PosY]);
|
||||
NIndent2:= SGetIndentChars(Strings.Lines[Caret.EndY]);
|
||||
NShiftInit:= Abs(FOptIndentSize) * IfThen(ARight, 1, -1);
|
||||
NShift1:= IfThen((Caret.PosX>=NIndent1) and (Caret.PosX>0), NShiftInit, 0);
|
||||
NShift2:= IfThen((Caret.EndX>=NIndent2), NShiftInit, 0);
|
||||
|
||||
//do indent
|
||||
Strings.BeginUndoGroup;
|
||||
try
|
||||
for i:= Y1 to Y2 do
|
||||
begin
|
||||
Str:= Strings.Lines[i];
|
||||
if Trim(Str)='' then Continue;
|
||||
Str:= SIndentUnindent(Str, ARight, FOptIndentSize, FTabSize);
|
||||
if Strings.Lines[i]<>Str then
|
||||
Strings.Lines[i]:= Str;
|
||||
end;
|
||||
finally
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
|
||||
//correct selection
|
||||
Caret.PosX:= Max(0, Caret.PosX+NShift1);
|
||||
Caret.EndX:= Max(0, Caret.EndX+NShift2);
|
||||
|
||||
Result:= [cResultText, cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_Undo: TATCommandResults;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
Strings.Undo(FOptUndoGrouped);
|
||||
Result:= [cResultText, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_Redo: TATCommandResults;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
Strings.SetGroupMark;
|
||||
Strings.Redo(FOptUndoGrouped);
|
||||
Result:= [cResultText, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_TextInsertColumnBlockOnce(const AText: atString;
|
||||
AKeepCaret: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Block: TATStrings;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
//cannot handle carets/selections for colblock
|
||||
DoCaretSingleAsIs;
|
||||
DoSelect_None;
|
||||
Caret:= FCarets[0];
|
||||
|
||||
Block:= TATStrings.Create;
|
||||
try
|
||||
Block.LoadFromString(AText);
|
||||
Block.ActionDeleteFakeLine;
|
||||
if Block.Count=0 then Exit;
|
||||
|
||||
Strings.TextInsertColumnBlock(Caret.PosX, Caret.PosY, Block, FOverwrite);
|
||||
if not AKeepCaret then
|
||||
Inc(Caret.PosY, Block.Count-1);
|
||||
finally
|
||||
FreeAndNil(Block);
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_TextDeleteToFileEnd: TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Str: atString;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
DoCaretSingleAsIs;
|
||||
|
||||
Caret:= FCarets[0];
|
||||
Str:= Strings.Lines[Caret.PosY];
|
||||
if Caret.PosX<Length(Str) then
|
||||
begin
|
||||
Delete(Str, Caret.PosX+1, MaxInt);
|
||||
Strings.Lines[Caret.PosY]:= Str;
|
||||
end;
|
||||
|
||||
for i:= Strings.Count-1 downto Caret.PosY+1 do
|
||||
Strings.LineDelete(i);
|
||||
|
||||
if Caret.PosY>=Strings.Count-1 then
|
||||
Strings.LinesEnds[Caret.PosY]:= cEndNone;
|
||||
|
||||
Result:= [cResultText, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_TextInsertEmptyAboveBelow(ADown: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
PosAfter: TPoint;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
DoCaretsDeleteOnSameLines;
|
||||
|
||||
for i:= Carets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
Strings.LineInsert(Caret.PosY + IfThen(ADown, 1), '');
|
||||
|
||||
PosAfter.X:= 0;
|
||||
PosAfter.Y:= Caret.PosY+IfThen(ADown, 1);
|
||||
|
||||
DoCaretsShift(0, Caret.PosY, 0, 1, PosAfter);
|
||||
|
||||
Caret.PosX:= PosAfter.X;
|
||||
Caret.PosY:= PosAfter.Y;
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextChangeCase(AMode: TATCaseConvert): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Str1, Str2: atString;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
Shift, PosAfter: TPoint;
|
||||
bSel: boolean;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
for i:= Carets.Count-1 downto 0 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then
|
||||
begin
|
||||
SFindWordBounds(Strings.Lines[Caret.PosY], Caret.PosX, X1, X2, FOptWordChars);
|
||||
if X1<0 then Continue;
|
||||
Y1:= Caret.PosY;
|
||||
Y2:= Caret.PosY;
|
||||
end;
|
||||
|
||||
Str1:= Strings.TextSubstring(X1, Y1, X2, Y2);
|
||||
case AMode of
|
||||
cCaseLower: Str2:= UnicodeLowerCase(Str1);
|
||||
cCaseUpper: Str2:= UnicodeUpperCase(Str1);
|
||||
cCaseTitle: Str2:= SCaseTitle(Str1, FOptWordChars);
|
||||
cCaseInvert: Str2:= SCaseInvert(Str1);
|
||||
cCaseSentence: Str2:= SCaseSentence(Str1, FOptWordChars);
|
||||
end;
|
||||
if Str1=Str2 then Continue;
|
||||
|
||||
Strings.TextDeleteRange(X1, Y1, X2, Y2, Shift, PosAfter);
|
||||
Strings.TextInsert(X1, Y1, Str2, false, Shift, PosAfter);
|
||||
end;
|
||||
Strings.EndUndoGroup;
|
||||
|
||||
Result:= [cResultText, cResultCaretAny];
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCommentSelectionLines(Act: TATCommentAction; const AComment: atString);
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
L: TStringList;
|
||||
NFrom, NTo, i: integer;
|
||||
Shift, PosAfter: TPoint;
|
||||
bChange: boolean;
|
||||
begin
|
||||
if Carets.Count=0 then exit;
|
||||
Caret:= Carets[0];
|
||||
Caret.GetSelLines(NFrom, NTo, true);
|
||||
if NFrom<0 then exit;
|
||||
if NTo<0 then exit;
|
||||
|
||||
L:= TStringList.Create;
|
||||
try
|
||||
for i:= NFrom to NTo do
|
||||
L.Add(Utf8Encode(Strings.Lines[i]));
|
||||
|
||||
bChange:= SCommentLineAction(L, AComment, Act);
|
||||
if not bChange then exit;
|
||||
Assert(L.Count=(NTo-NFrom+1), 'DoCommentSel changed line count');
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
try
|
||||
for i:= NFrom to NTo do
|
||||
Strings.Lines[i]:= Utf8Decode(L[i-NFrom]);
|
||||
finally
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
finally
|
||||
L.Free;
|
||||
end;
|
||||
|
||||
DoEventChange;
|
||||
Update(true);
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextTrimSpaces(AMode: TATTrimSpaces
|
||||
): TATCommandResults;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
if Strings.ActionTrimSpaces(AMode) then
|
||||
Result:= [cResultCaretAny, cResultText];
|
||||
end;
|
||||
|
||||
301
ATSynEdit/atsynedit/atsynedit_cmd_handler.inc
Normal file
@@ -0,0 +1,301 @@
|
||||
{$ifdef none}begin end;{$endif}
|
||||
|
||||
procedure TATSynEdit.DoMenuText;
|
||||
var
|
||||
P: TPoint;
|
||||
begin
|
||||
P:= ClientToScreen(Point(0, 0));
|
||||
if Assigned(FMenuText) then
|
||||
FMenuText.PopUp(P.X, P.Y)
|
||||
else
|
||||
FMenuStd.PopUp(P.X, P.Y);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.KeyDown(var Key: Word; Shift: TShiftState);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
if not FWantTabs then
|
||||
if Key=vk_tab then Exit;
|
||||
|
||||
if Key=vk_apps then
|
||||
begin
|
||||
DoMenuText;
|
||||
Exit
|
||||
end;
|
||||
|
||||
DoPaintModeStatic;
|
||||
try
|
||||
DoCommand(GetCommandFromKey(Key, Shift));
|
||||
finally
|
||||
DoPaintModeBlinking;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.UTF8KeyPress(var UTF8Key: TUTF8Char);
|
||||
var
|
||||
Str: atString;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
//skip control Ascii chars
|
||||
if Ord(UTF8Key[1])<32 then Exit;
|
||||
|
||||
Str:= Utf8Decode(Utf8Key);
|
||||
DoCommand(cCommand_TextInsert, Str);
|
||||
Utf8Key:= '';
|
||||
end;
|
||||
|
||||
function TATSynEdit.GetCommandFromKey(var Key: Word; Shift: TShiftState): integer;
|
||||
var
|
||||
Shortcut: TShortcut;
|
||||
begin
|
||||
Result:= 0;
|
||||
|
||||
if (Key=VK_TAB) and (Shift=[]) then
|
||||
begin
|
||||
Result:= cCommand_KeyTab;
|
||||
Key:= 0;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if IsEditableTextKey(Key) and ((Shift=[]) or (Shift=[ssShift])) then
|
||||
Exit;
|
||||
|
||||
if not Assigned(FKeymap) then
|
||||
begin
|
||||
ShowMessage('Keymap not assigned: cannot input keys');
|
||||
Exit
|
||||
end;
|
||||
|
||||
Shortcut:= KeyToShortCut(Key, Shift);
|
||||
Result:= FKeymap.GetCommandFromShortcut(Shortcut);
|
||||
if Result>0 then
|
||||
Key:= 0;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCommandResults(Res: TATCommandResults);
|
||||
begin
|
||||
if cResultText in Res then
|
||||
begin
|
||||
FWrapUpdateNeeded:= true;
|
||||
UpdateWrapInfo;
|
||||
|
||||
if FOptMouseHideCursor then
|
||||
if PtInRect(FRectMain, ScreenToClient(Mouse.CursorPos)) then
|
||||
SetCursor(crNone);
|
||||
end;
|
||||
|
||||
DoCaretsSort;
|
||||
UpdateCaretsCoords;
|
||||
|
||||
if cResultCaretLeft in Res then DoGotoCaret(cEdgeLeft);
|
||||
if cResultCaretRight in Res then DoGotoCaret(cEdgeRight);
|
||||
if cResultCaretTop in Res then DoGotoCaret(cEdgeTop);
|
||||
if cResultCaretBottom in Res then DoGotoCaret(cEdgeBottom);
|
||||
|
||||
if Res*[cResultCaretAny,
|
||||
cResultCaretLeft, cResultCaretRight,
|
||||
cResultCaretTop, cResultCaretBottom]<>[] then
|
||||
begin
|
||||
if not (cResultKeepColumnSel in Res) then
|
||||
FSelRect:= cRectEmpty;
|
||||
DoEventCarets;
|
||||
end;
|
||||
|
||||
if cResultText in Res then
|
||||
begin
|
||||
FSelRect:= cRectEmpty;
|
||||
DoEventChange;
|
||||
end;
|
||||
|
||||
if cResultScroll in Res then
|
||||
DoEventScroll;
|
||||
|
||||
if cResultState in Res then
|
||||
DoEventState;
|
||||
|
||||
Update;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCommand(ACmd: integer; const AText: atString = '');
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Res: TATCommandResults;
|
||||
i: integer;
|
||||
begin
|
||||
if ACmd<=0 then Exit;
|
||||
if DoEventCommand(ACmd, AText) then Exit;
|
||||
|
||||
Res:= [];
|
||||
FCaretSpecPos:= false;
|
||||
|
||||
//---handle special command bits (cCmdNNNNN)
|
||||
if ACmd and cCmdSelKeep <> 0 then
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
if Caret.EndY<0 then Caret.EndY:= Caret.PosY;
|
||||
if Caret.EndX<0 then Caret.EndX:= Caret.PosX;
|
||||
end;
|
||||
|
||||
if ACmd and cCmdSelReset <> 0 then
|
||||
DoSelect_None;
|
||||
|
||||
if ACmd and cCmdCaret <> 0 then
|
||||
begin
|
||||
Strings.SetGroupMark;
|
||||
|
||||
//save column for Up/Down movement
|
||||
Carets.UpdateColumnCoord(
|
||||
(ACmd=cCommand_KeyUp) or
|
||||
(ACmd=cCommand_KeyDown) or
|
||||
(ACmd=cCommand_KeyUp_Sel) or
|
||||
(ACmd=cCommand_KeyDown_Sel)
|
||||
);
|
||||
end;
|
||||
|
||||
//debug
|
||||
//if FCaretMoved then Beep;
|
||||
//--------
|
||||
|
||||
case ACmd of
|
||||
//most used commands
|
||||
cCommand_KeyLeft: Res:= DoCommand_KeyLeft(false);
|
||||
cCommand_KeyLeft_Sel: Res:= DoCommand_KeyLeft(true);
|
||||
cCommand_KeyRight: Res:= DoCommand_KeyRight(false);
|
||||
cCommand_KeyRight_Sel: Res:= DoCommand_KeyRight(true);
|
||||
cCommand_KeyUp,
|
||||
cCommand_KeyUp_Sel: Res:= DoCommand_KeyUpDown(false, 1, false);
|
||||
cCommand_KeyDown,
|
||||
cCommand_KeyDown_Sel: Res:= DoCommand_KeyUpDown(true, 1, false);
|
||||
cCommand_KeyHome,
|
||||
cCommand_KeyHome_Sel: Res:= DoCommand_KeyHome;
|
||||
cCommand_KeyEnd,
|
||||
cCommand_KeyEnd_Sel: Res:= DoCommand_KeyEnd;
|
||||
cCommand_KeyPageUp,
|
||||
cCommand_KeyPageUp_Sel: Res:= DoCommand_KeyUpDown(false, GetPageLines, FOptKeyPageKeepsRelativePos);
|
||||
cCommand_KeyPageDown,
|
||||
cCommand_KeyPageDown_Sel: Res:= DoCommand_KeyUpDown(true, GetPageLines, FOptKeyPageKeepsRelativePos);
|
||||
|
||||
cCommand_ColSelectLeft: Res:= DoCommand_SelectColumn(cDirColumnLeft);
|
||||
cCommand_ColSelectRight: Res:= DoCommand_SelectColumn(cDirColumnRight);
|
||||
cCommand_ColSelectUp: Res:= DoCommand_SelectColumn(cDirColumnUp);
|
||||
cCommand_ColSelectDown: Res:= DoCommand_SelectColumn(cDirColumnDown);
|
||||
cCommand_ColSelectPageUp: Res:= DoCommand_SelectColumn(cDirColumnPageUp);
|
||||
cCommand_ColSelectPageDown: Res:= DoCommand_SelectColumn(cDirColumnPageDown);
|
||||
cCommand_ColSelectToLineBegin: Res:= DoCommand_SelectColumnToLineEdge(false);
|
||||
cCommand_ColSelectToLineEnd: Res:= DoCommand_SelectColumnToLineEdge(true);
|
||||
|
||||
cCommand_TextInsert: Res:= DoCommand_TextInsertAtCarets(AText, false, FOverwrite, false);
|
||||
cCommand_TextInsertTabChar: Res:= DoCommand_TextInsertAtCarets(#9, false, FOverwrite, false);
|
||||
cCommand_KeyBackspace: Res:= DoCommand_TextBackspace;
|
||||
cCommand_KeyDelete: Res:= DoCommand_TextDelete;
|
||||
cCommand_KeyTab: Res:= DoCommand_TextTabulation;
|
||||
cCommand_KeyEnter: Res:= DoCommand_TextInsertEol(false);
|
||||
|
||||
cCommand_Undo: Res:= DoCommand_Undo;
|
||||
cCommand_Redo: Res:= DoCommand_Redo;
|
||||
//end of most used
|
||||
|
||||
cCommand_TextDeleteSelection: Res:= DoCommand_TextDeleteSelection;
|
||||
cCommand_TextDeleteLine: Res:= DoCommand_TextDeleteLines;
|
||||
cCommand_TextDuplicateLine: Res:= DoCommand_TextDuplicateLine;
|
||||
cCommand_TextDeleteToLineBegin: Res:= DoCommand_TextDeleteToLineBegin;
|
||||
cCommand_TextDeleteToLineEnd: Res:= DoCommand_TextDeleteToLineEnd;
|
||||
cCommand_TextDeleteToTextEnd: Res:= DoCommand_TextDeleteToFileEnd;
|
||||
cCommand_TextDeleteWordPrev: Res:= DoCommand_TextDeleteWord(false);
|
||||
cCommand_TextDeleteWordNext: Res:= DoCommand_TextDeleteWord(true);
|
||||
|
||||
cCommand_SelectAll: Res:= DoCommand_SelectAll;
|
||||
cCommand_SelectNone: Res:= [cResultCaretAny];
|
||||
cCommand_SelectInverted: Res:= DoCommand_SelectInverted;
|
||||
cCommand_SelectSplitToLines: Res:= DoCommand_SelectSplitToLines;
|
||||
cCommand_SelectExtendByLine: Res:= DoCommand_SelectExtendByLine;
|
||||
cCommand_SelectWords: Res:= DoCommand_SelectWords;
|
||||
cCommand_SelectLines: Res:= DoCommand_SelectLines;
|
||||
|
||||
cCommand_GotoTextBegin,
|
||||
cCommand_GotoTextBegin_Sel: Res:= DoCommand_GotoTextBegin;
|
||||
cCommand_GotoTextEnd,
|
||||
cCommand_GotoTextEnd_Sel: Res:= DoCommand_GotoTextEnd;
|
||||
cCommand_GotoWordNext,
|
||||
cCommand_GotoWordNext_Sel: Res:= DoCommand_GotoWord(true);
|
||||
cCommand_GotoWordPrev,
|
||||
cCommand_GotoWordPrev_Sel: Res:= DoCommand_GotoWord(false);
|
||||
|
||||
cCommand_ToggleOverwrite: Res:= DoCommand_ToggleOverwrite;
|
||||
cCommand_ToggleReadOnly: Res:= DoCommand_ToggleReadOnly;
|
||||
cCommand_ToggleWordWrap: Res:= DoCommand_ToggleWordWrap;
|
||||
cCommand_ToggleUnprinted: Res:= DoCommand_ToggleUnprinted;
|
||||
cCommand_ToggleUnprintedSpaces: Res:= DoCommand_ToggleUnprintedSpaces;
|
||||
cCommand_ToggleUnprintedEnds: Res:= DoCommand_ToggleUnprintedEnds;
|
||||
cCommand_ToggleUnprintedEndDetails: Res:= DoCommand_ToggleUnprintedEndDetails;
|
||||
cCommand_ToggleLineNums: Res:= DoCommand_ToggleLineNums;
|
||||
cCommand_ToggleFolding: Res:= DoCommand_ToggleFolding;
|
||||
cCommand_ToggleRuler: Res:= DoCommand_ToggleRuler;
|
||||
cCommand_ToggleMinimap: Res:= DoCommand_ToggleMinimap;
|
||||
|
||||
cCommand_TextIndent: Res:= DoCommand_TextIndentUnindent(true);
|
||||
cCommand_TextUnindent: Res:= DoCommand_TextIndentUnindent(false);
|
||||
|
||||
cCommand_ScrollLineUp: Res:= DoCommand_ScrollVert(-1);
|
||||
cCommand_ScrollLineDown: Res:= DoCommand_ScrollVert(1);
|
||||
cCommand_ScrollToCaretTop: Res:= [cResultCaretTop];
|
||||
cCommand_ScrollToCaretBottom: Res:= [cResultCaretBottom];
|
||||
cCommand_ScrollToCaretLeft: Res:= [cResultCaretLeft];
|
||||
cCommand_ScrollToCaretRight: Res:= [cResultCaretRight];
|
||||
|
||||
cCommand_ClipboardCopy: Res:= DoCommand_ClipboardCopy;
|
||||
cCommand_ClipboardCopyAdd: Res:= DoCommand_ClipboardCopy(true);
|
||||
cCommand_ClipboardCut: Res:= DoCommand_ClipboardCut;
|
||||
cCommand_ClipboardPaste: Res:= DoCommand_ClipboardPaste(false, false);
|
||||
cCommand_ClipboardPaste_Select: Res:= DoCommand_ClipboardPaste(false, true);
|
||||
cCommand_ClipboardPaste_KeepCaret: Res:= DoCommand_ClipboardPaste(true, false);
|
||||
cCommand_ClipboardPaste_Column: Res:= DoCommand_ClipboardPasteColumnBlock(false);
|
||||
cCommand_ClipboardPaste_ColumnKeepCaret: Res:= DoCommand_ClipboardPasteColumnBlock(true);
|
||||
|
||||
cCommand_MoveSelectionUp: Res:= DoCommand_MoveSelectionUpDown(false);
|
||||
cCommand_MoveSelectiondown: Res:= DoCommand_MoveSelectionUpDown(true);
|
||||
cCommand_TextInsertEmptyAbove: Res:= DoCommand_TextInsertEmptyAboveBelow(false);
|
||||
cCommand_TextInsertEmptyBelow: Res:= DoCommand_TextInsertEmptyAboveBelow(true);
|
||||
|
||||
cCommand_TextCaseLower: Res:= DoCommand_TextChangeCase(cCaseLower);
|
||||
cCommand_TextCaseUpper: Res:= DoCommand_TextChangeCase(cCaseUpper);
|
||||
cCommand_TextCaseTitle: Res:= DoCommand_TextChangeCase(cCaseTitle);
|
||||
cCommand_TextCaseInvert: Res:= DoCommand_TextChangeCase(cCaseInvert);
|
||||
cCommand_TextCaseSentence: Res:= DoCommand_TextChangeCase(cCaseSentence);
|
||||
|
||||
cCommand_TextTrimSpacesLeft: Res:= DoCommand_TextTrimSpaces(cTrimLeft);
|
||||
cCommand_TextTrimSpacesRight: Res:= DoCommand_TextTrimSpaces(cTrimRight);
|
||||
cCommand_TextTrimSpacesAll: Res:= DoCommand_TextTrimSpaces(cTrimAll);
|
||||
|
||||
cCommand_FoldAll: Res:= DoCommand_FoldUnfoldAll(true);
|
||||
cCommand_UnfoldAll: Res:= DoCommand_FoldUnfoldAll(false);
|
||||
cCommand_FoldLevel2..
|
||||
cCommand_FoldLevel9: Res:= DoCommand_FoldLevel(ACmd-cCommand_FoldLevel2+1);
|
||||
|
||||
cCommand_Cancel: Res:= DoCommand_Cancel;
|
||||
cCommand_CaretsExtendUpLine: Res:= DoCommand_CaretsExtend(false, 1);
|
||||
cCommand_CaretsExtendUpPage: Res:= DoCommand_CaretsExtend(false, GetPageLines);
|
||||
cCommand_CaretsExtendUpToTop: Res:= DoCommand_CaretsExtend(false, Strings.Count);
|
||||
cCommand_CaretsExtendDownLine: Res:= DoCommand_CaretsExtend(true, 1);
|
||||
cCommand_CaretsExtendDownPage: Res:= DoCommand_CaretsExtend(true, GetPageLines);
|
||||
cCommand_CaretsExtendDownToEnd: Res:= DoCommand_CaretsExtend(true, Strings.Count);
|
||||
|
||||
cCommand_ZoomIn: Res:= DoCommand_SizeChange(true);
|
||||
cCommand_ZoomOut: Res:= DoCommand_SizeChange(false);
|
||||
cCommand_RepeatTextCommand: DoCommand(FLastTextCmd, FLastTextCmdText);
|
||||
end;
|
||||
|
||||
if cResultText in Res then
|
||||
if ACmd<>cCommand_RepeatTextCommand then
|
||||
begin
|
||||
FLastTextCmd:= ACmd;
|
||||
FLastTextCmdText:= AText;
|
||||
end;
|
||||
|
||||
DoCommandResults(Res);
|
||||
end;
|
||||
|
||||
|
||||
335
ATSynEdit/atsynedit/atsynedit_cmd_keys.inc
Normal file
@@ -0,0 +1,335 @@
|
||||
{$ifdef nnnn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.DoCommand_KeyHome: TATCommandResults;
|
||||
var
|
||||
i, NIndent, NWrapped: integer;
|
||||
Caret: TATCaretItem;
|
||||
Pnt: TPoint;
|
||||
EolPos: boolean;
|
||||
begin
|
||||
for i:= 0 to FCarets.Count-1 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
NWrapped:= 0;
|
||||
NIndent:= 0;
|
||||
|
||||
if FOptKeyHomeEndNavigateWrapped and (FWrapMode<>cWrapOff) then
|
||||
begin
|
||||
Pnt.X:= 0;
|
||||
Pnt.Y:= Caret.CoordY;
|
||||
Pnt:= ClientPosToCaretPos(Pnt, EolPos);
|
||||
NWrapped:= Pnt.X;
|
||||
end;
|
||||
|
||||
if FOptKeyHomeToNonSpace then
|
||||
NIndent:= SGetIndentChars(Strings.Lines[Caret.PosY]);
|
||||
|
||||
if (NWrapped>0) and (Caret.PosX>NWrapped) then
|
||||
Caret.PosX:= NWrapped
|
||||
else
|
||||
if (NIndent>0) and (Caret.PosX>NIndent) then
|
||||
Caret.PosX:= NIndent
|
||||
else
|
||||
Caret.PosX:= 0;
|
||||
end;
|
||||
|
||||
Result:= [cResultCaretLeft];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_KeyEnd: TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Str: atString;
|
||||
Pnt: TPoint;
|
||||
i, NLen, NWrapped: integer;
|
||||
EolPos: boolean;
|
||||
begin
|
||||
for i:= 0 to FCarets.Count-1 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
Str:= Strings.Lines[Caret.PosY];
|
||||
NLen:= 0;
|
||||
NWrapped:= 0;
|
||||
|
||||
if FOptKeyHomeEndNavigateWrapped and (FWrapMode<>cWrapOff) then
|
||||
begin
|
||||
Pnt.X:= ClientWidth;
|
||||
Pnt.Y:= Caret.CoordY;
|
||||
Pnt:= ClientPosToCaretPos(Pnt, EolPos);
|
||||
if Pnt.X<Length(Str) then
|
||||
NWrapped:= Pnt.X-1;
|
||||
//-1 here: need to jump not to last chr but to last-1 chr
|
||||
//(like Synwrite; to not blink caret at next part of wrapped line)
|
||||
end;
|
||||
|
||||
if FOptKeyEndToNonSpace then
|
||||
NLen:= SGetNonSpaceLength(Str);
|
||||
|
||||
if (NWrapped>0) and (Caret.PosX<NWrapped) then
|
||||
Caret.PosX:= NWrapped
|
||||
else
|
||||
if (NLen>0) and (Caret.PosX<NLen) then
|
||||
Caret.PosX:= NLen
|
||||
else
|
||||
Caret.PosX:= Length(Str);
|
||||
end;
|
||||
|
||||
Result:= [cResultCaretRight];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_KeyLeft(ASelCommand: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [cResultCaretLeft];
|
||||
|
||||
if not ASelCommand then
|
||||
begin
|
||||
if FOptKeyLeftRightSwapSel then
|
||||
if DoCaretSwapEdge(true) then Exit;
|
||||
DoSelect_None;
|
||||
end;
|
||||
|
||||
for i:= 0 to FCarets.Count-1 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
if (Caret.PosX>0) then
|
||||
Dec(Caret.PosX)
|
||||
else
|
||||
if (Caret.PosY>0) and not FCaretVirtual then
|
||||
begin
|
||||
Dec(Caret.PosY);
|
||||
Caret.PosX:= Length(Strings.Lines[Caret.PosY]);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_KeyRight(ASelCommand: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= [cResultCaretRight];
|
||||
|
||||
if not ASelCommand then
|
||||
begin
|
||||
if FOptKeyLeftRightSwapSel then
|
||||
if DoCaretSwapEdge(false) then Exit;
|
||||
DoSelect_None;
|
||||
end;
|
||||
|
||||
for i:= 0 to FCarets.Count-1 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
if (Caret.PosX<Length(Strings.Lines[Caret.PosY])) or FCaretVirtual then
|
||||
Inc(Caret.PosX)
|
||||
else
|
||||
if (Caret.PosY<Strings.Count-1) then
|
||||
begin
|
||||
Caret.PosX:= 0;
|
||||
Inc(Caret.PosY);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_KeyUpDown(ADown: boolean; ALines: integer;
|
||||
AKeepRelativePos: boolean): TATCommandResults;
|
||||
var
|
||||
NRelative: integer;
|
||||
begin
|
||||
FCaretSpecPos:= true;
|
||||
|
||||
if AKeepRelativePos then
|
||||
NRelative:= LinesFromTop;
|
||||
|
||||
//don't check here FWrapMode<>cWrapOff
|
||||
if FOptKeyUpDownNavigateWrapped then
|
||||
Result:= DoCommand_KeyUpDown_Wrapped(ADown, ALines)
|
||||
else
|
||||
Result:= DoCommand_KeyUpDown_NextLine(ADown, ALines);
|
||||
|
||||
if AKeepRelativePos then
|
||||
LinesFromTop:= NRelative;
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_KeyUpDown_NextLine(ADown: boolean; ALines: integer): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
i, Y: integer;
|
||||
begin
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Caret:= Carets[i];
|
||||
Y:= Caret.PosY;
|
||||
|
||||
repeat
|
||||
Y:= Y+ALines*BoolToPlusMinusOne(ADown);
|
||||
until not Strings.IsIndexValid(Y) or not IsLineFolded(Y);
|
||||
|
||||
if not IsLineFolded(Y) then
|
||||
begin
|
||||
if Y<0 then Y:= GetFirstUnfoldedLineNumber;
|
||||
if Y>=Strings.Count then Y:= GetLastUnfoldedLineNumber;
|
||||
Caret.PosY:= Y;
|
||||
end;
|
||||
end;
|
||||
|
||||
if ADown then
|
||||
Result:= [cResultCaretBottom]
|
||||
else
|
||||
Result:= [cResultCaretTop];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_KeyUpDown_Wrapped(ADown: boolean; ALines: integer): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Pnt: TPoint;
|
||||
i: integer;
|
||||
EolPos: boolean;
|
||||
begin
|
||||
for i:= 0 to FCarets.Count-1 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
|
||||
if IsPosFolded(Caret.PosX, Caret.PosY) then
|
||||
begin
|
||||
Caret.PosX:= 0;
|
||||
Caret.PosY:= GetNextUnfoldedLineNumber(Caret.PosY, ADown);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
Pnt.X:= Caret.CoordX;
|
||||
if FOptKeyUpDownKeepColumn and (Caret.CoordColumn>0) then
|
||||
Pnt.X:= Caret.CoordColumn;
|
||||
|
||||
Pnt.Y:= Caret.CoordY + ALines*FCharSize.Y*BoolToPlusMinusOne(ADown);
|
||||
Pnt:= ClientPosToCaretPos(Pnt, EolPos);
|
||||
if Pnt.Y<0 then Continue;
|
||||
|
||||
if EolPos and (Pnt.X>0) then
|
||||
Dec(Pnt.X); //-1 so up/down won't jump to eol pos (caret may paint on next line)
|
||||
|
||||
Caret.PosX:= Pnt.X;
|
||||
Caret.PosY:= Pnt.Y;
|
||||
end;
|
||||
|
||||
if ADown then
|
||||
Result:= [cResultCaretBottom]
|
||||
else
|
||||
Result:= [cResultCaretTop];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_TextBackspace: TATCommandResults;
|
||||
var
|
||||
bColBlock: boolean;
|
||||
begin
|
||||
bColBlock:= not IsSelRectEmpty;
|
||||
if bColBlock then
|
||||
if FSelRect.Left=FSelRect.Right then
|
||||
begin
|
||||
DoSelect_None;
|
||||
bColBlock:= false;
|
||||
end;
|
||||
|
||||
if bColBlock then
|
||||
Result:= DoCommand_TextDeleteSelection
|
||||
else
|
||||
Result:= DoCommand_TextDeleteLeft(1, FOptKeyBackspaceUnindent);
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_GotoTextBegin: TATCommandResults;
|
||||
var
|
||||
Item: TATSynWrapItem;
|
||||
begin
|
||||
Item:= FWrapInfo[0];
|
||||
if Assigned(Item) then
|
||||
DoCaretSingle(0, Item.NLineIndex, false);
|
||||
|
||||
FScrollHorz.NPos:= 0;
|
||||
FScrollVert.NPos:= 0;
|
||||
|
||||
Result:= [cResultCaretTop];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_GotoTextEnd: TATCommandResults;
|
||||
var
|
||||
Item: TATSynWrapItem;
|
||||
begin
|
||||
Item:= FWrapInfo[FWrapInfo.Count-1];
|
||||
if Assigned(Item) then
|
||||
DoCaretSingle(Length(Strings.Lines[Item.NLineIndex]), Item.NLineIndex, false);
|
||||
|
||||
Result:= [cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ScrollVert(ALines: integer): TATCommandResults;
|
||||
begin
|
||||
DoScrollByDelta(0, ALines);
|
||||
Result:= [cResultScroll];
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_GotoWord(ANext: boolean): TATCommandResults;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Str: atString;
|
||||
i: integer;
|
||||
begin
|
||||
for i:= 0 to FCarets.Count-1 do
|
||||
begin
|
||||
Caret:= FCarets[i];
|
||||
if not Strings.IsIndexValid(Caret.PosY) then Continue;
|
||||
Str:= Strings.Lines[Caret.PosY];
|
||||
|
||||
//jump to prev line?
|
||||
if (Caret.PosX=0) and (not ANext) then
|
||||
begin
|
||||
if Caret.PosY>0 then
|
||||
begin
|
||||
Dec(Caret.PosY);
|
||||
Caret.PosX:= Length(Strings.Lines[Caret.PosY]);
|
||||
end;
|
||||
end
|
||||
else
|
||||
//jump to next line?
|
||||
if (Caret.PosX>=Length(Str)) and ANext then
|
||||
begin
|
||||
if Caret.PosY<Strings.Count-1 then
|
||||
begin
|
||||
Inc(Caret.PosY);
|
||||
Caret.PosX:= SGetIndentChars(Strings.Lines[Caret.PosY]);
|
||||
end;
|
||||
end
|
||||
else
|
||||
//jump from beyond eol to eol?
|
||||
if (Caret.PosX>Length(Str)) and (not ANext) then
|
||||
begin
|
||||
Caret.PosX:= Length(Str);
|
||||
end
|
||||
else
|
||||
//jump inside line?
|
||||
if (Caret.PosX<=Length(Str)) then
|
||||
begin
|
||||
Caret.PosX:= SFindWordOffset(Str, Caret.PosX, ANext, true, FOptWordChars);
|
||||
end;
|
||||
end;
|
||||
|
||||
if ANext then
|
||||
Result:= [cResultCaretBottom]
|
||||
else
|
||||
Result:= [cResultCaretTop];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_Cancel: TATCommandResults;
|
||||
begin
|
||||
DoCaretSingleAsIs;
|
||||
DoSelect_None;
|
||||
|
||||
FMouseDragDropping:= false;
|
||||
UpdateCursor;
|
||||
|
||||
Result:= [cResultCaretTop];
|
||||
end;
|
||||
|
||||
|
||||
126
ATSynEdit/atsynedit/atsynedit_cmd_misc.inc
Normal file
@@ -0,0 +1,126 @@
|
||||
{$ifdef nnn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleOverwrite: TATCommandResults;
|
||||
begin
|
||||
ModeOverwrite:= not ModeOverwrite;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleWordWrap: TATCommandResults;
|
||||
begin
|
||||
if FWrapMode=cWrapOff then
|
||||
FWrapMode:= cWrapOn
|
||||
else
|
||||
FWrapMode:= cWrapOff;
|
||||
FWrapUpdateNeeded:= true;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleUnprinted: TATCommandResults;
|
||||
begin
|
||||
OptUnprintedVisible:= not OptUnprintedVisible;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleUnprintedSpaces: TATCommandResults;
|
||||
begin
|
||||
OptUnprintedSpaces:= not OptUnprintedSpaces;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleUnprintedEnds: TATCommandResults;
|
||||
begin
|
||||
OptUnprintedEnds:= not OptUnprintedEnds;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleUnprintedEndDetails: TATCommandResults;
|
||||
begin
|
||||
OptUnprintedEndsDetails:= not OptUnprintedEndsDetails;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleLineNums: TATCommandResults;
|
||||
begin
|
||||
with Gutter.Items[GutterBandNum] do
|
||||
Visible:= not Visible;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleFolding: TATCommandResults;
|
||||
begin
|
||||
with Gutter.Items[GutterBandFold] do
|
||||
Visible:= not Visible;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleRuler: TATCommandResults;
|
||||
begin
|
||||
OptRulerVisible:= not OptRulerVisible;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleMinimap: TATCommandResults;
|
||||
begin
|
||||
OptMinimapVisible:= not OptMinimapVisible;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_ToggleReadOnly: TATCommandResults;
|
||||
begin
|
||||
ModeReadOnly:= not ModeReadOnly;
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_CaretsExtend(ADown: boolean; ALines: integer): TATCommandResults;
|
||||
begin
|
||||
DoCaretsExtend(ADown, ALines);
|
||||
Result:= [cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SizeChange(AIncrease: boolean): TATCommandResults;
|
||||
begin
|
||||
DoSizeChange(AIncrease);
|
||||
Result:= [cResultState];
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_FoldUnfoldAll(ADoFold: boolean): TATCommandResults;
|
||||
var
|
||||
Ar: TATIntArray;
|
||||
R: TATSynRange;
|
||||
i: integer;
|
||||
begin
|
||||
if ADoFold then
|
||||
begin
|
||||
Ar:= Fold.FindRangesContainingLines(
|
||||
0, Strings.Count-1, nil,
|
||||
false{OnlyFolded}, true{TopLevelOnly}, cRngHasAnyOfLines);
|
||||
for i:= Low(Ar) to High(Ar) do
|
||||
begin
|
||||
R:= Fold.Items[Ar[i]];
|
||||
if not R.Folded then
|
||||
DoRangeFold(R);
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
for i:= 0 to Fold.Count-1 do
|
||||
begin
|
||||
R:= Fold.Items[i];
|
||||
if R.Folded then
|
||||
DoRangeUnfold(R);
|
||||
end;
|
||||
end;
|
||||
|
||||
Result:= [cResultCaretAny, cResultScroll];
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.DoCommand_FoldLevel(ALevel: integer): TATCommandResults;
|
||||
begin
|
||||
DoFoldForLevel(ALevel);
|
||||
Result:= [cResultCaretAny, cResultScroll];
|
||||
end;
|
||||
|
||||
|
||||
222
ATSynEdit/atsynedit/atsynedit_cmd_sel.inc
Normal file
@@ -0,0 +1,222 @@
|
||||
{$ifdef nnn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.DoCommand_SelectAll: TATCommandResults;
|
||||
begin
|
||||
DoSelect_All;
|
||||
Result:= [cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SelectInverted: TATCommandResults;
|
||||
begin
|
||||
DoSelect_Inverted;
|
||||
Result:= [cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SelectSplitToLines: TATCommandResults;
|
||||
begin
|
||||
DoSelect_SplitSelectionToLines;
|
||||
Result:= [cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SelectExtendByLine: TATCommandResults;
|
||||
begin
|
||||
DoSelect_ExtendSelectionByLine;
|
||||
Result:= [cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SelectColumn(ADir: TATSelectColumnDirection): TATCommandResults;
|
||||
var
|
||||
PntBegin, PntEnd: TPoint;
|
||||
N, NPageLines, i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if Carets.Count=0 then Exit;
|
||||
|
||||
if IsSelRectEmpty or (FSelRectBegin.X<0) then
|
||||
begin
|
||||
DoSelect_NormalSelToColumnSel(PntBegin, PntEnd);
|
||||
DoCaretSingleAsIs;
|
||||
DoSelect_None;
|
||||
FSelRectBegin:= PntBegin;
|
||||
FSelRectEnd:= PntEnd;
|
||||
end;
|
||||
|
||||
if ADir in [cDirColumnPageUp, cDirColumnPageDown] then
|
||||
NPageLines:= GetPageLines
|
||||
else
|
||||
NPageLines:= 1;
|
||||
|
||||
case ADir of
|
||||
cDirColumnLeft:
|
||||
begin
|
||||
N:= SColumnPosToCharPos(Strings.Lines[FSelRectEnd.Y], FSelRectEnd.X, OptTabSize);
|
||||
if N>0 then Dec(N);
|
||||
N:= SCharPosToColumnPos(Strings.Lines[FSelRectEnd.Y], N, OptTabSize);
|
||||
FSelRectEnd.X:= N;
|
||||
Result:= [cResultKeepColumnSel, cResultCaretLeft];
|
||||
end;
|
||||
cDirColumnRight:
|
||||
begin
|
||||
N:= SColumnPosToCharPos(Strings.Lines[FSelRectEnd.Y], FSelRectEnd.X, OptTabSize);
|
||||
Inc(N);
|
||||
N:= SCharPosToColumnPos(Strings.Lines[FSelRectEnd.Y], N, OptTabSize);
|
||||
FSelRectEnd.X:= N;
|
||||
Result:= [cResultKeepColumnSel, cResultCaretRight];
|
||||
end;
|
||||
cDirColumnUp,
|
||||
cDirColumnPageUp:
|
||||
begin
|
||||
for i:= 1 to NPageLines do
|
||||
if FSelRectEnd.Y>0 then
|
||||
begin
|
||||
Dec(FSelRectEnd.Y);
|
||||
FSelRectEnd.Y:= GetNextUnfoldedLineNumber(FSelRectEnd.Y, false);
|
||||
end;
|
||||
Result:= [cResultKeepColumnSel, cResultCaretTop];
|
||||
end;
|
||||
cDirColumnDown,
|
||||
cDirColumnPageDown:
|
||||
begin
|
||||
for i:= 1 to NPageLines do
|
||||
if FSelRectEnd.Y<Strings.Count-1 then
|
||||
begin
|
||||
Inc(FSelRectEnd.Y);
|
||||
FSelRectEnd.Y:= GetNextUnfoldedLineNumber(FSelRectEnd.Y, true);
|
||||
end;
|
||||
Result:= [cResultKeepColumnSel, cResultCaretBottom];
|
||||
end;
|
||||
end;
|
||||
|
||||
DoSelect_ColumnBlock(FSelRectBegin, FSelRectEnd);
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SelectColumnToLineEdge(AToEnd: boolean): TATCommandResults;
|
||||
var
|
||||
NPos, i: integer;
|
||||
begin
|
||||
Result:= [];
|
||||
if Carets.Count=0 then Exit;
|
||||
|
||||
if IsSelRectEmpty or (FSelRectBegin.X<0) then Exit;
|
||||
//maybe todo: make column sel if normal sel active here
|
||||
//like DoCommand_SelectColumn
|
||||
|
||||
if not AToEnd then
|
||||
begin
|
||||
FSelRectEnd.X:= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
NPos:= 0;
|
||||
for i:= FSelRectBegin.Y to FSelRectEnd.Y do
|
||||
if Strings.IsIndexValid(i) then
|
||||
NPos:= Max(NPos, Length(STabsToSpaces(Strings.Lines[i], FTabSize)));
|
||||
FSelRectEnd.X:= NPos;
|
||||
end;
|
||||
|
||||
DoSelect_ColumnBlock(FSelRectBegin, FSelRectEnd);
|
||||
Result:= [cResultKeepColumnSel, cResultCaretBottom];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_MoveSelectionUpDown(ADown: boolean): TATCommandResults;
|
||||
var
|
||||
NFrom, NTo, NLastLine: integer;
|
||||
Caret: TATCaretItem;
|
||||
Str: atString;
|
||||
Pnt: TPoint;
|
||||
begin
|
||||
Result:= [];
|
||||
if ModeReadOnly then Exit;
|
||||
|
||||
DoCaretSingleAsIs;
|
||||
Caret:= Carets[0];
|
||||
Caret.GetSelLines(NFrom, NTo);
|
||||
if NFrom<0 then
|
||||
begin
|
||||
NFrom:= Caret.PosY;
|
||||
NTo:= NFrom;
|
||||
end;
|
||||
|
||||
if ADown then
|
||||
begin
|
||||
NLastLine:= Strings.Count-1;
|
||||
if Strings.IsLastLineFake then
|
||||
Dec(NLastLine);
|
||||
if NTo>=NLastLine then Exit;
|
||||
|
||||
Str:= Strings.Lines[NTo+1];
|
||||
Strings.LineDelete(NTo+1);
|
||||
Strings.LineInsert(NFrom, Str);
|
||||
|
||||
Inc(Caret.PosY);
|
||||
if Caret.EndY>=0 then
|
||||
Inc(Caret.EndY);
|
||||
|
||||
//correct caret if out of file
|
||||
if Caret.PosY>=Strings.Count then
|
||||
begin
|
||||
Pnt:= GetEndOfFilePos;
|
||||
Caret.PosX:= Pnt.X;
|
||||
Caret.PosY:= Pnt.Y;
|
||||
end;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if NFrom<=0 then Exit;
|
||||
Str:= Strings.Lines[NFrom-1];
|
||||
Strings.LineDelete(NFrom-1);
|
||||
Strings.LineInsert(NTo, Str);
|
||||
|
||||
Dec(Caret.PosY);
|
||||
if Caret.EndY>=0 then
|
||||
Dec(Caret.EndY);
|
||||
end;
|
||||
|
||||
Result:= [cResultText, cResultCaretTop];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SelectWords: TATCommandResults;
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
i, NOffset1, NOffset2: integer;
|
||||
Str: atString;
|
||||
begin
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Item:= FCarets[i];
|
||||
if not Strings.IsIndexValid(Item.PosY) then Continue;
|
||||
|
||||
Str:= Strings.Lines[Item.PosY];
|
||||
SFindWordBounds(Str, Item.PosX, NOffset1, NOffset2, FOptWordChars);
|
||||
if NOffset1<>NOffset2 then
|
||||
begin
|
||||
Item.EndY:= Item.PosY;
|
||||
Item.EndX:= NOffset1;
|
||||
Item.PosX:= NOffset2;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result:= [cResultCaretAny];
|
||||
end;
|
||||
|
||||
function TATSynEdit.DoCommand_SelectLines: TATCommandResults;
|
||||
var
|
||||
Item: TATCaretItem;
|
||||
i: integer;
|
||||
Str: atString;
|
||||
begin
|
||||
for i:= FCarets.Count-1 downto 0 do
|
||||
begin
|
||||
Item:= FCarets[i];
|
||||
if not Strings.IsIndexValid(Item.PosY) then Continue;
|
||||
|
||||
Str:= Strings.Lines[Item.PosY];
|
||||
Item.EndY:= Item.PosY;
|
||||
Item.EndX:= 0;
|
||||
Item.PosX:= Length(Str);
|
||||
end;
|
||||
|
||||
Result:= [cResultCaretAny];
|
||||
end;
|
||||
|
||||
|
||||
155
ATSynEdit/atsynedit/atsynedit_colors.pas
Normal file
@@ -0,0 +1,155 @@
|
||||
unit ATSynEdit_Colors;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics;
|
||||
|
||||
type
|
||||
TATSynEditColors = class(TPersistent)
|
||||
private
|
||||
FTextFont,
|
||||
FTextBG,
|
||||
FTextDisabledFont,
|
||||
FTextDisabledBG,
|
||||
FTextSelFont,
|
||||
FTextSelBG,
|
||||
FCaret,
|
||||
FMarkers,
|
||||
FGutterFont,
|
||||
FGutterBG,
|
||||
FGutterCaretBG,
|
||||
FGutterPlusBorder,
|
||||
FGutterPlusBG,
|
||||
FGutterFoldLine,
|
||||
FGutterFoldBG,
|
||||
FGutterSeparatorBG,
|
||||
FCurrentLineBG,
|
||||
FMarginRight,
|
||||
FMarginCaret,
|
||||
FMarginUser,
|
||||
FIndentVertLines,
|
||||
FBookmarkBG,
|
||||
FRulerFont,
|
||||
FRulerBG,
|
||||
FCollapseLine,
|
||||
FCollapseMarkFont,
|
||||
FCollapseMarkBG,
|
||||
FCollapseMarkBorder,
|
||||
FUnprintedFont,
|
||||
FUnprintedBG,
|
||||
FUnprintedHexFont,
|
||||
FMinimapBorder,
|
||||
FMinimapSelBG,
|
||||
FStateChanged,
|
||||
FStateAdded,
|
||||
FStateSaved,
|
||||
FTextHintFont,
|
||||
FBlockStaple,
|
||||
FBlockSepLine,
|
||||
FLockedBG,
|
||||
FMarkedLinesBG,
|
||||
FComboboxArrow,
|
||||
FComboboxArrowBG: TColor;
|
||||
published
|
||||
property TextFont: TColor read FTextFont write FTextFont;
|
||||
property TextBG: TColor read FTextBG write FTextBG;
|
||||
property TextDisabledFont: TColor read FTextDisabledFont write FTextDisabledFont;
|
||||
property TextDisabledBG: TColor read FTextDisabledBG write FTextDisabledBG;
|
||||
property TextSelFont: TColor read FTextSelFont write FTextSelFont;
|
||||
property TextSelBG: TColor read FTextSelBG write FTextSelBG;
|
||||
property Caret: TColor read FCaret write FCaret;
|
||||
property Markers: TColor read FMarkers write FMarkers;
|
||||
property GutterFont: TColor read FGutterFont write FGutterFont;
|
||||
property GutterBG: TColor read FGutterBG write FGutterBG;
|
||||
property GutterCaretBG: TColor read FGutterCaretBG write FGutterCaretBG;
|
||||
property GutterPlusBorder: TColor read FGutterPlusBorder write FGutterPlusBorder;
|
||||
property GutterPlusBG: TColor read FGutterPlusBG write FGutterPlusBG;
|
||||
property GutterFoldLine: TColor read FGutterFoldLine write FGutterFoldLine;
|
||||
property GutterFoldBG: TColor read FGutterFoldBG write FGutterFoldBG;
|
||||
property GutterSeparatorBG: TColor read FGutterSeparatorBG write FGutterSeparatorBG;
|
||||
property CurrentLineBG: TColor read FCurrentLineBG write FCurrentLineBG;
|
||||
property MarginRight: TColor read FMarginRight write FMarginRight;
|
||||
property MarginCaret: TColor read FMarginCaret write FMarginCaret;
|
||||
property MarginUser: TColor read FMarginUser write FMarginUser;
|
||||
property IndentVertLines: TColor read FIndentVertLines write FIndentVertLines;
|
||||
property BookmarkBG: TColor read FBookmarkBG write FBookmarkBG;
|
||||
property RulerFont: TColor read FRulerFont write FRulerFont;
|
||||
property RulerBG: TColor read FRulerBG write FRulerBG;
|
||||
property CollapseLine: TColor read FCollapseLine write FCollapseLine;
|
||||
property CollapseMarkFont: TColor read FCollapseMarkFont write FCollapseMarkFont;
|
||||
property CollapseMarkBG: TColor read FCollapseMarkBG write FCollapseMarkBG;
|
||||
property CollapseMarkBorder: TColor read FCollapseMarkBorder write FCollapseMarkBorder;
|
||||
property UnprintedFont: TColor read FUnprintedFont write FUnprintedFont;
|
||||
property UnprintedBG: TColor read FUnprintedBG write FUnprintedBG;
|
||||
property UnprintedHexFont: TColor read FUnprintedHexFont write FUnprintedHexFont;
|
||||
property MinimapBorder: TColor read FMinimapBorder write FMinimapBorder;
|
||||
property MinimapSelBG: TColor read FMinimapSelBG write FMinimapSelBG;
|
||||
property StateChanged: TColor read FStateChanged write FStateChanged;
|
||||
property StateAdded: TColor read FStateAdded write FStateAdded;
|
||||
property StateSaved: TColor read FStateSaved write FStateSaved;
|
||||
property BlockStaple: TColor read FBlockStaple write FBlockStaple;
|
||||
property BlockSepLine: TColor read FBlockSepLine write FBlockSepLine;
|
||||
property LockedBG: TColor read FLockedBG write FLockedBG;
|
||||
property TextHintFont: TColor read FTextHintFont write FTextHintFont;
|
||||
property MarkedLinesBG: TColor read FMarkedLinesBG write FMarkedLinesBG;
|
||||
property ComboboxArrow: TColor read FComboboxArrow write FComboboxArrow;
|
||||
property ComboboxArrowBG: TColor read FComboboxArrowBG write FComboboxArrowBG;
|
||||
end;
|
||||
|
||||
procedure InitDefaultColors(C: TATSynEditColors);
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
procedure InitDefaultColors(C: TATSynEditColors);
|
||||
begin
|
||||
C.TextFont:= clBlack;
|
||||
C.TextBG:= clWhite;
|
||||
C.TextSelFont:= clHighlightText;
|
||||
C.TextSelBG:= clHighlight;
|
||||
C.TextDisabledFont:= clGray;
|
||||
C.TextDisabledBG:= $f0f0f0;
|
||||
C.Caret:= clBlack;
|
||||
C.Markers:= $0000c0;
|
||||
C.GutterFont:= clGray;
|
||||
C.GutterBG:= $e0e0e0;
|
||||
C.GutterCaretBG:= $c8c8c8;
|
||||
C.GutterPlusBorder:= clGray;
|
||||
C.GutterPlusBG:= $f4f4f4;
|
||||
C.GutterFoldLine:= clGray;
|
||||
C.GutterFoldBG:= $c8c8c8;
|
||||
C.GutterSeparatorBG:= clBlack;
|
||||
C.CurrentLineBG:= $e0f0f0;
|
||||
C.BookmarkBG:= clMoneyGreen;
|
||||
C.RulerBG:= C.GutterBG;
|
||||
C.RulerFont:= clGray;
|
||||
C.CollapseLine:= $a06060;
|
||||
C.CollapseMarkFont:= $e08080;
|
||||
C.CollapseMarkBG:= clCream;
|
||||
C.CollapseMarkBorder:= $e08080;
|
||||
C.MarginRight:= clLtGray;
|
||||
C.MarginCaret:= clLime;
|
||||
C.MarginUser:= clYellow;
|
||||
C.IndentVertLines:= clMedGray;
|
||||
C.UnprintedFont:= $5050f0;
|
||||
C.UnprintedBG:= $e0e0e0;
|
||||
C.UnprintedHexFont:= clMedGray;
|
||||
C.MinimapBorder:= clLtGray;
|
||||
C.MinimapSelBG:= $eeeeee;
|
||||
C.StateChanged:= $00f0f0;
|
||||
C.StateAdded:= $20c020;
|
||||
C.StateSaved:= clMedGray;
|
||||
C.TextHintFont:= clGray;
|
||||
C.BlockStaple:= clMedGray;
|
||||
C.BlockSepLine:= clMedGray;
|
||||
C.LockedBG:= $e0e0e0;
|
||||
C.MarkedLinesBG:= $f0e0b0;
|
||||
C.ComboboxArrow:= clGray;
|
||||
C.ComboboxArrowBG:= $f0f0f0;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
164
ATSynEdit/atsynedit/atsynedit_commands.pas
Normal file
@@ -0,0 +1,164 @@
|
||||
unit ATSynEdit_Commands;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
const
|
||||
cCmdSelKeep = $10000; //cmd continues selection (new caret pos makes bigger selection)
|
||||
cCmdSelReset = $20000; //before command reset selection
|
||||
cCmdCaret = $80000; //cmd moves caret and makes new undo-group
|
||||
|
||||
const
|
||||
_base_KeyUp = 100 or cCmdCaret;
|
||||
_base_KeyDown = 101 or cCmdCaret;
|
||||
_base_KeyLeft = 102 or cCmdCaret;
|
||||
_base_KeyRight = 103 or cCmdCaret;
|
||||
_base_KeyHome = 104 or cCmdCaret;
|
||||
_base_KeyEnd = 105 or cCmdCaret;
|
||||
_base_KeyPageUp = 106 or cCmdCaret;
|
||||
_base_KeyPageDown = 107 or cCmdCaret;
|
||||
|
||||
cCommand_KeyUp = _base_KeyUp or cCmdSelReset;
|
||||
cCommand_KeyDown = _base_KeyDown or cCmdSelReset;
|
||||
cCommand_KeyLeft = _base_KeyLeft; //handles sel
|
||||
cCommand_KeyRight = _base_KeyRight; //handles sel
|
||||
cCommand_KeyHome = _base_KeyHome or cCmdSelReset;
|
||||
cCommand_KeyEnd = _base_KeyEnd or cCmdSelReset;
|
||||
cCommand_KeyPageUp = _base_KeyPageUp or cCmdSelReset;
|
||||
cCommand_KeyPageDown = _base_KeyPageDown or cCmdSelReset;
|
||||
|
||||
cCommand_KeyUp_Sel = _base_KeyUp or cCmdSelKeep;
|
||||
cCommand_KeyDown_Sel = _base_KeyDown or cCmdSelKeep;
|
||||
cCommand_KeyLeft_Sel = _base_KeyLeft or cCmdSelKeep;
|
||||
cCommand_KeyRight_Sel = _base_KeyRight or cCmdSelKeep;
|
||||
cCommand_KeyHome_Sel = _base_KeyHome or cCmdSelKeep;
|
||||
cCommand_KeyEnd_Sel = _base_KeyEnd or cCmdSelKeep;
|
||||
cCommand_KeyPageUp_Sel = _base_KeyPageUp or cCmdSelKeep;
|
||||
cCommand_KeyPageDown_Sel = _base_KeyPageDown or cCmdSelKeep;
|
||||
|
||||
cCommand_ColSelectUp = 110;
|
||||
cCommand_ColSelectDown = 111;
|
||||
cCommand_ColSelectLeft = 112;
|
||||
cCommand_ColSelectRight = 113;
|
||||
cCommand_ColSelectToLineBegin = 114;
|
||||
cCommand_ColSelectToLineEnd = 115;
|
||||
cCommand_ColSelectPageUp = 116;
|
||||
cCommand_ColSelectPageDown = 117;
|
||||
|
||||
cCommand_TextInsert = 150;
|
||||
cCommand_TextInsertTabChar = 151;
|
||||
cCommand_KeyBackspace = 152;
|
||||
cCommand_KeyDelete = 153;
|
||||
cCommand_KeyEnter = 154;
|
||||
cCommand_KeyTab = 155;
|
||||
|
||||
cCommand_TextDeleteSelection = 170;
|
||||
cCommand_TextDeleteLine = 171 or cCmdSelReset;
|
||||
cCommand_TextDuplicateLine = 172 or cCmdSelReset;
|
||||
cCommand_TextDeleteToLineBegin = 173 or cCmdSelReset;
|
||||
cCommand_TextDeleteToLineEnd = 174 or cCmdSelReset;
|
||||
cCommand_TextDeleteToTextEnd = 175 or cCmdSelReset;
|
||||
cCommand_TextDeleteWordNext = 176 or cCmdSelReset;
|
||||
cCommand_TextDeleteWordPrev = 177 or cCmdSelReset;
|
||||
|
||||
_base_GotoTextBegin = 200 or cCmdCaret;
|
||||
_base_GotoTextEnd = 201 or cCmdCaret;
|
||||
_base_GotoWordNext = 202 or cCmdCaret;
|
||||
_base_GotoWordPrev = 203 or cCmdCaret;
|
||||
|
||||
cCommand_GotoTextBegin = _base_GotoTextBegin or cCmdSelReset;
|
||||
cCommand_GotoTextEnd = _base_GotoTextEnd or cCmdSelReset;
|
||||
cCommand_GotoWordNext = _base_GotoWordNext or cCmdSelReset;
|
||||
cCommand_GotoWordPrev = _base_GotoWordPrev or cCmdSelReset;
|
||||
|
||||
cCommand_GotoTextBegin_Sel = _base_GotoTextBegin or cCmdSelKeep;
|
||||
cCommand_GotoTextEnd_Sel = _base_GotoTextEnd or cCmdSelKeep;
|
||||
cCommand_GotoWordNext_Sel = _base_GotoWordNext or cCmdSelKeep;
|
||||
cCommand_GotoWordPrev_Sel = _base_GotoWordPrev or cCmdSelKeep;
|
||||
|
||||
cCommand_Undo = 235 or cCmdSelReset;
|
||||
cCommand_Redo = 236 or cCmdSelReset;
|
||||
|
||||
cCommand_TextIndent = 240;
|
||||
cCommand_TextUnindent = 241;
|
||||
|
||||
cCommand_ScrollLineUp = 250;
|
||||
cCommand_ScrollLineDown = 251;
|
||||
cCommand_ScrollToCaretTop = 252;
|
||||
cCommand_ScrollToCaretBottom = 253;
|
||||
cCommand_ScrollToCaretLeft = 254;
|
||||
cCommand_ScrollToCaretRight = 255;
|
||||
|
||||
cCommand_SelectAll = 260 or cCmdSelReset or cCmdCaret;
|
||||
cCommand_SelectNone = 261 or cCmdSelReset or cCmdCaret;
|
||||
cCommand_SelectWords = 262 or cCmdSelReset or cCmdCaret;
|
||||
cCommand_SelectLines = 263 or cCmdSelReset or cCmdCaret;
|
||||
cCommand_SelectInverted = 264 or cCmdCaret;
|
||||
cCommand_SelectSplitToLines = 265 or cCmdCaret;
|
||||
cCommand_SelectExtendByLine = 266 or cCmdCaret;
|
||||
|
||||
cCommand_MoveSelectionUp = 268 or cCmdCaret;
|
||||
cCommand_MoveSelectionDown = 269 or cCmdCaret;
|
||||
cCommand_TextInsertEmptyAbove = 270 or cCmdSelReset or cCmdCaret;
|
||||
cCommand_TextInsertEmptyBelow = 271 or cCmdSelReset or cCmdCaret;
|
||||
|
||||
cCommand_ToggleOverwrite = 300;
|
||||
cCommand_ToggleReadOnly = 301;
|
||||
cCommand_ToggleWordWrap = 302;
|
||||
cCommand_ToggleUnprinted = 303;
|
||||
cCommand_ToggleUnprintedSpaces = 304;
|
||||
cCommand_ToggleUnprintedEnds = 305;
|
||||
cCommand_ToggleUnprintedEndDetails = 306;
|
||||
cCommand_ToggleLineNums = 307;
|
||||
cCommand_ToggleFolding = 308;
|
||||
cCommand_ToggleRuler = 309;
|
||||
cCommand_ToggleMinimap = 310;
|
||||
|
||||
cCommand_ClipboardPaste = 1000;
|
||||
cCommand_ClipboardPaste_Select = 1001;
|
||||
cCommand_ClipboardPaste_KeepCaret = 1002;
|
||||
cCommand_ClipboardPaste_Column = 1003 or cCmdSelReset;
|
||||
cCommand_ClipboardPaste_ColumnKeepCaret = 1004 or cCmdSelReset;
|
||||
cCommand_ClipboardCopy = 1006;
|
||||
cCommand_ClipboardCopyAdd = 1007;
|
||||
cCommand_ClipboardCut = 1008;
|
||||
|
||||
cCommand_TextCaseLower = 1020;
|
||||
cCommand_TextCaseUpper = 1021;
|
||||
cCommand_TextCaseTitle = 1022;
|
||||
cCommand_TextCaseInvert = 1023;
|
||||
cCommand_TextCaseSentence = 1024;
|
||||
|
||||
cCommand_TextTrimSpacesLeft = 1026;
|
||||
cCommand_TextTrimSpacesRight = 1027;
|
||||
cCommand_TextTrimSpacesAll = 1028;
|
||||
|
||||
cCommand_FoldAll = 1030;
|
||||
cCommand_UnfoldAll = 1031;
|
||||
cCommand_FoldLevel2 = 1032;
|
||||
cCommand_FoldLevel3 = 1033;
|
||||
cCommand_FoldLevel4 = 1034;
|
||||
cCommand_FoldLevel5 = 1035;
|
||||
cCommand_FoldLevel6 = 1036;
|
||||
cCommand_FoldLevel7 = 1037;
|
||||
cCommand_FoldLevel8 = 1038;
|
||||
cCommand_FoldLevel9 = 1039;
|
||||
|
||||
cCommand_Cancel = 2001;
|
||||
cCommand_RepeatTextCommand = 2002;
|
||||
cCommand_ZoomIn = 2003;
|
||||
cCommand_ZoomOut = 2004;
|
||||
cCommand_ComboboxRecentsMenu = 2005;
|
||||
|
||||
cCommand_CaretsExtendDownLine = 2010;
|
||||
cCommand_CaretsExtendDownPage = 2011;
|
||||
cCommand_CaretsExtendDownToEnd = 2012;
|
||||
cCommand_CaretsExtendUpLine = 2013;
|
||||
cCommand_CaretsExtendUpPage = 2014;
|
||||
cCommand_CaretsExtendUpToTop = 2015;
|
||||
|
||||
implementation
|
||||
|
||||
end.
|
||||
|
||||
42
ATSynEdit/atsynedit/atsynedit_debug.inc
Normal file
@@ -0,0 +1,42 @@
|
||||
{$ifdef nn}begin end;{$endif}
|
||||
|
||||
procedure TATSynEdit.DebugFindWrapIndex;
|
||||
var
|
||||
i, j, n1, n2: integer;
|
||||
begin
|
||||
for i:= 0 to Strings.Count-1 do
|
||||
begin
|
||||
FWrapInfo.FindIndexesOfLineNumber(i, n1, n2);
|
||||
if n1<0 then
|
||||
begin
|
||||
Application.MainForm.caption:= format('fail findindex: %d', [i]);
|
||||
Exit
|
||||
end;
|
||||
for j:= n1 to n2 do
|
||||
if FWrapInfo.Items[j].NLineIndex<>i then
|
||||
begin
|
||||
Application.MainForm.caption:= format('fail findindex: %d', [i]);
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
Application.MainForm.caption:= 'ok findindex';
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoDebugInitFoldList;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
FFold.Clear;
|
||||
|
||||
//sorted by 2nd param
|
||||
FFold.Add(1, 4, 15, false, '');
|
||||
FFold.Add(1, 5, 9, false, '');
|
||||
FFold.Add(1, 7, 8, false, '');
|
||||
FFold.Add(1, 11, 14, false, '');
|
||||
|
||||
for i:= 2 to (Strings.Count-1) div 10 do
|
||||
if Odd(i) then
|
||||
FFold.Add(4, i*10, i*10+9, false, '');
|
||||
end;
|
||||
|
||||
|
||||
197
ATSynEdit/atsynedit/atsynedit_edits.pas
Normal file
@@ -0,0 +1,197 @@
|
||||
unit ATSynEdit_Edits;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics, Controls,
|
||||
Menus, Math,
|
||||
ATSynEdit,
|
||||
ATSynEdit_CanvasProc,
|
||||
ATStringProc;
|
||||
|
||||
type
|
||||
{ TATEdit }
|
||||
|
||||
TATEdit = class(TATSynEdit)
|
||||
protected
|
||||
function DoGetTextString: atString; override;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
end;
|
||||
|
||||
type
|
||||
{ TATComboEdit }
|
||||
|
||||
TATComboEdit = class(TATEdit)
|
||||
private
|
||||
FItems: TStringList;
|
||||
FMenu: TPopupMenu;
|
||||
FArrowSize: integer;
|
||||
FSelectedIndex: integer;
|
||||
procedure DoComboUpDown(ADown: boolean);
|
||||
procedure MicromapClick(Sender: TObject; AX, AY: integer);
|
||||
procedure MicromapDraw(Sender: TObject; C: TCanvas; const ARect: TRect);
|
||||
procedure DoMenu;
|
||||
procedure MenuItemClick(Sender: TObject);
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
property Items: TStringList read FItems;
|
||||
procedure DoCommand(ACmd: integer; const AText: atString = ''); override;
|
||||
procedure DoAddLineToHistory(const AStr: atString; AMaxItems: integer);
|
||||
published
|
||||
property OptComboboxArrowSize: integer read FArrowSize write FArrowSize;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Types,
|
||||
ATSynEdit_Commands,
|
||||
ATSynEdit_Keymap_Init;
|
||||
|
||||
{ TATEdit }
|
||||
|
||||
function TATEdit.DoGetTextString: atString;
|
||||
begin
|
||||
Result:= inherited;
|
||||
//gets text with EOLs, strip them
|
||||
while (Result<>'') and
|
||||
IsCharEol(Result[Length(Result)]) do
|
||||
SetLength(Result, Length(Result)-1);
|
||||
end;
|
||||
|
||||
constructor TATEdit.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
WantTabs:= false;
|
||||
ModeOneLine:= true;
|
||||
BorderStyle:= bsSingle;
|
||||
|
||||
Keymap:= KeymapCombo;
|
||||
|
||||
OptTextOffsetTop:= 2;
|
||||
Height:= 26;
|
||||
end;
|
||||
|
||||
{ TATComboEdit }
|
||||
|
||||
constructor TATComboEdit.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited;
|
||||
|
||||
FItems:= TStringList.Create;
|
||||
FMenu:= TPopupMenu.Create(Self);
|
||||
FSelectedIndex:= -1;
|
||||
|
||||
OptMicromapVisible:= true;
|
||||
OptMicromapWidth:= 22;
|
||||
OptComboboxArrowSize:= 4;
|
||||
OnClickMicromap:= @MicromapClick;
|
||||
OnDrawMicromap:= @MicromapDraw;
|
||||
end;
|
||||
|
||||
procedure TATComboEdit.MicromapClick(Sender: TObject; AX, AY: integer);
|
||||
begin
|
||||
DoMenu;
|
||||
end;
|
||||
|
||||
procedure TATComboEdit.MicromapDraw(Sender: TObject; C: TCanvas;
|
||||
const ARect: TRect);
|
||||
begin
|
||||
C.Brush.Color:= Colors.ComboboxArrowBG;
|
||||
C.FillRect(ARect);
|
||||
|
||||
CanvasPaintTriangleDown(C, Colors.ComboboxArrow,
|
||||
Point(
|
||||
(ARect.Left+ARect.Right) div 2 - FArrowSize,
|
||||
(ARect.Top+ARect.Bottom) div 2 - FArrowSize div 2),
|
||||
FArrowSize);
|
||||
end;
|
||||
|
||||
procedure TATComboEdit.DoMenu;
|
||||
var
|
||||
p: TPoint;
|
||||
i: integer;
|
||||
mi: TMenuItem;
|
||||
begin
|
||||
p:= ClientToScreen(Point(Width-OptMicromapWidth, Height));
|
||||
with FMenu.Items do
|
||||
begin
|
||||
Clear;
|
||||
for i:= 0 to FItems.Count-1 do
|
||||
begin
|
||||
mi:= TMenuItem.Create(Self);
|
||||
mi.Caption:= FItems[i];
|
||||
mi.Tag:= i;
|
||||
mi.OnClick:= @MenuItemClick;
|
||||
Add(mi);
|
||||
end;
|
||||
end;
|
||||
FMenu.PopUp(p.x, p.y);
|
||||
end;
|
||||
|
||||
procedure TATComboEdit.MenuItemClick(Sender: TObject);
|
||||
var
|
||||
n: integer;
|
||||
begin
|
||||
n:= (Sender as TMenuItem).Tag;
|
||||
if n>=0 then
|
||||
begin
|
||||
Text:= UTF8Decode(FItems[n]);
|
||||
DoEventChange;
|
||||
|
||||
//scroll to left, select all
|
||||
DoScrollByDelta(-10000, 0);
|
||||
DoCommand(cCommand_SelectAll);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATComboEdit.DoCommand(ACmd: integer; const AText: atString);
|
||||
begin
|
||||
inherited;
|
||||
case ACmd of
|
||||
cCommand_ComboboxRecentsMenu:
|
||||
begin
|
||||
DoMenu;
|
||||
end;
|
||||
cCommand_KeyDown,
|
||||
cCommand_KeyUp:
|
||||
begin
|
||||
DoComboUpDown(ACmd=cCommand_KeyDown);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATComboEdit.DoAddLineToHistory(const AStr: atString;
|
||||
AMaxItems: integer);
|
||||
begin
|
||||
FSelectedIndex:= -1;
|
||||
SAddStringToHistory(Utf8Encode(AStr), FItems, AMaxItems);
|
||||
end;
|
||||
|
||||
procedure TATComboEdit.DoComboUpDown(ADown: boolean);
|
||||
begin
|
||||
if FItems.Count=0 then exit;
|
||||
if ADown then Inc(FSelectedIndex) else Dec(FSelectedIndex);
|
||||
FSelectedIndex:= Max(0, Min(FItems.Count-1, FSelectedIndex));
|
||||
|
||||
Text:= Utf8Decode(FItems[FSelectedIndex]);
|
||||
DoEventChange;
|
||||
DoCommand(cCommand_SelectAll);
|
||||
end;
|
||||
|
||||
destructor TATComboEdit.Destroy;
|
||||
begin
|
||||
FreeAndNil(FMenu);
|
||||
FreeAndNil(FItems);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
132
ATSynEdit/atsynedit/atsynedit_export_html.pas
Normal file
@@ -0,0 +1,132 @@
|
||||
unit ATSynEdit_Export_HTML;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics, StrUtils,
|
||||
ATSynEdit,
|
||||
ATSynEdit_CanvasProc,
|
||||
ATStringProc_HtmlColor,
|
||||
LazUTF8Classes;
|
||||
|
||||
procedure DoEditorExportToHTML(Ed: TATSynEdit;
|
||||
const AFilename, APageTitle, AFontName: string;
|
||||
AFontSize: integer; AWithNumbers: boolean;
|
||||
AColorBg, AColorNumbers: TColor);
|
||||
|
||||
implementation
|
||||
|
||||
procedure DoEditorExportToHTML(Ed: TATSynEdit; const AFilename, APageTitle,
|
||||
AFontName: string; AFontSize: integer; AWithNumbers: boolean; AColorBg,
|
||||
AColorNumbers: TColor);
|
||||
var
|
||||
L: TStringListUTF8;
|
||||
Parts: TATLineParts;
|
||||
PPart: ^TATLinePart;
|
||||
NColorFont: TColor;
|
||||
NColorAfter: TColor;
|
||||
NeedStyle: boolean;
|
||||
Str0, Str1: string;
|
||||
i, j: integer;
|
||||
begin
|
||||
NColorFont:= clBlack;
|
||||
FillChar(Parts, Sizeof(Parts), 0);
|
||||
|
||||
if FileExists(AFilename) then
|
||||
DeleteFile(AFilename);
|
||||
|
||||
L:= TStringListUTF8.Create;
|
||||
try
|
||||
L.Add('<!-- Generated by ATSynEdit Exporter -->');
|
||||
L.Add('<html>'+sLineBreak+
|
||||
'<head>'+sLineBreak+
|
||||
' <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />'+sLineBreak+
|
||||
' <title>'+APageTitle+'</title>'+sLineBreak+
|
||||
' <style>'+sLineBreak+
|
||||
' body, table {'+sLineBreak+
|
||||
' color: '+SColorToHtmlColor(NColorFont)+';'+sLineBreak+
|
||||
' background-color: '+SColorToHtmlColor(AColorBg)+';'+sLineBreak+
|
||||
' }'+sLineBreak+
|
||||
' pre, code {'+sLineBreak+
|
||||
' font-family: "'+AFontName+'", sans-serif;'+sLineBreak+
|
||||
' font-size: '+IntToStr(AFontSize)+'px;'+sLineBreak+
|
||||
' }'+sLineBreak+
|
||||
' table, td {'+sLineBreak+
|
||||
' border-style: hidden;'+sLineBreak+
|
||||
' }'+sLineBreak+
|
||||
' td {'+sLineBreak+
|
||||
' vertical-align: top;'+sLineBreak+
|
||||
' }'+sLineBreak+
|
||||
' td.num {'+sLineBreak+
|
||||
' color: '+SColorToHtmlColor(AColorNumbers)+';'+sLineBreak+
|
||||
' text-align: right;'+sLineBreak+
|
||||
' }'+sLineBreak+
|
||||
' </style>'+sLineBreak+
|
||||
'</head>'+sLineBreak+
|
||||
'<body>');
|
||||
|
||||
if AWithNumbers then
|
||||
begin
|
||||
L.Add('<table>'+sLineBreak+'<tr>'+sLineBreak+'<td class="num">');
|
||||
L.Add('<pre><code>'); //??? eol
|
||||
for i:= 0 to Ed.Strings.Count-1 do
|
||||
L.Add(IntToStr(i+1)+' ');
|
||||
L.Add('</code></pre>');
|
||||
L.Add('</td>'+sLineBreak+'<td>');
|
||||
end;
|
||||
|
||||
L.Add('<pre><code>');
|
||||
|
||||
for i:= 0 to Ed.Strings.Count-1 do
|
||||
begin
|
||||
Str0:= '';
|
||||
if not Ed.DoCalcLineHiliteEx(i, Parts, AColorBG, NColorAfter) then break;
|
||||
for j:= 0 to High(Parts) do
|
||||
begin
|
||||
PPart:= @Parts[j];
|
||||
if PPart^.Len=0 then Break;
|
||||
if PPart^.FontBold then Str0:= Str0+'<b>';
|
||||
if PPart^.FontItalic then Str0:= Str0+'<i>';
|
||||
if PPart^.FontStrikeOut then Str0:= Str0+'<s>';
|
||||
|
||||
NeedStyle:=
|
||||
(PPart^.ColorFont<>NColorFont) or
|
||||
(PPart^.ColorBG<>AColorBG);
|
||||
if NeedStyle then
|
||||
Str0:= Str0+'<span style="'+
|
||||
IfThen(PPart^.ColorFont<>NColorFont, 'color: '+SColorToHtmlColor(PPart^.ColorFont)+'; ')+
|
||||
IfThen(PPart^.ColorBG<>AColorBG, 'background: '+SColorToHtmlColor(PPart^.ColorBG)+'; ')+
|
||||
'">';
|
||||
|
||||
Str1:= Utf8Encode(Copy(Ed.Strings.Lines[i], PPart^.Offset+1, PPart^.Len));
|
||||
Str1:= StringReplace(Str1, '<', '<', [rfReplaceAll]);
|
||||
Str1:= StringReplace(Str1, '>', '>', [rfReplaceAll]);
|
||||
|
||||
Str0:= Str0+Str1;
|
||||
if NeedStyle then
|
||||
Str0:= Str0+'</span>';
|
||||
|
||||
if PPart^.FontStrikeOut then Str0:= Str0+'</s>';
|
||||
if PPart^.FontItalic then Str0:= Str0+'</i>';
|
||||
if PPart^.FontBold then Str0:= Str0+'</b>';
|
||||
end;
|
||||
L.Add(Str0);
|
||||
end;
|
||||
|
||||
L.Add('</code></pre>');
|
||||
if AWithNumbers then
|
||||
L.Add('</td></tr></table>');
|
||||
|
||||
L.Add('</body>');
|
||||
L.Add('</html>');
|
||||
|
||||
L.SaveToFile(AFilename);
|
||||
finally
|
||||
FreeAndNil(L);
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
721
ATSynEdit/atsynedit/atsynedit_finder.pas
Normal file
@@ -0,0 +1,721 @@
|
||||
unit ATSynEdit_Finder;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
SysUtils, Classes, Dialogs, Forms,
|
||||
Math,
|
||||
RegExpr, //must be with {$define Unicode}
|
||||
ATSynEdit,
|
||||
ATSynEdit_Carets,
|
||||
ATStringProc,
|
||||
ATStringProc_TextBuffer;
|
||||
|
||||
type
|
||||
TATFinderProgress = procedure(Sender: TObject; ACurPos, AMaxPos: integer;
|
||||
var AContinue: boolean) of object;
|
||||
TATFinderFound = procedure(Sender: TObject; APos1, APos2: TPoint) of object;
|
||||
TATFinderConfirmReplace = procedure(Sender: TObject;
|
||||
APos1, APos2: TPoint; AForMany: boolean;
|
||||
var AConfirm, AContinue: boolean) of object;
|
||||
|
||||
type
|
||||
{ TATTextFinder }
|
||||
|
||||
TATTextFinder = class
|
||||
private
|
||||
FMatchPos: integer;
|
||||
FMatchLen: integer;
|
||||
FStrFind: UnicodeString;
|
||||
FStrReplace: UnicodeString;
|
||||
FStrReplacement: UnicodeString;
|
||||
FOnProgress: TATFinderProgress;
|
||||
FOnBadRegex: TNotifyEvent;
|
||||
function DoCountMatchesRegex(FromPos: integer; AWithEvent: boolean): integer;
|
||||
function DoCountMatchesUsual(FromPos: integer; AWithEvent: boolean): Integer;
|
||||
function DoFindMatchRegex(FromPos: integer; var MatchPos, MatchLen: integer): boolean;
|
||||
function DoFindMatchUsual(FromPos: integer): Integer;
|
||||
function IsMatchUsual(APos: integer): boolean;
|
||||
procedure SetStrFind(const AValue: UnicodeString);
|
||||
procedure SetStrReplace(const AValue: UnicodeString);
|
||||
protected
|
||||
procedure DoOnFound; virtual;
|
||||
public
|
||||
OptBack: boolean; //for non-regex
|
||||
OptWords: boolean; //for non-regex
|
||||
OptCase: boolean; //for regex and usual
|
||||
OptRegex: boolean;
|
||||
OptWrapped: boolean;
|
||||
StrText: UnicodeString;
|
||||
property StrFind: UnicodeString read FStrFind write SetStrFind;
|
||||
property StrReplace: UnicodeString read FStrReplace write SetStrReplace;
|
||||
property StrReplacement: UnicodeString read FStrReplacement; //for regex
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
function FindMatch(ANext: boolean; ASkipLen: integer; AStartPos: integer): boolean;
|
||||
property MatchPos: integer read FMatchPos; //have meaning if FindMatch returned True
|
||||
property MatchLen: integer read FMatchLen; //too
|
||||
property OnProgress: TATFinderProgress read FOnProgress write FOnProgress;
|
||||
property OnBadRegex: TNotifyEvent read FOnBadRegex write FOnBadRegex;
|
||||
end;
|
||||
|
||||
type
|
||||
{ TATEditorFinder }
|
||||
|
||||
TATEditorFinder = class(TATTextFinder)
|
||||
private
|
||||
FBuffer: TATStringBuffer;
|
||||
FEditor: TATSynEdit;
|
||||
FSkipLen: integer;
|
||||
FOnFound: TATFinderFound;
|
||||
FOnConfirmReplace: TATFinderConfirmReplace;
|
||||
function DoFindOrReplace_Internal(ANext, AReplace, AForMany: boolean; out
|
||||
AChanged: boolean; AStartPos: integer): boolean;
|
||||
procedure DoFixCaretSelectionDirection;
|
||||
procedure DoReplaceTextInEditor(P1, P2: TPoint);
|
||||
function GetOffsetOfCaret: integer;
|
||||
function GetOffsetStartPos: integer;
|
||||
function GetRegexSkipIncrement: integer;
|
||||
protected
|
||||
procedure DoOnFound; override;
|
||||
public
|
||||
OptFromCaret: boolean;
|
||||
OptConfirmReplace: boolean;
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure UpdateBuffer;
|
||||
property Editor: TATSynEdit read FEditor write FEditor;
|
||||
property OnFound: TATFinderFound read FOnFound write FOnFound;
|
||||
property OnConfirmReplace: TATFinderConfirmReplace read FOnConfirmReplace write FOnConfirmReplace;
|
||||
function DoFindOrReplace(ANext, AReplace, AForMany: boolean; out AChanged: boolean): boolean;
|
||||
function DoReplaceSelectedMatch: boolean;
|
||||
function DoCountAll(AWithEvent: boolean): integer;
|
||||
function DoReplaceAll: integer;
|
||||
function IsSelectionStartsAtFoundMatch: boolean;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
function IsWordChar(ch: Widechar): boolean;
|
||||
begin
|
||||
Result:= ATStringProc.IsCharWord(ch, '');
|
||||
end;
|
||||
|
||||
function SRegexReplaceEscapedTabs(const AStr: string): string;
|
||||
begin
|
||||
Result:= AStr;
|
||||
Result:= StringReplace(Result, '\\', #1, [rfReplaceAll]);
|
||||
Result:= StringReplace(Result, '\t', #9, [rfReplaceAll]);
|
||||
Result:= StringReplace(Result, #1, '\\', [rfReplaceAll]);
|
||||
end;
|
||||
|
||||
|
||||
function TATTextFinder.IsMatchUsual(APos: integer): boolean;
|
||||
var
|
||||
LenF, LastPos: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
if StrFind='' then exit;
|
||||
if StrText='' then exit;
|
||||
|
||||
LenF:= Length(StrFind);
|
||||
LastPos:= Length(StrText)-LenF+1;
|
||||
|
||||
if OptCase then
|
||||
Result:= CompareMem(@StrFind[1], @StrText[APos], LenF*2)
|
||||
else
|
||||
Result:=
|
||||
UnicodeLowerCase(StrFind) =
|
||||
UnicodeLowerCase(Copy(StrText, APos, LenF));
|
||||
|
||||
if Result then
|
||||
if OptWords then
|
||||
Result:=
|
||||
((APos <= 1) or (not IsWordChar(StrText[APos - 1]))) and
|
||||
((APos >= LastPos) or (not IsWordChar(StrText[APos + LenF])));
|
||||
end;
|
||||
|
||||
procedure TATTextFinder.SetStrFind(const AValue: UnicodeString);
|
||||
begin
|
||||
if FStrFind=AValue then Exit;
|
||||
FStrFind:= AValue;
|
||||
FMatchPos:= -1;
|
||||
FMatchLen:= 0;
|
||||
end;
|
||||
|
||||
procedure TATTextFinder.SetStrReplace(const AValue: UnicodeString);
|
||||
begin
|
||||
if FStrReplace=AValue then Exit;
|
||||
FStrReplace:= AValue;
|
||||
end;
|
||||
|
||||
procedure TATTextFinder.DoOnFound;
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
function TATTextFinder.DoFindMatchUsual(FromPos: integer): Integer;
|
||||
var
|
||||
LastPos, i: integer;
|
||||
begin
|
||||
Result:= 0;
|
||||
if StrText='' then exit;
|
||||
if StrFind='' then exit;
|
||||
LastPos:= Length(StrText) - Length(StrFind) + 1;
|
||||
|
||||
if not OptBack then
|
||||
for i:= FromPos to LastPos do
|
||||
begin
|
||||
if IsMatchUsual(i) then
|
||||
begin
|
||||
Result:= i;
|
||||
Break
|
||||
end;
|
||||
end
|
||||
else
|
||||
for i:= FromPos downto 1 do
|
||||
begin
|
||||
if IsMatchUsual(i) then
|
||||
begin
|
||||
Result:= i;
|
||||
Break
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATTextFinder.DoFindMatchRegex(FromPos: integer; var MatchPos,
|
||||
MatchLen: integer): boolean;
|
||||
var
|
||||
Obj: TRegExpr;
|
||||
begin
|
||||
Result:= false;
|
||||
if StrText='' then exit;
|
||||
if StrFind='' then exit;
|
||||
|
||||
Obj:= TRegExpr.Create;
|
||||
try
|
||||
Obj.ModifierS:= false; //don't catch all text by .*
|
||||
Obj.ModifierM:= true; //allow to work with ^$
|
||||
Obj.ModifierI:= not OptCase;
|
||||
|
||||
try
|
||||
Obj.Expression:= StrFind;
|
||||
Obj.InputString:= StrText;
|
||||
Result:= Obj.ExecPos(FromPos);
|
||||
except
|
||||
if Assigned(FOnBadRegex) then
|
||||
FOnBadRegex(Self);
|
||||
Result:= false;
|
||||
end;
|
||||
|
||||
if Result then
|
||||
begin
|
||||
MatchPos:= Obj.MatchPos[0];
|
||||
MatchLen:= Obj.MatchLen[0];
|
||||
if StrReplace<>'' then
|
||||
FStrReplacement:= Obj.Replace(Obj.Match[0], SRegexReplaceEscapedTabs(StrReplace), true);
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(Obj);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATTextFinder.DoCountMatchesUsual(FromPos: integer; AWithEvent: boolean
|
||||
): Integer;
|
||||
var
|
||||
LastPos, i: Integer;
|
||||
Ok: boolean;
|
||||
begin
|
||||
Result:= 0;
|
||||
if StrText='' then exit;
|
||||
if StrFind='' then exit;
|
||||
LastPos:= Length(StrText) - Length(StrFind) + 1;
|
||||
|
||||
for i:= FromPos to LastPos do
|
||||
begin
|
||||
if Application.Terminated then exit;
|
||||
if IsMatchUsual(i) then
|
||||
begin
|
||||
Inc(Result);
|
||||
if AWithEvent then
|
||||
begin
|
||||
FMatchPos:= i;
|
||||
FMatchLen:= Length(StrFind);
|
||||
DoOnFound;
|
||||
end;
|
||||
|
||||
if Assigned(FOnProgress) then
|
||||
begin
|
||||
Ok:= true;
|
||||
FOnProgress(Self, i, LastPos, Ok);
|
||||
if not Ok then Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATTextFinder.DoCountMatchesRegex(FromPos: integer; AWithEvent: boolean
|
||||
): integer;
|
||||
var
|
||||
Obj: TRegExpr;
|
||||
Ok: boolean;
|
||||
begin
|
||||
Result:= 0;
|
||||
if StrFind='' then exit;
|
||||
if StrText='' then exit;
|
||||
|
||||
Obj:= TRegExpr.Create;
|
||||
try
|
||||
Obj.ModifierS:= false;
|
||||
Obj.ModifierM:= true;
|
||||
Obj.ModifierI:= not OptCase;
|
||||
|
||||
try
|
||||
Obj.Expression:= StrFind;
|
||||
Obj.InputString:= StrText;
|
||||
Ok:= Obj.ExecPos(FromPos);
|
||||
except
|
||||
if Assigned(FOnBadRegex) then
|
||||
FOnBadRegex(Self);
|
||||
Result:= 0;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if Ok then
|
||||
begin
|
||||
Inc(Result);
|
||||
if AWithEvent then
|
||||
begin
|
||||
FMatchPos:= Obj.MatchPos[0];
|
||||
FMatchLen:= Obj.MatchLen[0];
|
||||
DoOnFound;
|
||||
end;
|
||||
|
||||
while Obj.ExecNext do
|
||||
begin
|
||||
if Application.Terminated then exit;
|
||||
Inc(Result);
|
||||
if AWithEvent then
|
||||
begin
|
||||
FMatchPos:= Obj.MatchPos[0];
|
||||
FMatchLen:= Obj.MatchLen[0];
|
||||
DoOnFound;
|
||||
end;
|
||||
|
||||
if Assigned(FOnProgress) then
|
||||
begin
|
||||
Ok:= true;
|
||||
FOnProgress(Self, Obj.MatchPos[0], Length(StrText), Ok);
|
||||
if not Ok then Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(Obj);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATEditorFinder.UpdateBuffer;
|
||||
var
|
||||
Lens: TList;
|
||||
i: integer;
|
||||
begin
|
||||
Lens:= TList.Create;
|
||||
try
|
||||
Lens.Clear;
|
||||
for i:= 0 to FEditor.Strings.Count-1 do
|
||||
Lens.Add(pointer(Length(FEditor.Strings.Lines[i])));
|
||||
FBuffer.Setup(FEditor.Strings.TextString, Lens, 1);
|
||||
finally
|
||||
FreeAndNil(Lens);
|
||||
end;
|
||||
|
||||
StrText:= FBuffer.FText;
|
||||
end;
|
||||
|
||||
constructor TATEditorFinder.Create;
|
||||
begin
|
||||
inherited;
|
||||
FEditor:= nil;
|
||||
FBuffer:= TATStringBuffer.Create;
|
||||
OptFromCaret:= false;
|
||||
OptConfirmReplace:= false;
|
||||
end;
|
||||
|
||||
destructor TATEditorFinder.Destroy;
|
||||
begin
|
||||
FEditor:= nil;
|
||||
FreeAndNil(FBuffer);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TATEditorFinder.GetOffsetOfCaret: integer;
|
||||
var
|
||||
Pnt: TPoint;
|
||||
begin
|
||||
with FEditor.Carets[0] do
|
||||
begin
|
||||
Pnt.X:= PosX;
|
||||
Pnt.Y:= PosY;
|
||||
end;
|
||||
|
||||
Result:= FBuffer.CaretToStr(Pnt);
|
||||
Inc(Result); //was 0-based
|
||||
|
||||
//find-back must goto previous match
|
||||
if OptBack then
|
||||
Dec(Result, Length(StrFind));
|
||||
|
||||
if Result<1 then
|
||||
Result:= 1;
|
||||
end;
|
||||
|
||||
function TATEditorFinder.DoCountAll(AWithEvent: boolean): integer;
|
||||
begin
|
||||
UpdateBuffer;
|
||||
if OptRegex then
|
||||
Result:= DoCountMatchesRegex(1, AWithEvent)
|
||||
else
|
||||
Result:= DoCountMatchesUsual(1, AWithEvent);
|
||||
end;
|
||||
|
||||
function TATEditorFinder.DoReplaceAll: integer;
|
||||
var
|
||||
Ok, Changed: boolean;
|
||||
begin
|
||||
Result:= 0;
|
||||
if DoFindOrReplace(false, true, true, Changed) then
|
||||
begin
|
||||
if Changed then Inc(Result);
|
||||
while DoFindOrReplace(true, true, true, Changed) do
|
||||
begin
|
||||
if Application.Terminated then exit;
|
||||
if Changed then Inc(Result);
|
||||
if Assigned(FOnProgress) then
|
||||
begin
|
||||
Ok:= true;
|
||||
FOnProgress(Self, FMatchPos, Length(StrText), Ok);
|
||||
if not Ok then Break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATEditorFinder.DoReplaceTextInEditor(P1, P2: TPoint);
|
||||
var
|
||||
Shift, PosAfter: TPoint;
|
||||
Str: UnicodeString;
|
||||
begin
|
||||
if OptRegex then
|
||||
Str:= StrReplacement
|
||||
else
|
||||
Str:= StrReplace;
|
||||
|
||||
FEditor.Strings.BeginUndoGroup;
|
||||
FEditor.Strings.TextDeleteRange(P1.X, P1.Y, P2.X, P2.Y, Shift, PosAfter);
|
||||
FEditor.Strings.TextInsert(P1.X, P1.Y, Str, false, Shift, PosAfter);
|
||||
FEditor.Strings.EndUndoGroup;
|
||||
|
||||
//correct caret pos
|
||||
//(e.g. replace "dddddd" to "--": move lefter)
|
||||
if not OptBack then
|
||||
FEditor.Carets[0].PosX:= P1.X+Length(Str);
|
||||
end;
|
||||
|
||||
function TATEditorFinder.GetOffsetStartPos: integer;
|
||||
begin
|
||||
if OptFromCaret then
|
||||
Result:= GetOffsetOfCaret
|
||||
else
|
||||
if OptRegex then
|
||||
Result:= 1
|
||||
else
|
||||
if OptBack then
|
||||
Result:= Length(StrText)
|
||||
else
|
||||
Result:= 1;
|
||||
end;
|
||||
|
||||
procedure TATEditorFinder.DoFixCaretSelectionDirection;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
bSel: boolean;
|
||||
begin
|
||||
if FEditor.Carets.Count=0 then exit;
|
||||
Caret:= FEditor.Carets[0];
|
||||
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then exit;
|
||||
|
||||
if OptBack then
|
||||
begin
|
||||
Caret.PosX:= X1;
|
||||
Caret.PosY:= Y1;
|
||||
Caret.EndX:= X2;
|
||||
Caret.EndY:= Y2;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Caret.PosX:= X2;
|
||||
Caret.PosY:= Y2;
|
||||
Caret.EndX:= X1;
|
||||
Caret.EndY:= Y1;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATEditorFinder.DoFindOrReplace(ANext, AReplace, AForMany: boolean;
|
||||
out AChanged: boolean): boolean;
|
||||
var
|
||||
NStartPos: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
AChanged:= false;
|
||||
|
||||
if not Assigned(FEditor) then
|
||||
begin
|
||||
Showmessage('Finder.Editor not set');
|
||||
Exit
|
||||
end;
|
||||
if StrFind='' then
|
||||
begin
|
||||
Showmessage('Finder.StrFind not set');
|
||||
Exit
|
||||
end;
|
||||
if FEditor.Carets.Count=0 then
|
||||
begin
|
||||
Showmessage('Editor has not caret');
|
||||
Exit
|
||||
end;
|
||||
|
||||
if AReplace and FEditor.ModeReadOnly then exit;
|
||||
if OptRegex then OptBack:= false;
|
||||
|
||||
DoFixCaretSelectionDirection;
|
||||
|
||||
NStartPos:= GetOffsetStartPos;
|
||||
Result:= DoFindOrReplace_Internal(ANext, AReplace, AForMany, AChanged, NStartPos);
|
||||
|
||||
if not Result and OptWrapped then
|
||||
if (not OptBack and (NStartPos>1)) or
|
||||
(OptBack and (NStartPos<Length(StrText))) then
|
||||
begin
|
||||
//we must have AReplace=false
|
||||
//(if not, need more actions: don't allow to replace in wrapped part if too big pos)
|
||||
//
|
||||
if DoFindOrReplace_Internal(ANext, false, AForMany, AChanged,
|
||||
IfThen(not OptBack, 1, Length(StrText))) then
|
||||
begin
|
||||
Result:= (not OptBack and (MatchPos<NStartPos)) or
|
||||
(OptBack and (MatchPos>NStartPos));
|
||||
if not Result then
|
||||
begin
|
||||
FMatchPos:= -1;
|
||||
FMatchLen:= 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TATEditorFinder.DoFindOrReplace_Internal(ANext, AReplace, AForMany: boolean;
|
||||
out AChanged: boolean; AStartPos: integer): boolean;
|
||||
//function usually called 1 time in outer func,
|
||||
//or 1-2 times if OptWrap=true
|
||||
var
|
||||
P1, P2: TPoint;
|
||||
ConfirmThis, ConfirmContinue: boolean;
|
||||
begin
|
||||
AChanged:= false;
|
||||
Result:= FindMatch(ANext, FSkipLen, AStartPos);
|
||||
FSkipLen:= FMatchLen;
|
||||
|
||||
if Result then
|
||||
begin
|
||||
P1:= FBuffer.StrToCaret(MatchPos-1);
|
||||
P2:= FBuffer.StrToCaret(MatchPos-1+MatchLen);
|
||||
FEditor.DoCaretSingle(P1.X, P1.Y);
|
||||
|
||||
if AReplace then
|
||||
begin
|
||||
ConfirmThis:= true;
|
||||
ConfirmContinue:= true;
|
||||
|
||||
if OptConfirmReplace then
|
||||
if Assigned(FOnConfirmReplace) then
|
||||
FOnConfirmReplace(Self, P1, P2, AForMany, ConfirmThis, ConfirmContinue);
|
||||
|
||||
if not ConfirmContinue then
|
||||
begin
|
||||
Result:= false;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if ConfirmThis then
|
||||
begin
|
||||
DoReplaceTextInEditor(P1, P2);
|
||||
UpdateBuffer;
|
||||
|
||||
if OptRegex then
|
||||
FSkipLen:= Length(StrReplacement)+GetRegexSkipIncrement
|
||||
else
|
||||
FSkipLen:= Length(StrReplace);
|
||||
AChanged:= true;
|
||||
end;
|
||||
end;
|
||||
|
||||
if AReplace then
|
||||
//don't select
|
||||
FEditor.DoCaretSingle(P1.X, P1.Y)
|
||||
else
|
||||
//select to right (find forward) or to left (find back)
|
||||
if OptBack then
|
||||
FEditor.DoCaretSingle(P1.X, P1.Y, P2.X, P2.Y, true)
|
||||
else
|
||||
FEditor.DoCaretSingle(P2.X, P2.Y, P1.X, P1.Y, true);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATEditorFinder.IsSelectionStartsAtFoundMatch: boolean;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
PosOfBegin, PosOfEnd: integer;
|
||||
bSel: boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
if FEditor.Carets.Count=0 then exit;
|
||||
Caret:= FEditor.Carets[0];
|
||||
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then exit;
|
||||
|
||||
PosOfBegin:= FBuffer.CaretToStr(Point(X1, Y1))+1;
|
||||
PosOfEnd:= FBuffer.CaretToStr(Point(X2, Y2))+1;
|
||||
|
||||
//allow to replace, also if selection=Strfind
|
||||
Result:=
|
||||
((PosOfBegin=FMatchPos) and (PosOfEnd=FMatchPos+FMatchLen)) or
|
||||
((StrFind<>'') and (FEditor.TextSelected=StrFind));
|
||||
end;
|
||||
|
||||
function TATEditorFinder.DoReplaceSelectedMatch: boolean;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
P1, P2: TPoint;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
bSel: boolean;
|
||||
begin
|
||||
Result:= false;
|
||||
if not IsSelectionStartsAtFoundMatch then
|
||||
begin
|
||||
//do Find-next (from caret)
|
||||
DoFindOrReplace(false, false, false, bSel);
|
||||
exit;
|
||||
end;
|
||||
|
||||
Caret:= FEditor.Carets[0];
|
||||
Caret.GetRange(X1, Y1, X2, Y2, bSel);
|
||||
if not bSel then exit;
|
||||
P1:= Point(X1, Y1);
|
||||
P2:= Point(X2, Y2);
|
||||
|
||||
Caret.EndX:= -1;
|
||||
Caret.EndY:= -1;
|
||||
|
||||
DoReplaceTextInEditor(P1, P2);
|
||||
UpdateBuffer;
|
||||
|
||||
if OptRegex then
|
||||
FSkipLen:= Length(StrReplacement)
|
||||
else
|
||||
FSkipLen:= Length(StrReplace);
|
||||
Result:= true;
|
||||
end;
|
||||
|
||||
|
||||
constructor TATTextFinder.Create;
|
||||
begin
|
||||
StrText:= '';
|
||||
FStrFind:= '';
|
||||
FStrReplace:= '';
|
||||
FStrReplacement:= '';
|
||||
OptBack:= false;
|
||||
OptCase:= false;
|
||||
OptWords:= false;
|
||||
OptRegex:= false;
|
||||
FMatchPos:= -1;
|
||||
FMatchLen:= 0;
|
||||
end;
|
||||
|
||||
destructor TATTextFinder.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TATTextFinder.FindMatch(ANext: boolean; ASkipLen: integer; AStartPos: integer): boolean;
|
||||
var
|
||||
FromPos: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
if StrText='' then Exit;
|
||||
if StrFind='' then Exit;
|
||||
|
||||
//regex code
|
||||
if OptRegex then
|
||||
begin
|
||||
if not ANext then
|
||||
FromPos:= AStartPos
|
||||
else
|
||||
FromPos:= FMatchPos+ASkipLen;
|
||||
Result:= DoFindMatchRegex(FromPos, FMatchPos, FMatchLen);
|
||||
if Result then DoOnFound;
|
||||
Exit
|
||||
end;
|
||||
|
||||
//usual code
|
||||
if not ANext then
|
||||
begin
|
||||
FMatchPos:= AStartPos;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if FMatchPos<=0 then
|
||||
FMatchPos:= 1;
|
||||
if not OptBack then
|
||||
Inc(FMatchPos, ASkipLen)
|
||||
else
|
||||
Dec(FMatchPos, ASkipLen);
|
||||
end;
|
||||
|
||||
FMatchPos:= DoFindMatchUsual(FMatchPos);
|
||||
Result:= FMatchPos>0;
|
||||
if Result then
|
||||
begin
|
||||
FMatchLen:= Length(StrFind);
|
||||
DoOnFound;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATEditorFinder.DoOnFound;
|
||||
var
|
||||
P1, P2: TPoint;
|
||||
begin
|
||||
if Assigned(FOnFound) then
|
||||
begin
|
||||
P1:= FBuffer.StrToCaret(MatchPos-1);
|
||||
P2:= FBuffer.StrToCaret(MatchPos-1+MatchLen);
|
||||
FOnFound(Self, P1, P2);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATEditorFinder.GetRegexSkipIncrement: integer;
|
||||
//this is to solve loop-forever if regex "$" replaced-all to eg "==="
|
||||
//(need to skip one more char)
|
||||
begin
|
||||
Result:= 0;
|
||||
if StrFind='$' then Result:= 1;
|
||||
end;
|
||||
|
||||
end.
|
||||
405
ATSynEdit/atsynedit/atsynedit_fold.inc
Normal file
@@ -0,0 +1,405 @@
|
||||
{$ifdef nn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.IsLineFolded(ALine: integer; ADetectPartialFold: boolean): boolean;
|
||||
var
|
||||
Flag: integer;
|
||||
begin
|
||||
if not Strings.IsIndexValid(ALine) then
|
||||
begin
|
||||
Result:= false;
|
||||
Exit;
|
||||
end;
|
||||
|
||||
Flag:= Strings.LinesHidden[ALine, FEditorIndex];
|
||||
Result:= (Flag=-1) or (ADetectPartialFold and (Flag>0));
|
||||
end;
|
||||
|
||||
function TATSynEdit.IsLineFoldedFull(ALine: integer): boolean;
|
||||
begin
|
||||
Result:= IsLineFolded(ALine, false);
|
||||
end;
|
||||
|
||||
function TATSynEdit.GetFirstUnfoldedLineNumber: integer;
|
||||
begin
|
||||
Result:= GetNextUnfoldedLineNumber(0, true);
|
||||
end;
|
||||
|
||||
function TATSynEdit.GetLastUnfoldedLineNumber: integer;
|
||||
begin
|
||||
Result:= GetNextUnfoldedLineNumber(Strings.Count-1, false);
|
||||
end;
|
||||
|
||||
function TATSynEdit.GetNextUnfoldedLineNumber(ALine: integer; ADown: boolean): integer;
|
||||
var
|
||||
N: integer;
|
||||
begin
|
||||
Result:= ALine;
|
||||
N:= Result;
|
||||
while IsLineFolded(N) and Strings.IsIndexValid(N) do
|
||||
N:= N+BoolToPlusMinusOne(ADown);
|
||||
if Strings.IsIndexValid(N) then Result:= N;
|
||||
end;
|
||||
|
||||
function TATSynEdit.IsPosFolded(AX, AY: integer): boolean;
|
||||
begin
|
||||
Result:= Strings.IsPosFolded(AX, AY, FEditorIndex);
|
||||
end;
|
||||
|
||||
(*
|
||||
example of CPP file which is hard to unfold (if nested ranges folded).
|
||||
{
|
||||
d1
|
||||
{
|
||||
d2a
|
||||
}
|
||||
{
|
||||
d2b
|
||||
{
|
||||
d3a
|
||||
}
|
||||
{
|
||||
d3b
|
||||
{
|
||||
d4a
|
||||
}
|
||||
{
|
||||
d4b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
what we do. for each line in range:
|
||||
a) if line not in any subrange, show it
|
||||
b) for all subranges at top level:
|
||||
b1) if subrange marked folded, unfold 1st line "[...]"
|
||||
b2) if subrange marked unfolded, recursion
|
||||
*)
|
||||
procedure TATSynEdit.DoRangeUnfold(ARange: TATSynRange);
|
||||
var
|
||||
List: TATIntArray;
|
||||
R: TATSynRange;
|
||||
i, j: integer;
|
||||
InSubrange: boolean;
|
||||
begin
|
||||
ARange.Folded:= false;
|
||||
FWrapUpdateNeeded:= true;
|
||||
|
||||
List:= FFold.FindRangesContainingLines(-1, -1, ARange,
|
||||
false{OnlyFolded}, true{TopLevel}, cRngIgnore);
|
||||
|
||||
//show all lines not in subranges
|
||||
for i:= ARange.Y to ARange.Y2 do
|
||||
begin
|
||||
InSubrange:= false;
|
||||
for j:= Low(List) to High(List) do
|
||||
if FFold[List[j]].IsLineInside(i) then
|
||||
begin
|
||||
InSubrange:= true;
|
||||
Break
|
||||
end;
|
||||
|
||||
if not InSubrange then
|
||||
Strings.LinesHidden[i, FEditorIndex]:= 0;
|
||||
end;
|
||||
|
||||
//unfold subranges, resursion
|
||||
for i:= Low(List) to High(List) do
|
||||
begin
|
||||
R:= FFold[List[i]];
|
||||
if R.Folded then
|
||||
Strings.LinesHidden[R.Y, FEditorIndex]:= R.X
|
||||
else
|
||||
DoRangeUnfold(R);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoRangeFold(ARange: TATSynRange);
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
ARange.Folded:= true;
|
||||
FWrapUpdateNeeded:= true;
|
||||
|
||||
//partially fold 1st line
|
||||
if ARange.Hint<>'' then
|
||||
begin
|
||||
Strings.LinesHidden[ARange.Y, FEditorIndex]:= ARange.X;
|
||||
end
|
||||
else
|
||||
case FFoldStyle of
|
||||
cFoldHereWithDots:
|
||||
begin
|
||||
Strings.LinesHidden[ARange.Y, FEditorIndex]:= ARange.X;
|
||||
end;
|
||||
cFoldHereWithTruncatedText:
|
||||
begin
|
||||
Strings.LinesHidden[ARange.Y, FEditorIndex]:= ARange.X;
|
||||
ARange.Hint:= Copy(Strings.Lines[ARange.Y], ARange.X, cFoldedLenOfEmptyHint)+'...';
|
||||
end;
|
||||
cFoldFromEndOfLine:
|
||||
begin
|
||||
Strings.LinesHidden[ARange.Y, FEditorIndex]:= Length(Strings.Lines[ARange.Y])+1;
|
||||
end;
|
||||
cFoldFromNextLine:
|
||||
begin
|
||||
//don't fold line
|
||||
end;
|
||||
end;
|
||||
|
||||
//fully fold next lines
|
||||
for i:= ARange.Y+1 to ARange.Y2 do
|
||||
Strings.LinesHidden[i, FEditorIndex]:= -1;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoUnfoldLine(ALine: integer);
|
||||
var
|
||||
List: TATIntArray;
|
||||
i: integer;
|
||||
begin
|
||||
List:= FFold.FindRangesContainingLines(ALine, ALine, nil,
|
||||
true{OnlyFolded}, false{TopLevelOnly}, cRngHasAllLines);
|
||||
for i:= Low(List) to High(List) do
|
||||
DoRangeUnfold(FFold[List[i]]);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoFoldbarClick(ALine: integer);
|
||||
var
|
||||
R: TATSynRange;
|
||||
begin
|
||||
R:= FFold.FindRangeWithPlusAtLine(ALine);
|
||||
if Assigned(R) then
|
||||
begin
|
||||
if R.Folded then
|
||||
DoRangeUnfold(R)
|
||||
else
|
||||
DoRangeFold(R);
|
||||
Update;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
function TATSynEdit.GetFoldedMarkText(ALine: integer): string;
|
||||
var
|
||||
R: TATSynRange;
|
||||
begin
|
||||
Result:= '';
|
||||
R:= FFold.FindRangeWithPlusAtLine(ALine);
|
||||
if Assigned(R) then
|
||||
Result:= R.Hint;
|
||||
if Result='' then
|
||||
Result:= '...';
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.UpdateFoldedFromLinesHidden;
|
||||
var
|
||||
i, j: integer;
|
||||
N: integer;
|
||||
R: TATSynRange;
|
||||
begin
|
||||
for i:= 0 to Strings.Count-1 do
|
||||
begin
|
||||
N:= Strings.LinesHidden[i, FEditorIndex];
|
||||
if N<=0 then Continue;
|
||||
|
||||
for j:= 0 to Fold.Count-1 do
|
||||
begin
|
||||
R:= Fold.Items[j];
|
||||
if (R.Y>i) then Break;
|
||||
if (R.Y=i) and (R.X=N) then
|
||||
begin
|
||||
DoRangeFold(R); //do not just R.Folded:= true;
|
||||
Break
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynEdit.IsFoldLineNeededBeforeWrapitem(N: integer): boolean;
|
||||
var
|
||||
NLineCur, NLinePrev: integer;
|
||||
begin
|
||||
if FWrapInfo.IsIndexValid(N) and (N>0) then
|
||||
begin
|
||||
NLineCur:= FWrapInfo.Items[N].NLineIndex;
|
||||
NLinePrev:= FWrapInfo.Items[N-1].NLineIndex;
|
||||
//before this line some is skipped
|
||||
Result:= NLineCur-NLinePrev > 1;
|
||||
//and prev visible line is fully visible
|
||||
if Result then
|
||||
Result:= Strings.LinesHidden[NLinePrev, FEditorIndex]=0;
|
||||
end
|
||||
else
|
||||
Result:= false;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoMenuGutterFold;
|
||||
var
|
||||
Menu: TPopupMenu;
|
||||
mi, miSub: TMenuItem;
|
||||
i: integer;
|
||||
begin
|
||||
InitResourcesFoldbar;
|
||||
if FMenuGutterFoldStd=nil then
|
||||
FMenuGutterFoldStd:= TPopupMenu.Create(Self);
|
||||
|
||||
Menu:= FMenuGutterFoldStd;
|
||||
Menu.Images:= FFoldImageList;
|
||||
Menu.Items.Clear;
|
||||
|
||||
//items "fold all", "unfold all"
|
||||
mi:= TMenuItem.Create(Self);
|
||||
mi.Caption:= cStrMenuItemFoldAll;
|
||||
mi.OnClick:= @MenuFoldFoldAllClick;
|
||||
mi.Enabled:= Fold.Count>0;
|
||||
Menu.Items.Add(mi);
|
||||
|
||||
mi:= TMenuItem.Create(Self);
|
||||
mi.Caption:= cStrMenuItemUnfoldAll;
|
||||
mi.OnClick:= @MenuFoldUnfoldAllClick;
|
||||
mi.Enabled:= Fold.Count>0;
|
||||
Menu.Items.Add(mi);
|
||||
|
||||
//submenu "fold level"
|
||||
miSub:= TMenuItem.Create(Self);
|
||||
miSub.Caption:= cStrMenuItemFoldLevel;
|
||||
miSub.Enabled:= Fold.Count>0;
|
||||
Menu.Items.Add(miSub);
|
||||
|
||||
for i:= 2 to 9 do
|
||||
begin
|
||||
mi:= TMenuItem.Create(Self);
|
||||
mi.Caption:= Inttostr(i);
|
||||
mi.Tag:= i-1;
|
||||
mi.OnClick:=@MenuFoldLevelClick;
|
||||
miSub.Add(mi);
|
||||
end;
|
||||
|
||||
//dynamic items [+], [-]
|
||||
DoMenuGutterFold_AddDynamicItems(Menu);
|
||||
|
||||
Menu.Popup;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoMenuGutterFold_AddDynamicItems(Menu: TPopupMenu);
|
||||
var
|
||||
Pnt: TPoint;
|
||||
AtEnd: boolean;
|
||||
NLine: integer;
|
||||
IntList: TATIntArray;
|
||||
Rng: TATSynRange;
|
||||
mi: TMenuItem;
|
||||
i: integer;
|
||||
begin
|
||||
//calc ranges for curr line
|
||||
Pnt:= ScreenToClient(Mouse.CursorPos);
|
||||
Pnt:= ClientPosToCaretPos(Pnt, AtEnd);
|
||||
NLine:= Pnt.Y;
|
||||
if NLine<0 then Exit;
|
||||
|
||||
IntList:= Fold.FindRangesContainingLines(NLine, NLine, nil,
|
||||
false{OnlyFolded}, false{TopLevel}, cRngHasAllLines);
|
||||
if Length(IntList)=0 then Exit;
|
||||
|
||||
//separator
|
||||
mi:= TMenuItem.Create(Self);
|
||||
mi.Caption:= '-';
|
||||
Menu.Items.Add(mi);
|
||||
|
||||
//items for ranges for current line
|
||||
for i:= 0 to High(IntList) do
|
||||
begin
|
||||
Rng:= Fold[IntList[i]];
|
||||
mi:= TMenuItem.Create(Self);
|
||||
mi.Tag:= ptrint(Rng);
|
||||
mi.OnClick:= @MenuFoldPlusMinusClick;
|
||||
|
||||
mi.Caption:=
|
||||
cHintScrollPrefix+' '+Inttostr(Rng.Y+1)+': '+
|
||||
UTF8Encode(Copy(Strings.Lines[Rng.Y], 1, cFoldedLenOfEmptyHint));
|
||||
|
||||
if Rng.Folded then
|
||||
mi.ImageIndex:= 0
|
||||
else
|
||||
mi.ImageIndex:= 1;
|
||||
|
||||
Menu.Items.Add(mi);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.InitResourcesFoldbar;
|
||||
begin
|
||||
if FFoldImageList=nil then
|
||||
begin
|
||||
FFoldImageList:= TImageList.Create(Self);
|
||||
FFoldImageList.Width:= 12;
|
||||
FFoldImageList.Height:= 12;
|
||||
FFoldImageList.AddResourceName(HInstance, 'FOLDBAR_P');
|
||||
FFoldImageList.AddResourceName(HInstance, 'FOLDBAR_M');
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.MenuFoldPlusMinusClick(Sender: TObject);
|
||||
var
|
||||
Rng: TATSynRange;
|
||||
begin
|
||||
Rng:= TATSynRange((Sender as TComponent).Tag);
|
||||
if Rng.Folded then
|
||||
DoRangeUnfold(Rng)
|
||||
else
|
||||
DoRangeFold(Rng);
|
||||
Update;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.MenuFoldFoldAllClick(Sender: TObject);
|
||||
begin
|
||||
DoCommand(cCommand_FoldAll);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.MenuFoldLevelClick(Sender: TObject);
|
||||
begin
|
||||
DoFoldForLevel((Sender as TComponent).Tag);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.MenuFoldUnfoldAllClick(Sender: TObject);
|
||||
begin
|
||||
DoCommand(cCommand_UnfoldAll);
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoFoldForLevelAndLines(ALineFrom, ALineTo: integer;
|
||||
ALevel: integer; AForThisRange: TATSynRange);
|
||||
var
|
||||
List: TATIntArray;
|
||||
R: TATSynRange;
|
||||
i: integer;
|
||||
begin
|
||||
//this func recursive. it calls itself with ALevel-1.
|
||||
//folds ranges if ALevel=0, else goes to subranges until found ALevel=0.
|
||||
if ALevel<0 then exit;
|
||||
|
||||
List:= Fold.FindRangesContainingLines(ALineFrom, ALineTo, AForThisRange,
|
||||
false{OnlyFolded}, true{TopLevel}, cRngExceptThisRange);
|
||||
|
||||
for i:= Low(List) to High(List) do
|
||||
begin
|
||||
R:= Fold.Items[List[i]];
|
||||
if R.IsSimple then Continue;
|
||||
if R.Folded then Continue;
|
||||
if ALevel=0 then
|
||||
DoRangeFold(R)
|
||||
else
|
||||
DoFoldForLevelAndLines(R.Y, R.Y2, ALevel-1, R);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoFoldForLevel(ALevel: integer);
|
||||
begin
|
||||
DoCommand(cCommand_UnfoldAll);
|
||||
DoFoldForLevelAndLines(0, Strings.Count-1, ALevel, nil);
|
||||
Update;
|
||||
end;
|
||||
|
||||
31
ATSynEdit/atsynedit/atsynedit_form_complete.lfm
Normal file
@@ -0,0 +1,31 @@
|
||||
object FormATSynEditComplete: TFormATSynEditComplete
|
||||
Left = 528
|
||||
Height = 240
|
||||
Top = 447
|
||||
Width = 320
|
||||
BorderStyle = bsNone
|
||||
ClientHeight = 240
|
||||
ClientWidth = 320
|
||||
FormStyle = fsStayOnTop
|
||||
KeyPreview = True
|
||||
OnClose = FormClose
|
||||
OnCreate = FormCreate
|
||||
OnDeactivate = FormDeactivate
|
||||
OnDestroy = FormDestroy
|
||||
OnKeyDown = FormKeyDown
|
||||
OnShow = FormShow
|
||||
OnUTF8KeyPress = FormUTF8KeyPress
|
||||
LCLVersion = '1.5'
|
||||
object List: TATListbox
|
||||
Left = 4
|
||||
Height = 232
|
||||
Top = 4
|
||||
Width = 312
|
||||
Align = alClient
|
||||
BorderSpacing.Around = 4
|
||||
Color = clSilver
|
||||
ItemHeight = 28
|
||||
OnClick = ListClick
|
||||
OnDrawItem = ListDrawItem
|
||||
end
|
||||
end
|
||||
447
ATSynEdit/atsynedit/atsynedit_form_complete.pas
Normal file
@@ -0,0 +1,447 @@
|
||||
unit atsynedit_form_complete;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Forms, Controls, Graphics,
|
||||
Dialogs,
|
||||
LclProc, LclType,
|
||||
ATSynEdit,
|
||||
ATSynEdit_Carets,
|
||||
ATSynEdit_Commands,
|
||||
ATStringProc,
|
||||
ATListbox,
|
||||
Math;
|
||||
|
||||
type
|
||||
TATCompletionPropEvent = procedure (Sender: TObject;
|
||||
out AText, ASuffix: string; out ACharsLeft, ACharsRight: integer) of object;
|
||||
|
||||
//AText is #13-separated strings, each string is '|'-separated items.
|
||||
//Usually item_0 is prefix to show,
|
||||
//item_1 is actual text (result of function),
|
||||
//item_2..etc are only to show.
|
||||
//e.g. 'func|Func1|(param1, param2)'+#13+'var|Var1'+#13+'var|Var2'
|
||||
//AChars: how many chars to replace before caret.
|
||||
|
||||
procedure DoEditorCompletionListbox(AEd: TATSynEdit;
|
||||
AOnGetProp: TATCompletionPropEvent);
|
||||
|
||||
procedure EditorGetCurrentWord(Ed: TATSynEdit; const AWordChars: atString;
|
||||
out AWord: atString; out ACharsLeft, ACharsRight: integer);
|
||||
|
||||
type
|
||||
{ TFormATSynEditComplete }
|
||||
|
||||
TFormATSynEditComplete = class(TForm)
|
||||
List: TATListbox;
|
||||
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
|
||||
procedure FormCreate(Sender: TObject);
|
||||
procedure FormDeactivate(Sender: TObject);
|
||||
procedure FormDestroy(Sender: TObject);
|
||||
procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
|
||||
procedure FormShow(Sender: TObject);
|
||||
procedure FormUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
|
||||
procedure ListClick(Sender: TObject);
|
||||
procedure ListDrawItem(Sender: TObject; C: TCanvas; AIndex: integer;
|
||||
const ARect: TRect);
|
||||
private
|
||||
{ private declarations }
|
||||
SList: TStringlist;
|
||||
FOnGetProp: TATCompletionPropEvent;
|
||||
FEdit: TATSynEdit;
|
||||
FCharsLeft,
|
||||
FCharsRight: integer;
|
||||
FSuffix: string;
|
||||
FHintWnd: THintWindow;
|
||||
procedure DoHintHide;
|
||||
procedure DoHintShow(const AHint: string);
|
||||
procedure DoReplaceTo(const Str: string);
|
||||
procedure DoResult;
|
||||
procedure DoUpdate;
|
||||
function GetItemText(S: string; AIndex: integer): string;
|
||||
function GetResultText: string;
|
||||
public
|
||||
{ public declarations }
|
||||
property Editor: TATSynEdit read FEdit write FEdit;
|
||||
property OnGetProp: TATCompletionPropEvent read FOnGetProp write FOnGetProp;
|
||||
end;
|
||||
|
||||
const
|
||||
cCompleteItemCount = 5;
|
||||
var
|
||||
cCompleteColorFont: array[0..cCompleteItemCount-1] of TColor =
|
||||
(clPurple, clBlack, clNavy, clBlack, clBlack);
|
||||
cCompleteFontStyles: array[0..cCompleteItemCount-1] of TFontStyles =
|
||||
([fsBold], [], [], [], []);
|
||||
cCompleteColorBg: TColor = $e0e0e0;
|
||||
cCompleteColorSelBg: TColor = clMedGray;
|
||||
|
||||
cCompleteIndexOfText: integer = 1;
|
||||
cCompleteIndexOfDesc: integer = 2;
|
||||
cCompleteSepChar: char = '|';
|
||||
cCompleteHintChar: char = #9;
|
||||
cCompleteListSort: boolean = false;
|
||||
cCompleteKeyUpDownWrap: boolean = true;
|
||||
cCompleteInsertAlsoBracket: boolean = true;
|
||||
cCompleteFontName: string = 'default';
|
||||
cCompleteFontSize: integer = 10;
|
||||
cCompleteItemHeight: integer = 17;
|
||||
cCompleteBorderSize: integer = 4;
|
||||
cCompleteFormSizeX: integer = 500;
|
||||
cCompleteFormSizeY: integer = 200;
|
||||
cCompleteHintSizeX: integer = 400;
|
||||
cCompleteTextIndent0: integer = 4;
|
||||
cCompleteTextIndent: integer = 8;
|
||||
|
||||
implementation
|
||||
|
||||
{$R *.lfm}
|
||||
|
||||
var
|
||||
FormComplete: TFormATSynEditComplete = nil;
|
||||
|
||||
procedure DoEditorCompletionListbox(AEd: TATSynEdit;
|
||||
AOnGetProp: TATCompletionPropEvent);
|
||||
begin
|
||||
if AEd.ModeReadOnly then exit;
|
||||
if AEd.Carets.Count<>1 then exit;
|
||||
|
||||
if FormComplete=nil then
|
||||
FormComplete:= TFormATSynEditComplete.Create(nil);
|
||||
|
||||
FormComplete.Editor:= AEd;
|
||||
FormComplete.OnGetProp:= AOnGetProp;
|
||||
FormComplete.DoUpdate;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.DoReplaceTo(const Str: string);
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
Pos, Shift, PosAfter: TPoint;
|
||||
begin
|
||||
if Str<>'' then
|
||||
begin
|
||||
Caret:= Editor.Carets[0];
|
||||
Pos.X:= Caret.PosX;
|
||||
Pos.Y:= Caret.PosY;
|
||||
|
||||
FCharsLeft:= Min(Pos.X, FCharsLeft);
|
||||
Dec(Pos.X, FCharsLeft);
|
||||
Editor.Strings.TextDeleteRight(Pos.X, Pos.Y, FCharsLeft+FCharsRight, Shift, PosAfter, false);
|
||||
Editor.Strings.TextInsert(Pos.X, Pos.Y, Utf8Decode(Str), false, Shift, PosAfter);
|
||||
|
||||
Caret.PosX:= Pos.X+Length(Utf8Decode(Str));
|
||||
Caret.EndX:= -1;
|
||||
Caret.EndY:= -1;
|
||||
|
||||
Editor.Update(true);
|
||||
Editor.DoEventChange;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TFormATSynEditComplete }
|
||||
|
||||
procedure TFormATSynEditComplete.FormCreate(Sender: TObject);
|
||||
begin
|
||||
SList:= TStringList.Create;
|
||||
FHintWnd:= THintWindow.Create(Self);
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.FormDeactivate(Sender: TObject);
|
||||
begin
|
||||
Close;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.FormClose(Sender: TObject;
|
||||
var CloseAction: TCloseAction);
|
||||
begin
|
||||
DoHintHide;
|
||||
if Assigned(FEdit) then
|
||||
FEdit.OptCaretStopUnfocused:= true;
|
||||
CloseAction:= caHide;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.FormDestroy(Sender: TObject);
|
||||
begin
|
||||
SList.Free;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.FormKeyDown(Sender: TObject; var Key: Word;
|
||||
Shift: TShiftState);
|
||||
begin
|
||||
if (key=vk_up) and (shift=[]) then
|
||||
begin
|
||||
if List.ItemIndex>0 then
|
||||
List.ItemIndex:= List.ItemIndex-1
|
||||
else
|
||||
if cCompleteKeyUpDownWrap then
|
||||
List.ItemIndex:= List.ItemCount-1;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=vk_down) and (shift=[]) then
|
||||
begin
|
||||
if List.ItemIndex<List.ItemCount-1 then
|
||||
List.ItemIndex:= List.ItemIndex+1
|
||||
else
|
||||
if cCompleteKeyUpDownWrap then
|
||||
List.ItemIndex:= 0;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=VK_PRIOR) and (shift=[]) then
|
||||
begin
|
||||
List.ItemIndex:= Max(0, List.ItemIndex-List.VisibleItems);
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=VK_NEXT) and (shift=[]) then
|
||||
begin
|
||||
List.ItemIndex:= Min(List.Itemcount-1, List.ItemIndex+List.VisibleItems);
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=vk_home) then
|
||||
begin
|
||||
List.ItemIndex:= 0;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=vk_end) then
|
||||
begin
|
||||
List.ItemIndex:= List.ItemCount-1;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=VK_ESCAPE) then
|
||||
begin
|
||||
Close;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=VK_RETURN) or (key=VK_TAB) then
|
||||
begin
|
||||
DoResult;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=VK_LEFT) and (shift=[]) then
|
||||
begin
|
||||
Editor.DoCommand(cCommand_KeyLeft, '');
|
||||
DoUpdate;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
|
||||
if (key=VK_RIGHT) and (shift=[]) then
|
||||
begin
|
||||
Editor.DoCommand(cCommand_KeyRight, '');
|
||||
DoUpdate;
|
||||
key:= 0;
|
||||
exit
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.FormShow(Sender: TObject);
|
||||
begin
|
||||
if Assigned(FEdit) then
|
||||
FEdit.OptCaretStopUnfocused:= false;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.FormUTF8KeyPress(Sender: TObject;
|
||||
var UTF8Key: TUTF8Char);
|
||||
var
|
||||
Str: atString;
|
||||
begin
|
||||
inherited;
|
||||
|
||||
//backsp
|
||||
if (UTF8Key=#8) then
|
||||
begin
|
||||
FEdit.DoCommand(cCommand_KeyBackspace, '');
|
||||
DoUpdate;
|
||||
Utf8Key:= '';
|
||||
exit;
|
||||
end;
|
||||
|
||||
//skip control Ascii chars
|
||||
if Ord(UTF8Key[1])<32 then Exit;
|
||||
|
||||
Str:= Utf8Decode(Utf8Key);
|
||||
FEdit.DoCommand(cCommand_TextInsert, Str);
|
||||
DoUpdate;
|
||||
Utf8Key:= '';
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.ListClick(Sender: TObject);
|
||||
begin
|
||||
DoResult;
|
||||
end;
|
||||
|
||||
function TFormATSynEditComplete.GetItemText(S: string; AIndex: integer): string;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= 0 to AIndex do
|
||||
Result:= SGetItem(S, cCompleteSepChar);
|
||||
end;
|
||||
|
||||
function TFormATSynEditComplete.GetResultText: string;
|
||||
var
|
||||
SText, SDesc: string;
|
||||
begin
|
||||
Result:= '';
|
||||
if List.ItemIndex>=0 then
|
||||
begin
|
||||
SText:= GetItemText(SList[List.ItemIndex], cCompleteIndexOfText);
|
||||
SDesc:= GetItemText(SList[List.ItemIndex], cCompleteIndexOfDesc);
|
||||
Result:= SText;
|
||||
|
||||
if FSuffix<>'' then
|
||||
Result:= Result+FSuffix
|
||||
else
|
||||
if cCompleteInsertAlsoBracket then
|
||||
if SBeginsWith(SDesc, '(') then
|
||||
Result:= Result+'(';
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.ListDrawItem(Sender: TObject; C: TCanvas;
|
||||
AIndex: integer; const ARect: TRect);
|
||||
var
|
||||
Str, SItem, SHint: string;
|
||||
NSize, i: integer;
|
||||
begin
|
||||
Str:= SList[AIndex];
|
||||
SHint:= SGetItemAtEnd(Str, cCompleteHintChar);
|
||||
|
||||
if AIndex=List.ItemIndex then
|
||||
DoHintShow(SHint);
|
||||
|
||||
if AIndex=List.ItemIndex then
|
||||
C.Brush.Color:= cCompleteColorSelBg
|
||||
else
|
||||
C.Brush.Color:= cCompleteColorBg;
|
||||
C.FillRect(ARect);
|
||||
|
||||
C.Font.Assign(List.Font);
|
||||
NSize:= cCompleteTextIndent0;
|
||||
|
||||
for i:= 0 to cCompleteItemCount-1 do
|
||||
begin
|
||||
SItem:= SGetItem(Str, cCompleteSepChar);
|
||||
C.Font.Style:= cCompleteFontStyles[i];
|
||||
C.Font.Color:= cCompleteColorFont[i];
|
||||
C.TextOut(ARect.Left+NSize, ARect.Top, SItem);
|
||||
Inc(NSize, C.TextWidth(SItem)+cCompleteTextIndent);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.DoResult;
|
||||
begin
|
||||
DoReplaceTo(GetResultText);
|
||||
Close;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.DoUpdate;
|
||||
var
|
||||
AText: string;
|
||||
P: TPoint;
|
||||
begin
|
||||
if Assigned(FOnGetProp) then
|
||||
FOnGetProp(Editor, AText, FSuffix, FCharsLeft, FCharsRight);
|
||||
|
||||
if (AText='') then
|
||||
begin Close; exit end;
|
||||
|
||||
SList.Text:= AText;
|
||||
if SList.Count=0 then exit;
|
||||
if cCompleteListSort then SList.Sort;
|
||||
|
||||
List.ItemCount:= SList.Count;
|
||||
List.ItemIndex:= 0;
|
||||
|
||||
Color:= cCompleteColorBg;
|
||||
List.Color:= cCompleteColorBg;
|
||||
List.Font.Name:= cCompleteFontName;
|
||||
List.Font.Size:= cCompleteFontSize;
|
||||
List.ItemHeight:= cCompleteItemHeight;
|
||||
List.BorderSpacing.Around:= cCompleteBorderSize;
|
||||
List.Invalidate;
|
||||
|
||||
P.X:= Editor.Carets[0].CoordX-Editor.TextCharSize.X*FCharsLeft;
|
||||
P.Y:= Editor.Carets[0].CoordY+Editor.TextCharSize.Y;
|
||||
P:= Editor.ClientToScreen(P);
|
||||
|
||||
SetBounds(P.X, P.Y, cCompleteFormSizeX, cCompleteFormSizeY);
|
||||
Show;
|
||||
end;
|
||||
|
||||
|
||||
procedure EditorGetCurrentWord(Ed: TATSynEdit; const AWordChars: atString;
|
||||
out AWord: atString; out ACharsLeft, ACharsRight: integer);
|
||||
var
|
||||
str: atString;
|
||||
n: integer;
|
||||
begin
|
||||
AWord:= '';
|
||||
ACharsLeft:= 0;
|
||||
ACharsRight:= 0;
|
||||
|
||||
str:= Ed.Strings.Lines[Ed.Carets[0].PosY];
|
||||
n:= Ed.Carets[0].PosX;
|
||||
if (n>Length(str)) then exit;
|
||||
|
||||
while (n>0) and (IsCharWord(str[n], AWordChars)) do
|
||||
begin
|
||||
AWord:= str[n]+AWord;
|
||||
Dec(n);
|
||||
Inc(ACharsLeft);
|
||||
end;
|
||||
|
||||
n:= Ed.Carets[0].PosX;
|
||||
while (n<Length(str)) and (IsCharWord(str[n+1], AWordChars)) do
|
||||
begin
|
||||
Inc(n);
|
||||
Inc(ACharsRight);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.DoHintShow(const AHint: string);
|
||||
var
|
||||
P: TPoint;
|
||||
R: TRect;
|
||||
begin
|
||||
R:= FHintWnd.CalcHintRect(cCompleteHintSizeX, AHint, nil);
|
||||
|
||||
P:= ClientToScreen(Point(Width, 0));
|
||||
OffsetRect(R, P.X, P.Y);
|
||||
|
||||
FHintWnd.ActivateHint(R, AHint);
|
||||
FHintWnd.Invalidate; //for Win
|
||||
Editor.Invalidate; //for Win
|
||||
end;
|
||||
|
||||
procedure TFormATSynEditComplete.DoHintHide;
|
||||
begin
|
||||
if Assigned(FHintWnd) then
|
||||
FHintWnd.Hide;
|
||||
end;
|
||||
|
||||
finalization
|
||||
if Assigned(FormComplete) then
|
||||
FormComplete.Free;
|
||||
|
||||
end.
|
||||
|
||||
171
ATSynEdit/atsynedit/atsynedit_form_complete_css.pas
Normal file
@@ -0,0 +1,171 @@
|
||||
unit atsynedit_form_complete_css;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics,
|
||||
ATSynEdit,
|
||||
ATSynEdit_Carets,
|
||||
RegExpr,
|
||||
Dialogs;
|
||||
|
||||
//it needs file css_list.ini from SynWrite distro
|
||||
procedure DoEditorCompletionCss(AEdit: TATSynEdit;
|
||||
const AFilenameCssList: string);
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
ATStringProc,
|
||||
ATSynEdit_form_complete;
|
||||
|
||||
type
|
||||
{ TAcp }
|
||||
|
||||
TAcp = class
|
||||
private
|
||||
List: TStringlist;
|
||||
procedure DoOnGetCompleteProp(Sender: TObject; out AText, ASuffix: string;
|
||||
out ACharsLeft, ACharsRight: integer);
|
||||
public
|
||||
Ed: TATSynEdit;
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
var
|
||||
Acp: TAcp = nil;
|
||||
|
||||
|
||||
function SFindRegex(const SText, SRegex: string; NGroup: integer): string;
|
||||
var
|
||||
R: TRegExpr;
|
||||
begin
|
||||
Result:= '';
|
||||
R:= TRegExpr.Create;
|
||||
try
|
||||
R.ModifierS:= false;
|
||||
R.ModifierM:= true;
|
||||
R.ModifierI:= true;
|
||||
|
||||
R.Expression:= SRegex;
|
||||
R.InputString:= SText;
|
||||
|
||||
if R.ExecPos(1) then
|
||||
Result:= Copy(SText, R.MatchPos[NGroup], R.MatchLen[NGroup]);
|
||||
finally
|
||||
R.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function EditorGetCssTag(Ed: TATSynEdit): string;
|
||||
const
|
||||
//char class for all chars in css values
|
||||
cRegexChars = '[''"\w\s\.,:/~&%@!=\#\$\^\-\+\(\)\?]';
|
||||
//regex to catch css property name, before css attribs and before ":", at line end
|
||||
cRegexProp = '([\w\-]+):\s*' + cRegexChars + '*$';
|
||||
cRegexGroup = 1; //group 1 in (..)
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
S: atString;
|
||||
begin
|
||||
Result:= '';
|
||||
Caret:= Ed.Carets[0];
|
||||
S:= Ed.Strings.Lines[Caret.PosY];
|
||||
S:= Copy(S, 1, Caret.PosX);
|
||||
if S<>'' then
|
||||
Result:= SFindRegex(S, cRegexProp, cRegexGroup);
|
||||
end;
|
||||
|
||||
|
||||
procedure TAcp.DoOnGetCompleteProp(Sender: TObject; out AText, ASuffix: string;
|
||||
out ACharsLeft, ACharsRight: integer);
|
||||
const
|
||||
cWordChars = '-#!@.'; //don't include ':'
|
||||
var
|
||||
s_word: atString;
|
||||
s_tag, s_item, s_val: string;
|
||||
n: integer;
|
||||
ok: boolean;
|
||||
begin
|
||||
AText:= '';
|
||||
ASuffix:= '';
|
||||
ACharsLeft:= 0;
|
||||
ACharsRight:= 0;
|
||||
|
||||
s_tag:= EditorGetCssTag(Ed);
|
||||
if s_tag<>'' then
|
||||
//show list of values for s_tag
|
||||
begin
|
||||
s_item:= List.Values[s_tag];
|
||||
if s_item='' then exit;
|
||||
repeat
|
||||
s_val:= SGetItem(s_item);
|
||||
if s_val='' then Break;
|
||||
AText:= AText+'css '+s_tag+'|'+s_val+#13;
|
||||
until false;
|
||||
end
|
||||
else
|
||||
//show list of all tags
|
||||
begin
|
||||
ASuffix:= ': ';
|
||||
EditorGetCurrentWord(Ed, cWordChars, s_word, ACharsLeft, ACharsRight);
|
||||
|
||||
for n:= 0 to List.Count-1 do
|
||||
begin
|
||||
s_item:= List.Names[n];
|
||||
|
||||
//filter by cur word (not case sens)
|
||||
if s_word<>'' then
|
||||
begin
|
||||
ok:= SBeginsWith(UpperCase(s_item), UpperCase(s_word));
|
||||
if not ok then Continue;
|
||||
end;
|
||||
|
||||
AText:= AText+'css'+'|'+s_item+#13;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TAcp.Create;
|
||||
begin
|
||||
inherited;
|
||||
List:= TStringlist.create;
|
||||
end;
|
||||
|
||||
destructor TAcp.Destroy;
|
||||
begin
|
||||
FreeAndNil(List);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure DoEditorCompletionCss(AEdit: TATSynEdit;
|
||||
const AFilenameCssList: string);
|
||||
begin
|
||||
Acp.Ed:= AEdit;
|
||||
|
||||
//load file only once
|
||||
if Acp.List.Count=0 then
|
||||
begin
|
||||
if not FileExists(AFilenameCssList) then exit;
|
||||
Acp.List.LoadFromFile(AFilenameCssList);
|
||||
end;
|
||||
|
||||
DoEditorCompletionListbox(AEdit, @Acp.DoOnGetCompleteProp);
|
||||
end;
|
||||
|
||||
initialization
|
||||
Acp:= TAcp.Create;
|
||||
|
||||
cCompleteFontStyles[0]:= [];
|
||||
cCompleteColorFont[0]:= clPurple;
|
||||
cCompleteColorFont[1]:= clBlack;
|
||||
|
||||
finalization
|
||||
FreeAndNil(Acp);
|
||||
|
||||
end.
|
||||
|
||||
263
ATSynEdit/atsynedit/atsynedit_form_complete_html.pas
Normal file
@@ -0,0 +1,263 @@
|
||||
unit atsynedit_form_complete_html;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Graphics,
|
||||
ATSynEdit,
|
||||
ATSynEdit_Carets,
|
||||
RegExpr,
|
||||
Dialogs;
|
||||
|
||||
//it needs file html_list.ini from SynWrite distro
|
||||
procedure DoEditorCompletionHtml(AEdit: TATSynEdit;
|
||||
const AFilenameHtmlList: string);
|
||||
|
||||
type
|
||||
TCompleteHtmlMode = (
|
||||
acpModeNone,
|
||||
acpModeTags,
|
||||
acpModeTagsClose,
|
||||
acpModeAttrs,
|
||||
acpModeVals
|
||||
);
|
||||
|
||||
//detect tag and its attribute at caret pos
|
||||
procedure EditorGetHtmlTag(Ed: TATSynedit; out STag, SAttr: string;
|
||||
out AMode: TCompleteHtmlMode);
|
||||
function EditorHasCssAtCaret(Ed: TATSynEdit): boolean;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
ATStringProc,
|
||||
ATSynEdit_form_complete;
|
||||
|
||||
type
|
||||
{ TAcp }
|
||||
|
||||
TAcp = class
|
||||
private
|
||||
List: TStringlist;
|
||||
procedure DoOnGetCompleteProp(Sender: TObject; out AText, ASuffix: string;
|
||||
out ACharsLeft, ACharsRight: integer);
|
||||
public
|
||||
Ed: TATSynEdit;
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
var
|
||||
Acp: TAcp = nil;
|
||||
|
||||
function SFindRegex(const SText, SRegex: string; NGroup: integer): string;
|
||||
var
|
||||
R: TRegExpr;
|
||||
begin
|
||||
Result:= '';
|
||||
R:= TRegExpr.Create;
|
||||
try
|
||||
R.ModifierS:= false;
|
||||
R.ModifierM:= true;
|
||||
R.ModifierI:= true;
|
||||
|
||||
R.Expression:= SRegex;
|
||||
R.InputString:= SText;
|
||||
|
||||
if R.ExecPos(1) then
|
||||
Result:= Copy(SText, R.MatchPos[NGroup], R.MatchLen[NGroup]);
|
||||
finally
|
||||
R.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure EditorGetHtmlTag(Ed: TATSynedit; out STag, SAttr: string; out AMode: TCompleteHtmlMode);
|
||||
const
|
||||
//regex to catch tag name at line start
|
||||
cRegexTagPart = '^\w+\b';
|
||||
cRegexTagOnly = '^\w*$';
|
||||
cRegexTagClose = '^/\w*$';
|
||||
//character class for all chars inside quotes
|
||||
cRegexChars = '[\s\w,\.:;\-\+\*\?=\(\)\[\]\{\}/\\\|~`\^\$&%\#@!]';
|
||||
//regex to catch attrib name, followed by "=" and not-closed quote, only at line end
|
||||
cRegexAttr = '\b([\w\-]+)\s*\=\s*([''"]' + cRegexChars + '*)?$';
|
||||
//regex group
|
||||
cGroupTagPart = 0;
|
||||
cGroupTagOnly = 0;
|
||||
cGroupTagClose = 0;
|
||||
cGroupAttr = 1;
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
S: atString;
|
||||
N: integer;
|
||||
begin
|
||||
STag:= '';
|
||||
SAttr:= '';
|
||||
AMode:= acpModeNone;
|
||||
|
||||
//str before caret
|
||||
Caret:= Ed.Carets[0];
|
||||
S:= Ed.Strings.Lines[Caret.PosY];
|
||||
S:= Copy(S, 1, Caret.PosX);
|
||||
if S='' then Exit;
|
||||
|
||||
//cut string before last "<" or ">" char
|
||||
N:= Length(S);
|
||||
while (N>0) and (S[N]<>'<') and (S[N]<>'>') do Dec(N);
|
||||
if N=0 then Exit;
|
||||
Delete(S, 1, N);
|
||||
|
||||
STag:= SFindRegex(S, cRegexTagClose, cGroupTagClose);
|
||||
if STag<>'' then
|
||||
begin AMode:= acpModeTagsClose; exit end;
|
||||
|
||||
STag:= SFindRegex(S, cRegexTagOnly, cGroupTagOnly);
|
||||
if STag<>'' then
|
||||
begin AMode:= acpModeTags; exit end;
|
||||
|
||||
STag:= SFindRegex(S, cRegexTagPart, cGroupTagPart);
|
||||
if STag<>'' then
|
||||
begin
|
||||
SAttr:= SFindRegex(S, cRegexAttr, cGroupAttr);
|
||||
if SAttr<>'' then
|
||||
AMode:= acpModeVals
|
||||
else
|
||||
AMode:= acpModeAttrs;
|
||||
end
|
||||
else
|
||||
AMode:= acpModeTags;
|
||||
end;
|
||||
|
||||
function EditorHasCssAtCaret(Ed: TATSynEdit): boolean;
|
||||
var
|
||||
STag, SAttr: string;
|
||||
Mode: TCompleteHtmlMode;
|
||||
begin
|
||||
EditorGetHtmlTag(Ed, STag, SAttr, Mode);
|
||||
Result:= (Mode=acpModeVals) and (LowerCase(SAttr)='style');
|
||||
end;
|
||||
|
||||
|
||||
procedure TAcp.DoOnGetCompleteProp(Sender: TObject; out AText, ASuffix: string;
|
||||
out ACharsLeft, ACharsRight: integer);
|
||||
const
|
||||
cWordChars = '-';
|
||||
var
|
||||
mode: TCompleteHtmlMode;
|
||||
s_word: atString;
|
||||
s_tag, s_attr, s_item, s_subitem, s_value: string;
|
||||
i: integer;
|
||||
ok: boolean;
|
||||
begin
|
||||
AText:= '';
|
||||
ASuffix:= '';
|
||||
ACharsLeft:= 0;
|
||||
ACharsRight:= 0;
|
||||
|
||||
EditorGetHtmlTag(Ed, s_tag, s_attr, mode);
|
||||
EditorGetCurrentWord(Ed, cWordChars, s_word, ACharsLeft, ACharsRight);
|
||||
|
||||
case mode of
|
||||
acpModeTags,
|
||||
acpModeTagsClose:
|
||||
begin
|
||||
if mode=acpModeTagsClose then
|
||||
ASuffix:= '>'
|
||||
else
|
||||
ASuffix:= ' ';
|
||||
|
||||
for i:= 0 to List.Count-1 do
|
||||
begin
|
||||
s_item:= List.Names[i];
|
||||
|
||||
//filter items
|
||||
if s_word<>'' then
|
||||
begin
|
||||
ok:= SBeginsWith(UpperCase(s_item), UpperCase(s_word));
|
||||
if not ok then Continue;
|
||||
end;
|
||||
AText:= AText+'tag|'+s_item+#13;
|
||||
end;
|
||||
end;
|
||||
|
||||
acpModeAttrs:
|
||||
begin
|
||||
ASuffix:='=';
|
||||
s_item:= List.Values[s_tag];
|
||||
if s_item='' then exit;
|
||||
repeat
|
||||
s_subitem:= SGetItem(s_item, '|');
|
||||
if s_subitem='' then Break;
|
||||
s_subitem:= SGetItem(s_subitem, '<');
|
||||
|
||||
//filter items
|
||||
if s_word<>'' then
|
||||
begin
|
||||
ok:= SBeginsWith(UpperCase(s_subitem), UpperCase(s_word));
|
||||
if not ok then Continue;
|
||||
end;
|
||||
AText:= AText+s_tag+' attrib|'+s_subitem+#13;
|
||||
until false;
|
||||
end;
|
||||
|
||||
acpModeVals:
|
||||
begin
|
||||
ASuffix:=' ';
|
||||
s_item:= List.Values[s_tag];
|
||||
if s_item='' then exit;
|
||||
repeat
|
||||
s_subitem:= SGetItem(s_item, '|');
|
||||
if s_subitem='' then Break;
|
||||
if SGetItem(s_subitem, '<')<>s_attr then Continue;
|
||||
repeat
|
||||
s_value:= SGetItem(s_subitem, '?');
|
||||
if s_value='' then Break;
|
||||
AText:= AText+s_attr+' value|"'+s_value+'"'+#13;
|
||||
until false;
|
||||
until false;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TAcp.Create;
|
||||
begin
|
||||
inherited;
|
||||
List:= TStringlist.create;
|
||||
end;
|
||||
|
||||
destructor TAcp.Destroy;
|
||||
begin
|
||||
FreeAndNil(List);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure DoEditorCompletionHtml(AEdit: TATSynEdit;
|
||||
const AFilenameHtmlList: string);
|
||||
begin
|
||||
Acp.Ed:= AEdit;
|
||||
|
||||
//load file only once
|
||||
if Acp.List.Count=0 then
|
||||
begin
|
||||
if not FileExists(AFilenameHtmlList) then exit;
|
||||
Acp.List.LoadFromFile(AFilenameHtmlList);
|
||||
end;
|
||||
|
||||
DoEditorCompletionListbox(AEdit, @Acp.DoOnGetCompleteProp);
|
||||
end;
|
||||
|
||||
initialization
|
||||
Acp:= TAcp.Create;
|
||||
|
||||
cCompleteFontStyles[0]:= [];
|
||||
cCompleteColorFont[0]:= clPurple;
|
||||
cCompleteColorFont[1]:= clBlack;
|
||||
|
||||
finalization
|
||||
FreeAndNil(Acp);
|
||||
|
||||
end.
|
||||
|
||||
237
ATSynEdit/atsynedit/atsynedit_form_complete_synwrite.pas
Normal file
@@ -0,0 +1,237 @@
|
||||
unit atsynedit_form_complete_synwrite;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, StrUtils, Graphics,
|
||||
Dialogs,
|
||||
ATSynEdit;
|
||||
|
||||
procedure DoEditorCompletionAcp(AEdit: TATSynEdit;
|
||||
const AFilenameAcp: string; ACaseSens, AIsPascal: boolean);
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
ATStringProc,
|
||||
ATSynEdit_form_complete;
|
||||
|
||||
type
|
||||
{ TAcp }
|
||||
|
||||
TAcp = class
|
||||
private
|
||||
ListAcpType: TStringlist;
|
||||
ListAcpText: TStringlist;
|
||||
ListAcpDesc: TStringlist;
|
||||
FWordChars: string;
|
||||
procedure DoLoadAcpFile(const fn: string; IsPascal: boolean);
|
||||
procedure DoOnGetCompleteProp(Sender: TObject;
|
||||
out AText, ASuffix: string;
|
||||
out ACharsLeft, ACharsRight: integer);
|
||||
public
|
||||
Ed: TATSynEdit;
|
||||
CaseSens: boolean;
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
end;
|
||||
|
||||
var
|
||||
Acp: TAcp = nil;
|
||||
|
||||
//parse control string from .acp file (starts with #)
|
||||
procedure SParseString_AcpControlLine(const s: string;
|
||||
var WordChars: string;
|
||||
var IsBracketSep: boolean);
|
||||
var
|
||||
n: Integer;
|
||||
begin
|
||||
if SBeginsWith(s, '#chars') then
|
||||
begin
|
||||
WordChars:= '';
|
||||
IsBracketSep:= true;
|
||||
n:= Pos(' ', s);
|
||||
if n>0 then
|
||||
begin
|
||||
WordChars:= Copy(s, n+1, MaxInt);
|
||||
IsBracketSep:= Pos('(', WordChars)=0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
//parse string from .acp file
|
||||
procedure SParseString_AcpStd(
|
||||
const S: string;
|
||||
IsBracketSep: boolean;
|
||||
out SType, SId, SPar, SHint: string);
|
||||
const
|
||||
cMaxHintLen = 300;
|
||||
var
|
||||
a, b, c: Integer;
|
||||
begin
|
||||
SType:= '';
|
||||
SId:= '';
|
||||
SPar:= '';
|
||||
SHint:= '';
|
||||
if Trim(s)='' then Exit;
|
||||
|
||||
a:= PosEx(' ', s, 1);
|
||||
b:= PosEx(' ', s, a+1);
|
||||
if b=0 then
|
||||
b:= Length(s)+1;
|
||||
|
||||
if IsBracketSep then
|
||||
begin
|
||||
c:= PosEx('(', s, a+1);
|
||||
if (c<b) and (c<>0) then
|
||||
b:= c;
|
||||
end;
|
||||
|
||||
c:= PosEx('|', s, b);
|
||||
if c=0 then
|
||||
c:= MaxInt div 2;
|
||||
|
||||
SType:= Copy(s, 1, a-1);
|
||||
SId:= Copy(s, a+1, b-a-1);
|
||||
SPar:= Copy(s, b, c-b);
|
||||
SHint:= Copy(s, c+1, cMaxHintLen);
|
||||
|
||||
SReplaceAllPercentChars(SId);
|
||||
SReplaceAllPercentChars(SPar);
|
||||
|
||||
SReplaceAll(SPar, ';', ','); //Pascal lexer has ";" param separator
|
||||
SReplaceAll(SPar, '[,', ',['); //for optional params
|
||||
end;
|
||||
|
||||
|
||||
procedure TAcp.DoLoadAcpFile(const fn: string; IsPascal: boolean);
|
||||
var
|
||||
List: TStringList;
|
||||
s, SType, SText, SPar, SHint: string;
|
||||
IsBracketSep: boolean;
|
||||
i: Integer;
|
||||
begin
|
||||
ListAcpType.Clear;
|
||||
ListAcpText.Clear;
|
||||
ListAcpDesc.Clear;
|
||||
|
||||
FWordChars:= '';
|
||||
IsBracketSep:= true;
|
||||
|
||||
List:= TStringList.Create;
|
||||
try
|
||||
List.LoadFromFile(fn);
|
||||
for i:= 0 to List.Count-1 do
|
||||
begin
|
||||
s:= List[i];
|
||||
if s='' then
|
||||
Continue;
|
||||
|
||||
if s[1]='#' then
|
||||
begin
|
||||
SParseString_AcpControlLine(s, FWordChars, IsBracketSep);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
SParseString_AcpStd(s, IsBracketSep, SType, SText, SPar, SHint);
|
||||
if SText<>'' then
|
||||
begin
|
||||
if IsPascal then
|
||||
begin
|
||||
SDeleteFrom(SText, ':');
|
||||
if Pos('):', SPar)>0 then
|
||||
begin
|
||||
SDeleteFrom(SPar, '):');
|
||||
SPar:= SPar+')';
|
||||
end;
|
||||
end;
|
||||
|
||||
ListAcpType.Add(SType);
|
||||
ListAcpText.Add(SText);
|
||||
ListAcpDesc.Add(SPar+cCompleteHintChar+SHint);
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
FreeAndNil(List);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TAcp.DoOnGetCompleteProp(Sender: TObject; out AText, ASuffix: string;
|
||||
out ACharsLeft, ACharsRight: integer);
|
||||
var
|
||||
s_word_w: atString;
|
||||
s_type, s_text, s_desc, s_word: string;
|
||||
n: integer;
|
||||
ok: boolean;
|
||||
begin
|
||||
AText:= '';
|
||||
ASuffix:= '';
|
||||
ACharsLeft:= 0;
|
||||
ACharsRight:= 0;
|
||||
EditorGetCurrentWord(Ed, FWordChars, s_word_w, ACharsLeft, ACharsRight);
|
||||
s_word:= Utf8Encode(s_word_w);
|
||||
|
||||
for n:= 0 to ListAcpText.Count-1 do
|
||||
begin
|
||||
s_type:= ListAcpType[n];
|
||||
s_text:= ListAcpText[n];
|
||||
s_desc:= ListAcpDesc[n];
|
||||
|
||||
if s_word<>'' then
|
||||
begin
|
||||
if CaseSens then
|
||||
ok:= SBeginsWith(s_text, s_word)
|
||||
else
|
||||
ok:= SBeginsWith(UpperCase(s_text), UpperCase(s_word));
|
||||
if not ok then Continue;
|
||||
end;
|
||||
|
||||
AText:= AText+s_type+'|'+s_text+'|'+s_desc+#13;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TAcp.Create;
|
||||
begin
|
||||
inherited;
|
||||
ListAcpType:= TStringlist.create;
|
||||
ListAcpText:= TStringlist.create;
|
||||
ListAcpDesc:= TStringlist.create;
|
||||
end;
|
||||
|
||||
destructor TAcp.Destroy;
|
||||
begin
|
||||
FreeAndNil(ListAcpType);
|
||||
FreeAndNil(ListAcpText);
|
||||
FreeAndNil(ListAcpDesc);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure DoEditorCompletionAcp(AEdit: TATSynEdit;
|
||||
const AFilenameAcp: string; ACaseSens, AIsPascal: boolean);
|
||||
begin
|
||||
if not FileExists(AFilenameAcp) then exit;
|
||||
Acp.DoLoadAcpFile(AFilenameAcp, AIsPascal);
|
||||
Acp.Ed:= AEdit;
|
||||
Acp.CaseSens:= ACaseSens;
|
||||
DoEditorCompletionListbox(AEdit, @Acp.DoOnGetCompleteProp);
|
||||
end;
|
||||
|
||||
initialization
|
||||
Acp:= TAcp.Create;
|
||||
|
||||
cCompleteFontStyles[0]:= [];
|
||||
cCompleteColorFont[0]:= clPurple;
|
||||
cCompleteColorFont[1]:= clBlack;
|
||||
cCompleteColorFont[2]:= clGray;
|
||||
cCompleteColorFont[3]:= clGreen;
|
||||
|
||||
finalization
|
||||
FreeAndNil(Acp);
|
||||
|
||||
end.
|
||||
|
||||
141
ATSynEdit/atsynedit/atsynedit_gutter.pas
Normal file
@@ -0,0 +1,141 @@
|
||||
unit ATSynEdit_Gutter;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils;
|
||||
|
||||
type
|
||||
TATGutterItem = class
|
||||
Visible: boolean;
|
||||
Size: integer;
|
||||
Left, Right: integer;
|
||||
end;
|
||||
|
||||
type
|
||||
TATGutter = class
|
||||
private
|
||||
FList: TList;
|
||||
function GetItem(N: Integer): TATGutterItem;
|
||||
public
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
function IsIndexValid(N: integer): boolean;
|
||||
procedure Add(ASize: integer);
|
||||
procedure Delete(N: integer);
|
||||
procedure Clear;
|
||||
function Count: integer;
|
||||
property Items[N: integer]: TATGutterItem read GetItem; default;
|
||||
function Width: integer;
|
||||
procedure Update;
|
||||
function IndexAt(AX: integer): integer;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
{ TATGutter }
|
||||
|
||||
function TATGutter.IsIndexValid(N: integer): boolean;
|
||||
begin
|
||||
Result:= (N>=0) and (N<FList.Count);
|
||||
end;
|
||||
|
||||
function TATGutter.GetItem(N: Integer): TATGutterItem;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
Result:= TATGutterItem(FList[N])
|
||||
else
|
||||
Result:= nil;
|
||||
end;
|
||||
|
||||
constructor TATGutter.Create;
|
||||
begin
|
||||
inherited;
|
||||
FList:= TList.Create;
|
||||
end;
|
||||
|
||||
destructor TATGutter.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
FreeAndNil(FList);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TATGutter.Add(ASize: integer);
|
||||
var
|
||||
Item: TATGutterItem;
|
||||
begin
|
||||
Item:= TATGutterItem.Create;
|
||||
Item.Size:= ASize;
|
||||
Item.Visible:= true;
|
||||
FList.Add(Item);
|
||||
Update;
|
||||
end;
|
||||
|
||||
procedure TATGutter.Delete(N: integer);
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
begin
|
||||
TObject(FList[N]).Free;
|
||||
FList.Delete(N);
|
||||
end;
|
||||
Update;
|
||||
end;
|
||||
|
||||
procedure TATGutter.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= Count-1 downto 0 do
|
||||
Delete(i);
|
||||
end;
|
||||
|
||||
function TATGutter.Count: integer;
|
||||
begin
|
||||
Result:= FList.Count;
|
||||
end;
|
||||
|
||||
function TATGutter.Width: integer;
|
||||
begin
|
||||
if Count>0 then
|
||||
Result:= Items[Count-1].Right
|
||||
else
|
||||
Result:= 0;
|
||||
end;
|
||||
|
||||
procedure TATGutter.Update;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= 0 to Count-1 do
|
||||
with Items[i] do
|
||||
begin
|
||||
if i>0 then
|
||||
Left:= Items[i-1].Right
|
||||
else
|
||||
Left:= 0;
|
||||
Right:= Left;
|
||||
if Visible then
|
||||
Inc(Right, Size);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATGutter.IndexAt(AX: integer): integer;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= -1;
|
||||
for i:= 0 to Count-1 do
|
||||
with Items[i] do
|
||||
if (AX>=Left) and (AX<Right) then
|
||||
begin
|
||||
Result:= i;
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
239
ATSynEdit/atsynedit/atsynedit_hilite.inc
Normal file
@@ -0,0 +1,239 @@
|
||||
{$ifdef nnn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.DoCalcLineHiliteEx(ALineIndex: integer;
|
||||
var AParts: TATLineParts;
|
||||
AColorBG: TColor; out AColorAfter: TColor): boolean;
|
||||
const
|
||||
cMaxCharsInLine = 50*1024;
|
||||
var
|
||||
WrapItem: TATSynWrapItem;
|
||||
Str: atString;
|
||||
begin
|
||||
Result:= Strings.IsIndexValid(ALineIndex);
|
||||
if not Result then exit;
|
||||
FillChar(AParts, SizeOf(AParts), 0);
|
||||
|
||||
Str:= Strings.Lines[ALineIndex];
|
||||
if Str='' then exit;
|
||||
|
||||
WrapItem:= TATSynWrapItem.Create(ALineIndex, 1, Length(Str), 0, cWrapItemFinal);
|
||||
try
|
||||
AColorAfter:= AColorBG;
|
||||
DoCalcLineHilite(WrapItem, AParts, 0, cMaxCharsInLine,
|
||||
AColorBG, false, AColorAfter);
|
||||
finally
|
||||
FreeAndNil(WrapItem);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCalcLineHilite(const AItem: TATSynWrapItem;
|
||||
var AParts: TATLineParts; ACharsSkipped, ACharsMax: integer;
|
||||
AColorBG: TColor; AColorForced: boolean; var AColorAfter: TColor);
|
||||
var
|
||||
nMaxOffset, nCharIndex, nLineIndex, nLineLen: integer;
|
||||
begin
|
||||
nMaxOffset:= Min(ACharsMax, AItem.NLength-ACharsSkipped);
|
||||
nLineIndex:= AItem.NLineIndex;
|
||||
nLineLen:= AItem.NLength;
|
||||
nCharIndex:= AItem.NCharIndex+ACharsSkipped;
|
||||
|
||||
FillChar(AParts, SizeOf(AParts), 0);
|
||||
if Assigned(FAdapterHilite) then
|
||||
FAdapterHilite.OnEditorCalcHilite(Self, AParts, nLineIndex, nCharIndex, nLineLen, AColorAfter);
|
||||
if Assigned(FOnCalcHilite) then
|
||||
FOnCalcHilite(Self, AParts, nLineIndex, nCharIndex, nLineLen, AColorAfter);
|
||||
|
||||
DoPartSetColorBG(AParts, AColorBG, AColorForced);
|
||||
if AColorForced then
|
||||
AColorAfter:= AColorBG;
|
||||
|
||||
//first add Attribs,
|
||||
//selection must be over attribs
|
||||
DoPartCalc_ApplyAttribsOver(AParts, nMaxOffset, nLineIndex, nCharIndex-1);
|
||||
|
||||
//Createnew makes parts for selection and fills empty AParts with these parts.
|
||||
//Applyover makes parts for selection and inserts these one-by-one over ready AParts
|
||||
//calculated before (in adapter or OnCalc event).
|
||||
//Maybe possible to always use Applyover but it's slower so i made Createnew for
|
||||
//faster render w/out adapter
|
||||
if AParts[0].Len>0 then
|
||||
begin
|
||||
DoPartCalc_ApplyOver(AParts, nMaxOffset, nLineIndex, nCharIndex-1);
|
||||
end
|
||||
else
|
||||
begin
|
||||
DoPartCalc_CreateNew(AParts, nMaxOffset, nLineIndex, nCharIndex-1, AColorBG);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoPartCalc_CreateNew(var AParts: TATLineParts;
|
||||
AOffsetMax, ALineIndex, ACharIndex: integer; AColorBG: TColor);
|
||||
var
|
||||
bSel, bSelPrev, bAdd: boolean;
|
||||
nIndex, i: integer;
|
||||
begin
|
||||
bSel:= false;
|
||||
bSelPrev:= false;
|
||||
nIndex:= -1;
|
||||
|
||||
for i:= 0 to AOffsetMax do
|
||||
begin
|
||||
bSel:= IsPosSelected(ACharIndex+i, ALineIndex);
|
||||
|
||||
if nIndex<0 then
|
||||
bAdd:= true
|
||||
else
|
||||
bAdd:= bSel<>bSelPrev;
|
||||
bSelPrev:= bSel;
|
||||
|
||||
if not bAdd then
|
||||
begin
|
||||
Inc(AParts[nIndex].Len);
|
||||
end
|
||||
else
|
||||
begin
|
||||
Inc(nIndex);
|
||||
if nIndex>=High(AParts) then Break;
|
||||
with AParts[nIndex] do
|
||||
begin
|
||||
Offset:= i;
|
||||
Len:= 1;
|
||||
if bSel then
|
||||
begin
|
||||
ColorFont:= FColors.TextSelFont;//random($ffff);
|
||||
ColorBG:= FColors.TextSelBG;
|
||||
end
|
||||
else
|
||||
begin
|
||||
ColorFont:= GetColorTextFont;//random($ffff);
|
||||
ColorBG:= AColorBG;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoPartCalc_ApplyOver(var AParts: TATLineParts; AOffsetMax,
|
||||
ALineIndex, ACharIndex: integer);
|
||||
var
|
||||
bSel, bSelPrev: boolean;
|
||||
Part: TATLinePart;
|
||||
i: integer;
|
||||
begin
|
||||
FillChar(Part{%H-}, SizeOf(Part), 0);
|
||||
Part.ColorFont:= Colors.TextSelFont;
|
||||
Part.ColorBG:= Colors.TextSelBG;
|
||||
|
||||
bSel:= false;
|
||||
bSelPrev:= false;
|
||||
|
||||
for i:= 0 to AOffsetMax do
|
||||
begin
|
||||
bSel:= IsPosSelected(ACharIndex+i, ALineIndex);
|
||||
|
||||
if bSel and (i=AOffsetMax) then
|
||||
begin
|
||||
DoPartInsert(AParts, Part, true);
|
||||
Break
|
||||
end;
|
||||
|
||||
if bSel and bSelPrev then
|
||||
Inc(Part.Len)
|
||||
else
|
||||
if not bSelPrev and bSel then
|
||||
begin
|
||||
Part.Offset:= i;
|
||||
Part.Len:= 1;
|
||||
end
|
||||
else
|
||||
if bSelPrev and not bSel then
|
||||
begin
|
||||
DoPartInsert(AParts, Part, true);
|
||||
end;
|
||||
bSelPrev:= bSel;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoCalcPosColor(AX, AY: integer; var AColor: TColor);
|
||||
begin
|
||||
if Assigned(FAdapterHilite) then
|
||||
FAdapterHilite.OnEditorCalcPosColor(Self, AX, AY, AColor);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoCalcLineEntireColor(ALine: integer; ACoordTop: integer;
|
||||
ALineWithCaret: boolean; out AColor: TColor; out AColorForced: boolean);
|
||||
var
|
||||
BmKind: integer;
|
||||
begin
|
||||
AColor:= clNone;
|
||||
|
||||
BmKind:= Strings.LinesBm[ALine];
|
||||
if BmKind<>0 then
|
||||
begin
|
||||
AColor:= Colors.BookmarkBG;
|
||||
if Assigned(FOnCalcBookmarkColor) then
|
||||
FOnCalcBookmarkColor(Self, BmKind, AColor);
|
||||
end;
|
||||
|
||||
if FOptShowCurLine then
|
||||
begin
|
||||
if FOptShowCurLineMinimal then
|
||||
begin
|
||||
if ALineWithCaret and IsLinePartWithCaret(ALine, ACoordTop) then
|
||||
AColor:= Colors.CurrentLineBG;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if ALineWithCaret then
|
||||
AColor:= Colors.CurrentLineBG;
|
||||
end;
|
||||
end;
|
||||
|
||||
if FMarkedRange.Count=2 then
|
||||
if (ALine>=FMarkedRange.Items[0].PosY) and
|
||||
(ALine<=FMarkedRange.Items[1].PosY) then
|
||||
AColor:= Colors.MarkedLinesBG;
|
||||
|
||||
AColorForced:= AColor<>clNone;
|
||||
if not AColorForced then
|
||||
AColor:= GetColorTextBG;
|
||||
end;
|
||||
|
||||
|
||||
procedure TATSynEdit.DoPartCalc_ApplyAttribsOver(var AParts: TATLineParts;
|
||||
AOffsetMax, ALineIndex, ACharIndex: integer);
|
||||
var
|
||||
i: integer;
|
||||
Attr: TATMarkerItem;
|
||||
Part: TATLinePart;
|
||||
PartObj: TATLinePartClass;
|
||||
begin
|
||||
for i:= 0 to Attribs.Count-1 do
|
||||
begin
|
||||
Attr:= Attribs[i];
|
||||
PartObj:= TATLinePartClass(Attr.Ptr);
|
||||
if Assigned(PartObj) then
|
||||
if Attr.PosY=ALineIndex then
|
||||
begin
|
||||
//empty parts? init part for whole line, for DoPartInsert to work
|
||||
if AParts[0].Len=0 then
|
||||
begin
|
||||
AParts[0].Offset:= 0;
|
||||
AParts[0].Len:= AOffsetMax;
|
||||
AParts[0].ColorBG:= Colors.TextBG;
|
||||
AParts[0].ColorFont:= Colors.TextFont;
|
||||
end;
|
||||
|
||||
Part:= PartObj.Data;
|
||||
Part.Len:= Attr.SelLen;
|
||||
Part.Offset:= Attr.PosX-ACharIndex;
|
||||
DoPartInsert(AParts, Part, false);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
290
ATSynEdit/atsynedit/atsynedit_keymap.pas
Normal file
@@ -0,0 +1,290 @@
|
||||
unit ATSynEdit_Keymap;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
//{$define test_correct_keynames}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Forms;
|
||||
|
||||
const
|
||||
cMaxKeyCombo = 3; //3 must be enougth for everybody..
|
||||
|
||||
type
|
||||
TATKeyArray = array[0..Pred(cMaxKeyCombo)] of TShortcut;
|
||||
|
||||
function KeyArrayToString(const K: TATKeyArray): string;
|
||||
function KeyArraysEqualNotEmpty(const a1, a2: TATKeyArray): boolean;
|
||||
function KeyArrayLength(const K: TATKeyArray): integer;
|
||||
|
||||
type
|
||||
{ TATKeymapItem }
|
||||
|
||||
TATKeymapItem = class
|
||||
public
|
||||
Command: integer;
|
||||
Name: string;
|
||||
Keys1,
|
||||
Keys2: TATKeyArray;
|
||||
end;
|
||||
|
||||
type
|
||||
{ TATKeymap }
|
||||
|
||||
TATKeymap = class
|
||||
private
|
||||
FList: TList;
|
||||
FHistory: TATKeyArray;
|
||||
function GetItem(N: integer): TATKeymapItem;
|
||||
procedure ClearHistory;
|
||||
procedure AddToHistory(sh: TShortcut);
|
||||
function IsMatchedKeys(const AKeys: TATKeyArray; AKey: TShortcut;
|
||||
AAllowOneKey: boolean): boolean;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
procedure Clear;
|
||||
function Count: integer;
|
||||
function IsIndexValid(N: integer): boolean;
|
||||
property Items[N: integer]: TATKeymapItem read GetItem; default;
|
||||
procedure Add(ACmd: integer; const AName: string; const AKeys1, AKeys2: array of string);
|
||||
procedure Delete(N: integer);
|
||||
function IndexOf(ACmd: integer): integer;
|
||||
function GetShortcutFromCommand(ACode: integer): TShortcut;
|
||||
function GetCommandFromShortcut(AKey: TShortcut): integer;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math,
|
||||
LCLProc,
|
||||
Dialogs;
|
||||
|
||||
function KeyArrayLength(const K: TATKeyArray): integer;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= 0;
|
||||
for i:= Low(K) to High(K) do
|
||||
if K[i]<>0 then
|
||||
Inc(Result);
|
||||
end;
|
||||
|
||||
{ TATKeymap }
|
||||
|
||||
constructor TATKeymap.Create;
|
||||
begin
|
||||
FList:= TList.Create;
|
||||
ClearHistory;
|
||||
end;
|
||||
|
||||
destructor TATKeymap.Destroy;
|
||||
begin
|
||||
ClearHistory;
|
||||
Clear;
|
||||
FreeAndNil(FList);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
function TATKeymap.GetItem(N: integer): TATKeymapItem;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
Result:= TATKeymapItem(FList[N])
|
||||
else
|
||||
Result:= nil;
|
||||
end;
|
||||
|
||||
procedure TATKeymap.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= FList.Count-1 downto 0 do
|
||||
TObject(FList[i]).Free;
|
||||
FList.Clear;
|
||||
end;
|
||||
|
||||
function TATKeymap.Count: integer;
|
||||
begin
|
||||
Result:= FList.Count;
|
||||
end;
|
||||
|
||||
function TATKeymap.IsIndexValid(N: integer): boolean;
|
||||
begin
|
||||
Result:= (N>=0) and (N<FList.Count);
|
||||
end;
|
||||
|
||||
function _TextToShortcut(const S: string): TShortcut;
|
||||
begin
|
||||
Result:= TextToShortCut(S);
|
||||
{$ifdef test_correct_keynames}
|
||||
if Result=0 then
|
||||
Showmessage('Incorrect key in keymap: "'+S+'"');
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
procedure TATKeymap.Add(ACmd: integer; const AName: string; const AKeys1,
|
||||
AKeys2: array of string);
|
||||
var
|
||||
Item: TATKeymapItem;
|
||||
i: integer;
|
||||
begin
|
||||
Item:= TATKeymapItem.Create;
|
||||
Item.Command:= ACmd;
|
||||
Item.Name:= AName;
|
||||
|
||||
FillChar(Item.Keys1, Sizeof(Item.Keys1), 0);
|
||||
FillChar(Item.Keys2, Sizeof(Item.Keys2), 0);
|
||||
|
||||
for i:= 0 to Min(High(AKeys1), High(Item.Keys1)) do Item.Keys1[i]:= _TextToShortcut(AKeys1[i]);
|
||||
for i:= 0 to Min(High(AKeys2), High(Item.Keys2)) do Item.Keys2[i]:= _TextToShortcut(AKeys2[i]);
|
||||
|
||||
FList.Add(Item);
|
||||
end;
|
||||
|
||||
procedure TATKeymap.Delete(N: integer);
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
FList.Delete(N);
|
||||
end;
|
||||
|
||||
function TATKeymap.IndexOf(ACmd: integer): integer;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= -1;
|
||||
for i:= 0 to Count-1 do
|
||||
if Items[i].Command=ACmd then
|
||||
begin Result:= i; Exit end;
|
||||
end;
|
||||
|
||||
function TATKeymap.GetShortcutFromCommand(ACode: integer): TShortcut;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= scNone;
|
||||
for i:= 0 to Count-1 do
|
||||
if Items[i].Command=ACode then
|
||||
begin
|
||||
Result:= Items[i].Keys1[0];
|
||||
Exit
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATKeymap.GetCommandFromShortcut(AKey: TShortcut): integer;
|
||||
var
|
||||
bCheckSingle: boolean;
|
||||
i: integer;
|
||||
begin
|
||||
Result:= 0;
|
||||
|
||||
//first check combos, then check single-keys
|
||||
for bCheckSingle:= false to true do
|
||||
for i:= 0 to Count-1 do
|
||||
if IsMatchedKeys(Items[i].Keys1, AKey, bCheckSingle) or
|
||||
IsMatchedKeys(Items[i].Keys2, AKey, bCheckSingle) then
|
||||
begin
|
||||
Result:= Items[i].Command;
|
||||
ClearHistory;
|
||||
Exit
|
||||
end;
|
||||
|
||||
if AKey>0 then
|
||||
AddToHistory(AKey);
|
||||
end;
|
||||
|
||||
function TATKeymap.IsMatchedKeys(const AKeys: TATKeyArray; AKey: TShortcut;
|
||||
AAllowOneKey: boolean): boolean;
|
||||
//function called first for all items with Allow=false (for combos)
|
||||
//if not found, called for all items with Allow=true (for single keys)
|
||||
var
|
||||
LenThis, LenStack, IndexStack, i: integer;
|
||||
begin
|
||||
Result:= false;
|
||||
|
||||
LenThis:= KeyArrayLength(AKeys);
|
||||
if LenThis=0 then Exit;
|
||||
|
||||
if LenThis=1 then
|
||||
begin
|
||||
Result:= AAllowOneKey and (AKeys[0]=AKey);
|
||||
Exit
|
||||
end;
|
||||
|
||||
//AKey is last in combo AKeys?
|
||||
if AKeys[LenThis-1]<>AKey then Exit;
|
||||
|
||||
//stack filled?
|
||||
LenStack:= KeyArrayLength(FHistory);
|
||||
if LenStack<LenThis-1 then
|
||||
begin
|
||||
//showmessage('no match: if lenstack');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
//first keys (except last) of combo lie in stack?
|
||||
for i:= LenThis-2 downto 0 do
|
||||
begin
|
||||
IndexStack:= LenStack-1-(LenThis-2-i);
|
||||
if (IndexStack>=Low(FHistory)) and (IndexStack<=High(FHistory)) then
|
||||
if AKeys[i]<>FHistory[IndexStack] then
|
||||
begin
|
||||
//showmessage('no match: check items');
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
Result:= true;
|
||||
end;
|
||||
|
||||
procedure TATKeymap.ClearHistory;
|
||||
begin
|
||||
FillChar(FHistory, Sizeof(FHistory), 0);
|
||||
end;
|
||||
|
||||
procedure TATKeymap.AddToHistory(sh: TShortcut);
|
||||
var
|
||||
len: integer;
|
||||
begin
|
||||
len:= KeyArrayLength(FHistory);
|
||||
if len>=Length(FHistory) then
|
||||
begin
|
||||
ClearHistory;
|
||||
len:= KeyArrayLength(FHistory);
|
||||
end;
|
||||
FHistory[len]:= sh;
|
||||
end;
|
||||
|
||||
|
||||
function KeyArrayToString(const K: TATKeyArray): string;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
result:= '';
|
||||
for i:= Low(K) to High(K) do
|
||||
if K[i]<>0 then
|
||||
begin
|
||||
if result<>'' then
|
||||
result:= result+' * ';
|
||||
result:= result+ShortcutToText(K[i]);
|
||||
end;
|
||||
end;
|
||||
|
||||
function KeyArraysEqualNotEmpty(const a1, a2: TATKeyArray): boolean;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= true;
|
||||
|
||||
if a1[0]=0 then Exit(false);
|
||||
if a2[0]=0 then Exit(false);
|
||||
|
||||
for i:= Low(a1) to High(a1) do
|
||||
if a1[i]<>a2[i] then Exit(false);
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
233
ATSynEdit/atsynedit/atsynedit_keymap_init.pas
Normal file
@@ -0,0 +1,233 @@
|
||||
unit ATSynEdit_Keymap_Init;
|
||||
|
||||
//{$define test_combo}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
ATSynEdit_Keymap,
|
||||
ATSynEdit_Commands;
|
||||
|
||||
procedure InitKeymapFull(var M: TATKeymap);
|
||||
procedure InitKeymapCombo(var M: TATKeymap);
|
||||
|
||||
var
|
||||
KeymapFull: TATKeymap = nil;
|
||||
KeymapCombo: TATKeymap = nil;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
SysUtils,
|
||||
LCLProc,
|
||||
Dialogs;
|
||||
|
||||
const
|
||||
//Mac: instead of Ctrl use Command-key
|
||||
cXControl = {$ifdef darwin} 'Meta' {$else} 'Ctrl' {$endif};
|
||||
|
||||
|
||||
procedure InitKeymapFull(var M: TATKeymap);
|
||||
begin
|
||||
M.Clear;
|
||||
|
||||
M.Add(cCommand_KeyLeft, 'caret char left', ['Left'], []);
|
||||
M.Add(cCommand_KeyLeft_Sel, 'caret char left + select', ['Shift+Left'], []);
|
||||
M.Add(cCommand_KeyRight, 'caret char right', ['Right'], []);
|
||||
M.Add(cCommand_KeyRight_Sel, 'caret char right + select', ['Shift+Right'], []);
|
||||
M.Add(cCommand_KeyUp, 'caret line up', ['Up'], []);
|
||||
M.Add(cCommand_KeyUp_Sel, 'caret line up + select', ['Shift+Up'], []);
|
||||
M.Add(cCommand_KeyDown, 'caret line down', ['Down'], []);
|
||||
M.Add(cCommand_KeyDown_Sel, 'caret line down + select', ['Shift+Down'], []);
|
||||
M.Add(cCommand_KeyHome, 'caret to line start', ['Home'], []);
|
||||
M.Add(cCommand_KeyHome_Sel, 'caret to line start + select', ['Shift+Home'], []);
|
||||
M.Add(cCommand_KeyEnd, 'caret to line end', ['End'], []);
|
||||
M.Add(cCommand_KeyEnd_Sel, 'caret to line end + select', ['Shift+End'], []);
|
||||
M.Add(cCommand_KeyPageUp, 'caret page up', ['PgUp'], []);
|
||||
M.Add(cCommand_KeyPageUp_Sel, 'caret page up + select', ['Shift+PgUp'], []);
|
||||
M.Add(cCommand_KeyPageDown, 'caret page down', ['PgDn'], []);
|
||||
M.Add(cCommand_KeyPageDown_Sel, 'caret page down + select', ['Shift+PgDn'], []);
|
||||
|
||||
M.Add(cCommand_ColSelectLeft, 'column select: left', ['Shift+Alt+Left'], []);
|
||||
M.Add(cCommand_ColSelectRight, 'column select: right', ['Shift+Alt+Right'], []);
|
||||
M.Add(cCommand_ColSelectUp, 'column select: up', ['Shift+Alt+Up'], []);
|
||||
M.Add(cCommand_ColSelectDown, 'column select: down', ['Shift+Alt+Down'], []);
|
||||
M.Add(cCommand_ColSelectPageUp, 'column select: page up', ['Shift+Alt+PgUp'], []);
|
||||
M.Add(cCommand_ColSelectPageDown, 'column select: page down', ['Shift+Alt+PgDn'], []);
|
||||
M.Add(cCommand_ColSelectToLineBegin, 'column select: to line begin', ['Shift+Alt+Home'], []);
|
||||
M.Add(cCommand_ColSelectToLineEnd, 'column select: to line end', ['Shift+Alt+End'], []);
|
||||
|
||||
M.Add(cCommand_KeyBackspace, 'delete char left (backspace)', ['Bksp'], []);
|
||||
M.Add(cCommand_KeyDelete, 'delete char right (delete)', ['Del'], []);
|
||||
M.Add(cCommand_KeyEnter, 'insert line-break (enter)', ['Enter'], []);
|
||||
M.Add(cCommand_KeyTab, 'tabulation key', [], []);
|
||||
M.Add(cCommand_TextInsertTabChar, 'insert tab char', [], []);
|
||||
|
||||
M.Add(cCommand_TextDeleteLine, 'delete line', [cXControl+'+Y'], []);
|
||||
M.Add(cCommand_TextDuplicateLine, 'duplicate line', [cXControl+'+D'], []);
|
||||
|
||||
M.Add(cCommand_GotoTextBegin, 'goto text begin', [cXControl+'+Home'], []);
|
||||
M.Add(cCommand_GotoTextBegin_Sel, 'goto text begin + select', [cXControl+'+Shift+Home'], []);
|
||||
M.Add(cCommand_GotoTextEnd, 'goto text end', [cXControl+'+End'], []);
|
||||
M.Add(cCommand_GotoTextEnd_Sel, 'goto text end + select', [cXControl+'+Shift+End'], []);
|
||||
M.Add(cCommand_GotoWordPrev, 'goto word left', [cXControl+'+Left'], []);
|
||||
M.Add(cCommand_GotoWordPrev_Sel, 'goto word left + select', [cXControl+'+Shift+Left'], []);
|
||||
M.Add(cCommand_GotoWordNext, 'goto word right', [cXControl+'+Right'], []);
|
||||
M.Add(cCommand_GotoWordNext_Sel, 'goto word right + select', [cXControl+'+Shift+Right'], []);
|
||||
|
||||
M.Add(cCommand_SelectAll, 'selection: select all', [cXControl+'+A'], []);
|
||||
M.Add(cCommand_TextDeleteSelection, 'selection: delete selected text', [], []);
|
||||
M.Add(cCommand_SelectInverted, 'selection: invert selection', [], []);
|
||||
M.Add(cCommand_SelectSplitToLines, 'selection: split selection into lines', [], []);
|
||||
M.Add(cCommand_SelectExtendByLine, 'selection: extend selection by line', [cXControl+'+L'], []);
|
||||
M.Add(cCommand_SelectWords, 'selection: select words at carets', [], []);
|
||||
M.Add(cCommand_SelectLines, 'selection: select lines at carets', [], []);
|
||||
M.Add(cCommand_SelectNone, 'selection: cancel selection', [], []);
|
||||
M.Add(cCommand_Cancel, 'selection: cancel carets, selection, drag-drop', ['Esc'], []);
|
||||
|
||||
M.Add(cCommand_ToggleOverwrite, 'toggle insert/overwrite mode', ['Ins'], []);
|
||||
M.Add(cCommand_ToggleReadOnly, 'toggle read-only mode', ['Ctrl+Shift+R'], []);
|
||||
M.Add(cCommand_ToggleWordWrap, 'toggle word-wrap mode', [cXControl+'+U'], []);
|
||||
|
||||
M.Add(cCommand_ToggleUnprinted, 'toggle unprinted chars: enable all', [], []);
|
||||
M.Add(cCommand_ToggleUnprintedSpaces, 'toggle unprinted chars: spaces/tabs', [], []);
|
||||
M.Add(cCommand_ToggleUnprintedEnds, 'toggle unprinted chars: ends', [], []);
|
||||
M.Add(cCommand_ToggleUnprintedEndDetails, 'toggle unprinted chars: end details', [], []);
|
||||
|
||||
M.Add(cCommand_ToggleLineNums, 'toggle show line numbers', [], []);
|
||||
M.Add(cCommand_ToggleFolding, 'toggle show folding bar', [], []);
|
||||
M.Add(cCommand_ToggleRuler, 'toggle show ruler', [], []);
|
||||
M.Add(cCommand_ToggleMinimap, 'toggle show minimap', [], []);
|
||||
|
||||
M.Add(cCommand_TextDeleteWordPrev, 'delete word left', [cXControl+'+Bksp'], []);
|
||||
M.Add(cCommand_TextDeleteWordNext, 'delete word right', [cXControl+'+Del'], []);
|
||||
M.Add(cCommand_TextDeleteToLineBegin, 'delete to line start', [], []);
|
||||
M.Add(cCommand_TextDeleteToLineEnd, 'delete to line end', [cXControl+'+K'], []);
|
||||
M.Add(cCommand_TextDeleteToTextEnd, 'delete to text end', [], []);
|
||||
|
||||
M.Add(cCommand_TextIndent, 'indent selection', [cXControl+'+I'], []);
|
||||
M.Add(cCommand_TextUnindent, 'unindent selection', ['Shift+Tab'], []);
|
||||
|
||||
M.Add(cCommand_Undo, 'perform undo', [cXControl+'+Z'], []);
|
||||
M.Add(cCommand_Redo, 'perform redo', [cXControl+'+Shift+Z'], []);
|
||||
|
||||
M.Add(cCommand_ClipboardCopy, 'clipboard: copy', [cXControl+'+C'], [cXControl+'+Ins']);
|
||||
M.Add(cCommand_ClipboardCopyAdd, 'clipboard: copy/append', [], []);
|
||||
M.Add(cCommand_ClipboardCut, 'clipboard: cut', [cXControl+'+X'], ['Shift+Del']);
|
||||
M.Add(cCommand_ClipboardPaste, 'clipboard: paste', [cXControl+'+V'], ['Shift+Ins']);
|
||||
M.Add(cCommand_ClipboardPaste_Select, 'clipboard: paste, select', [], []);
|
||||
M.Add(cCommand_ClipboardPaste_KeepCaret, 'clipboard: paste, keep caret', [], []);
|
||||
M.Add(cCommand_ClipboardPaste_Column, 'clipboard: paste, force column block', [], []);
|
||||
M.Add(cCommand_ClipboardPaste_ColumnKeepCaret, 'clipboard: paste, force column block, keep caret', [], []);
|
||||
|
||||
M.Add(cCommand_ScrollLineUp, 'scroll line up', [cXControl+'+Up'], []);
|
||||
M.Add(cCommand_ScrollLineDown, 'scroll line down', [cXControl+'+Down'], []);
|
||||
M.Add(cCommand_ScrollToCaretTop, 'scroll to caret, top', [], []);
|
||||
M.Add(cCommand_ScrollToCaretBottom, 'scroll to caret, bottom', [], []);
|
||||
M.Add(cCommand_ScrollToCaretLeft, 'scroll to caret, left', [], []);
|
||||
M.Add(cCommand_ScrollToCaretRight, 'scroll to caret, right', [], []);
|
||||
|
||||
M.Add(cCommand_MoveSelectionUp, 'move selected lines up', ['Alt+Up'], []);
|
||||
M.Add(cCommand_MoveSelectionDown, 'move selected lines down', ['Alt+Down'], []);
|
||||
M.Add(cCommand_TextInsertEmptyAbove, 'insert empty line above', [], []);
|
||||
M.Add(cCommand_TextInsertEmptyBelow, 'insert empty line below', [], []);
|
||||
|
||||
M.Add(cCommand_CaretsExtendUpLine, 'carets extend: up a line', [], []);
|
||||
M.Add(cCommand_CaretsExtendUpPage, 'carets extend: up a page', [], []);
|
||||
M.Add(cCommand_CaretsExtendUpToTop, 'carets extend: up to top', [], []);
|
||||
M.Add(cCommand_CaretsExtendDownLine, 'carets extend: down a line', [], []);
|
||||
M.Add(cCommand_CaretsExtendDownPage, 'carets extend: down a page', [], []);
|
||||
M.Add(cCommand_CaretsExtendDownToEnd, 'carets extend: down to end', [], []);
|
||||
|
||||
{$ifdef test_combo}
|
||||
M.Add(cCommand_ZoomIn, 'zoom in', ['Ctrl+B', 'Ctrl+P'], []);
|
||||
M.Add(cCommand_ZoomOut, 'zoom out', ['Ctrl+B', 'Ctrl+B', 'Ctrl+M'], []);
|
||||
{$else}
|
||||
M.Add(cCommand_ZoomIn, 'zoom in', [], []);
|
||||
M.Add(cCommand_ZoomOut, 'zoom out', [], []);
|
||||
{$endif}
|
||||
|
||||
M.Add(cCommand_TextCaseLower, 'convert case: lower case', [], []);
|
||||
M.Add(cCommand_TextCaseUpper, 'convert case: upper case', [], []);
|
||||
M.Add(cCommand_TextCaseTitle, 'convert case: title case', [], []);
|
||||
M.Add(cCommand_TextCaseInvert, 'convert case: invert case', [], []);
|
||||
M.Add(cCommand_TextCaseSentence, 'convert case: sentence case', [], []);
|
||||
|
||||
M.Add(cCommand_TextTrimSpacesLeft, 'trim spaces: left', [], []);
|
||||
M.Add(cCommand_TextTrimSpacesRight, 'trim spaces: right', [], []);
|
||||
M.Add(cCommand_TextTrimSpacesAll, 'trim spaces: all', [], []);
|
||||
|
||||
M.Add(cCommand_RepeatTextCommand, 'repeat last text command', [], []);
|
||||
|
||||
M.Add(cCommand_FoldAll, 'folding: fold all', [], []);
|
||||
M.Add(cCommand_UnfoldAll, 'folding: unfold all', [], []);
|
||||
M.Add(cCommand_FoldLevel2, 'folding: fold level 2', [], []);
|
||||
M.Add(cCommand_FoldLevel3, 'folding: fold level 3', [], []);
|
||||
M.Add(cCommand_FoldLevel4, 'folding: fold level 4', [], []);
|
||||
M.Add(cCommand_FoldLevel5, 'folding: fold level 5', [], []);
|
||||
M.Add(cCommand_FoldLevel6, 'folding: fold level 6', [], []);
|
||||
M.Add(cCommand_FoldLevel7, 'folding: fold level 7', [], []);
|
||||
M.Add(cCommand_FoldLevel8, 'folding: fold level 8', [], []);
|
||||
M.Add(cCommand_FoldLevel9, 'folding: fold level 9', [], []);
|
||||
end;
|
||||
|
||||
procedure InitKeymapCombo(var M: TATKeymap);
|
||||
begin
|
||||
M.Clear;
|
||||
|
||||
M.Add(cCommand_KeyLeft, 'caret char left', ['Left'], []);
|
||||
M.Add(cCommand_KeyLeft_Sel, 'caret char left + select', ['Shift+Left'], []);
|
||||
M.Add(cCommand_KeyRight, 'caret char right', ['Right'], []);
|
||||
M.Add(cCommand_KeyRight_Sel, 'caret char right + select', ['Shift+Right'], []);
|
||||
M.Add(cCommand_KeyHome, 'caret to line start', ['Home'], []);
|
||||
M.Add(cCommand_KeyHome_Sel, 'caret to line start + select', ['Shift+Home'], []);
|
||||
M.Add(cCommand_KeyEnd, 'caret to line end', ['End'], []);
|
||||
M.Add(cCommand_KeyEnd_Sel, 'caret to line end + select', ['Shift+End'], []);
|
||||
|
||||
M.Add(cCommand_KeyBackspace, 'delete char left (backspace)', ['Bksp'], []);
|
||||
M.Add(cCommand_KeyDelete, 'delete char right (delete)', ['Del'], []);
|
||||
M.Add(cCommand_KeyEnter, 'insert line-break (enter)', ['Enter'], []);
|
||||
M.Add(cCommand_KeyTab, 'tabulation key', [], []);
|
||||
|
||||
M.Add(cCommand_GotoWordPrev, 'goto word left', [cXControl+'+Left'], []);
|
||||
M.Add(cCommand_GotoWordPrev_Sel, 'goto word left + select', [cXControl+'+Shift+Left'], []);
|
||||
M.Add(cCommand_GotoWordNext, 'goto word right', [cXControl+'+Right'], []);
|
||||
M.Add(cCommand_GotoWordNext_Sel, 'goto word right + select', [cXControl+'+Shift+Right'], []);
|
||||
|
||||
M.Add(cCommand_SelectAll, 'selection: select all', [cXControl+'+A'], []);
|
||||
M.Add(cCommand_TextDeleteSelection, 'selection: delete selected text', [], []);
|
||||
|
||||
M.Add(cCommand_ToggleOverwrite, 'toggle insert/overwrite mode', ['Ins'], []);
|
||||
|
||||
M.Add(cCommand_TextDeleteWordPrev, 'delete word left', [cXControl+'+Bksp'], []);
|
||||
M.Add(cCommand_TextDeleteWordNext, 'delete word right', [cXControl+'+Del'], []);
|
||||
|
||||
M.Add(cCommand_Undo, 'perform undo', [cXControl+'+Z'], []);
|
||||
M.Add(cCommand_Redo, 'perform redo', [cXControl+'+Shift+Z'], []);
|
||||
|
||||
M.Add(cCommand_ClipboardCopy, 'clipboard: copy', [cXControl+'+C'], [cXControl+'+Ins']);
|
||||
M.Add(cCommand_ClipboardCopyAdd, 'clipboard: copy/append', [], []);
|
||||
M.Add(cCommand_ClipboardCut, 'clipboard: cut', [cXControl+'+X'], ['Shift+Del']);
|
||||
M.Add(cCommand_ClipboardPaste, 'clipboard: paste', [cXControl+'+V'], ['Shift+Ins']);
|
||||
|
||||
M.Add(cCommand_ComboboxRecentsMenu, 'combobox: recent items menu', ['Alt+Down'], [cXControl+'+Down']);
|
||||
|
||||
M.Add(cCommand_KeyUp, 'blocked: caret line up', ['Up'], []);
|
||||
M.Add(cCommand_KeyDown, 'blocked: caret line down', ['Down'], []);
|
||||
M.Add(cCommand_KeyPageUp, 'blocked: caret page up', ['PgUp'], []);
|
||||
M.Add(cCommand_KeyPageDown, 'blocked: caret page down', ['PgDn'], []);
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
KeymapFull:= TATKeymap.Create;
|
||||
KeymapCombo:= TATKeymap.Create;
|
||||
InitKeymapFull(KeymapFull);
|
||||
InitKeymapCombo(KeymapCombo);
|
||||
|
||||
finalization
|
||||
FreeAndNil(KeymapFull);
|
||||
FreeAndNil(KeymapCombo);
|
||||
|
||||
end.
|
||||
|
||||
120
ATSynEdit/atsynedit/atsynedit_markers.pas
Normal file
@@ -0,0 +1,120 @@
|
||||
unit ATSynEdit_Markers;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils;
|
||||
|
||||
type
|
||||
TATMarkerItem = class
|
||||
public
|
||||
PosX, PosY: integer;
|
||||
CoordX, CoordY: integer; //screen coords
|
||||
Tag: integer;
|
||||
//used in CudaText: when "collect marker" runs, for all markers
|
||||
//with the same Tag>0 multi-carets placed
|
||||
SelLen: integer;
|
||||
//used in CudaText: when "collect marker" runs, caret will
|
||||
//be with selection of this len
|
||||
Ptr: TObject;
|
||||
//used in Attribs object of ATSynedit
|
||||
end;
|
||||
|
||||
type
|
||||
{ TATMarkers }
|
||||
|
||||
TATMarkers = class
|
||||
private
|
||||
FList: TList;
|
||||
function GetItem(N: integer): TATMarkerItem;
|
||||
public
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
procedure Clear;
|
||||
procedure Delete(N: integer);
|
||||
function Count: integer;
|
||||
function IsIndexValid(N: integer): boolean;
|
||||
property Items[N: integer]: TATMarkerItem read GetItem; default;
|
||||
procedure Add(APosX, APosY: integer;
|
||||
ATag: integer=0; ASelLen: integer=0; APtr: TObject=nil);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TATMarkers }
|
||||
|
||||
constructor TATMarkers.Create;
|
||||
begin
|
||||
inherited;
|
||||
FList:= TList.Create;
|
||||
end;
|
||||
|
||||
destructor TATMarkers.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
FreeAndNil(FList);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TATMarkers.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= FList.Count-1 downto 0 do
|
||||
Delete(i);
|
||||
end;
|
||||
|
||||
procedure TATMarkers.Delete(N: integer);
|
||||
var
|
||||
Mark: TATMarkerItem;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
begin
|
||||
Mark:= TATMarkerItem(FList[N]);
|
||||
if Assigned(Mark.Ptr) then
|
||||
Mark.Ptr.Free;
|
||||
Mark.Free;
|
||||
|
||||
FList.Delete(N);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATMarkers.Count: integer;
|
||||
begin
|
||||
Result:= FList.Count;
|
||||
end;
|
||||
|
||||
function TATMarkers.IsIndexValid(N: integer): boolean;
|
||||
begin
|
||||
Result:= (N>=0) and (N<FList.Count);
|
||||
end;
|
||||
|
||||
function TATMarkers.GetItem(N: integer): TATMarkerItem;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
Result:= TATMarkerItem(FList[N])
|
||||
else
|
||||
Result:= nil;
|
||||
end;
|
||||
|
||||
procedure TATMarkers.Add(APosX, APosY: integer; ATag: integer;
|
||||
ASelLen: integer; APtr: TObject);
|
||||
var
|
||||
Item: TATMarkerItem;
|
||||
begin
|
||||
Item:= TATMarkerItem.Create;
|
||||
Item.PosX:= APosX;
|
||||
Item.PosY:= APosY;
|
||||
Item.CoordX:= -1;
|
||||
Item.CoordY:= -1;
|
||||
Item.Tag:= ATag;
|
||||
Item.SelLen:= ASelLen;
|
||||
Item.Ptr:= APtr;
|
||||
|
||||
FList.Add(Item);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
116
ATSynEdit/atsynedit/atsynedit_package.lpk
Normal file
@@ -0,0 +1,116 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CONFIG>
|
||||
<Package Version="4">
|
||||
<PathDelim Value="\"/>
|
||||
<Name Value="atsynedit_package"/>
|
||||
<Type Value="RunAndDesignTime"/>
|
||||
<Author Value="Alexey Torgashin"/>
|
||||
<CompilerOptions>
|
||||
<Version Value="11"/>
|
||||
<PathDelim Value="\"/>
|
||||
<SearchPaths>
|
||||
<OtherUnitFiles Value=".;..\proc_lexer"/>
|
||||
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
</CompilerOptions>
|
||||
<Description Value="ATSynEdit"/>
|
||||
<License Value="MPL 2.0"/>
|
||||
<Version Major="1"/>
|
||||
<Files Count="19">
|
||||
<Item1>
|
||||
<Filename Value="atsynedit_register.pas"/>
|
||||
<HasRegisterProc Value="True"/>
|
||||
<UnitName Value="ATSynEdit_Register"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<Filename Value="atstringproc.pas"/>
|
||||
<UnitName Value="ATStringProc"/>
|
||||
</Item2>
|
||||
<Item3>
|
||||
<Filename Value="atstringproc_textbuffer.pas"/>
|
||||
<UnitName Value="ATStringProc_TextBuffer"/>
|
||||
</Item3>
|
||||
<Item4>
|
||||
<Filename Value="atstringproc_utf8detect.pas"/>
|
||||
<UnitName Value="atstringproc_utf8detect"/>
|
||||
</Item4>
|
||||
<Item5>
|
||||
<Filename Value="atstringproc_wordjump.pas"/>
|
||||
<UnitName Value="ATStringProc_WordJump"/>
|
||||
</Item5>
|
||||
<Item6>
|
||||
<Filename Value="atstrings.pas"/>
|
||||
<UnitName Value="ATStrings"/>
|
||||
</Item6>
|
||||
<Item7>
|
||||
<Filename Value="atstrings_undo.pas"/>
|
||||
<UnitName Value="ATStrings_Undo"/>
|
||||
</Item7>
|
||||
<Item8>
|
||||
<Filename Value="atsynedit.pas"/>
|
||||
<UnitName Value="ATSynEdit"/>
|
||||
</Item8>
|
||||
<Item9>
|
||||
<Filename Value="atsynedit_adapters.pas"/>
|
||||
<UnitName Value="ATSynEdit_Adapters"/>
|
||||
</Item9>
|
||||
<Item10>
|
||||
<Filename Value="atsynedit_canvasproc.pas"/>
|
||||
<UnitName Value="ATSynEdit_CanvasProc"/>
|
||||
</Item10>
|
||||
<Item11>
|
||||
<Filename Value="atsynedit_carets.pas"/>
|
||||
<UnitName Value="atsynedit_carets"/>
|
||||
</Item11>
|
||||
<Item12>
|
||||
<Filename Value="atsynedit_commands.pas"/>
|
||||
<UnitName Value="ATSynEdit_Commands"/>
|
||||
</Item12>
|
||||
<Item13>
|
||||
<Filename Value="atsynedit_edits.pas"/>
|
||||
<UnitName Value="ATSynEdit_Edits"/>
|
||||
</Item13>
|
||||
<Item14>
|
||||
<Filename Value="atsynedit_gutter.pas"/>
|
||||
<UnitName Value="ATSynEdit_Gutter"/>
|
||||
</Item14>
|
||||
<Item15>
|
||||
<Filename Value="atsynedit_keymap.pas"/>
|
||||
<UnitName Value="ATSynEdit_Keymap"/>
|
||||
</Item15>
|
||||
<Item16>
|
||||
<Filename Value="atsynedit_keymap_init.pas"/>
|
||||
<UnitName Value="ATSynEdit_Keymap_Init"/>
|
||||
</Item16>
|
||||
<Item17>
|
||||
<Filename Value="atsynedit_package.pas"/>
|
||||
<UnitName Value="atsynedit_package"/>
|
||||
</Item17>
|
||||
<Item18>
|
||||
<Filename Value="atsynedit_ranges.pas"/>
|
||||
<UnitName Value="ATSynEdit_Ranges"/>
|
||||
</Item18>
|
||||
<Item19>
|
||||
<Filename Value="atsynedit_wrapinfo.pas"/>
|
||||
<UnitName Value="ATSynEdit_WrapInfo"/>
|
||||
</Item19>
|
||||
</Files>
|
||||
<RequiredPkgs Count="2">
|
||||
<Item1>
|
||||
<PackageName Value="LCL"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<PackageName Value="FCL"/>
|
||||
</Item2>
|
||||
</RequiredPkgs>
|
||||
<UsageOptions>
|
||||
<UnitPath Value="$(PkgOutDir)"/>
|
||||
</UsageOptions>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
</PublishOptions>
|
||||
<CustomOptions Items="ExternHelp" Version="2">
|
||||
<_ExternHelp Items="Count"/>
|
||||
</CustomOptions>
|
||||
</Package>
|
||||
</CONFIG>
|
||||
26
ATSynEdit/atsynedit/atsynedit_package.pas
Normal file
@@ -0,0 +1,26 @@
|
||||
{ This file was automatically created by Lazarus. Do not edit!
|
||||
This source is only used to compile and install the package.
|
||||
}
|
||||
|
||||
unit atsynedit_package;
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
ATSynEdit_Register, ATStringProc, ATStringProc_TextBuffer,
|
||||
atstringproc_utf8detect, ATStringProc_WordJump, ATStrings, ATStrings_Undo,
|
||||
ATSynEdit, ATSynEdit_Adapters, ATSynEdit_CanvasProc, atsynedit_carets,
|
||||
ATSynEdit_Commands, ATSynEdit_Edits, ATSynEdit_Gutter, ATSynEdit_Keymap,
|
||||
ATSynEdit_Keymap_Init, ATSynEdit_Ranges, ATSynEdit_WrapInfo,
|
||||
LazarusPackageIntf;
|
||||
|
||||
implementation
|
||||
|
||||
procedure Register;
|
||||
begin
|
||||
RegisterUnit('ATSynEdit_Register', @ATSynEdit_Register.Register);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterPackage('atsynedit_package', @Register);
|
||||
end.
|
||||
68
ATSynEdit/atsynedit/atsynedit_proc.inc
Normal file
@@ -0,0 +1,68 @@
|
||||
//for browser-scroll feature
|
||||
var
|
||||
cBitmapNiceScroll: TBitmap = nil;
|
||||
const
|
||||
cBitmapNiceScrollRadius = 16;
|
||||
crNiceScrollNone = TCursor(-30);
|
||||
crNiceScrollUp = TCursor(-31);
|
||||
crNiceScrollDown = TCursor(-32);
|
||||
crNiceScrollLeft = TCursor(-33);
|
||||
crNiceScrollRight = TCursor(-34);
|
||||
|
||||
const
|
||||
//under Mac don't use Ctrl key, use Meta key as default (e.g. Meta+C, Meta+A)
|
||||
ssXControl = {$ifndef darwin} ssCtrl {$else} ssMeta {$endif};
|
||||
|
||||
procedure AppProcessMessages;
|
||||
begin
|
||||
//why we need it?
|
||||
//1) ScrollTop:=N applies for drawed control (it needs wrapinfo),
|
||||
//and it needs paint called. paint called only passive, QT+Mac needs it.
|
||||
//so need to call processmessages to wait for paint..
|
||||
//2) for showing "wait" on loading huge file
|
||||
|
||||
{$ifdef allow_proc_msg}
|
||||
Application.ProcessMessages;
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
procedure DoClearScrollInfo(var Info: TATSynScrollInfo);
|
||||
begin
|
||||
Info.NPos:= 0;
|
||||
Info.NMin:= 0;
|
||||
Info.NMax:= 1;
|
||||
Info.NPage:= 1;
|
||||
end;
|
||||
|
||||
function IsEqualScrollInfo(const Info1, Info2: TATSynScrollInfo): boolean;
|
||||
begin
|
||||
Result:=
|
||||
(Info1.NPos=Info2.NPos) and
|
||||
(Info1.NMin=Info2.NMin) and
|
||||
(Info1.NMax=Info2.NMax) and
|
||||
(Info1.NPage=Info2.NPage);
|
||||
end;
|
||||
|
||||
procedure InitClipboardFormat;
|
||||
begin
|
||||
cATClipboardFormatId:= RegisterClipboardFormat('Application/X-Laz-ATSynEdit-Block');
|
||||
end;
|
||||
|
||||
procedure InitResourcesNicescroll;
|
||||
begin
|
||||
cBitmapNiceScroll:= TBitmap.Create;
|
||||
cBitmapNiceScroll.LoadFromResourceName(HInstance, 'AB_MOVE');
|
||||
cBitmapNiceScroll.Transparent:= true;
|
||||
|
||||
Screen.Cursors[crNiceScrollNone]:= LoadCursor(HInstance, 'AB_MOVE');
|
||||
Screen.Cursors[crNiceScrollUp]:= LoadCursor(HInstance, 'AB_MOVE_U');
|
||||
Screen.Cursors[crNiceScrollDown]:= LoadCursor(HInstance, 'AB_MOVE_D');
|
||||
Screen.Cursors[crNiceScrollLeft]:= LoadCursor(HInstance, 'AB_MOVE_L');
|
||||
Screen.Cursors[crNiceScrollRight]:= LoadCursor(HInstance, 'AB_MOVE_R');
|
||||
end;
|
||||
|
||||
procedure FreeResources;
|
||||
begin
|
||||
FreeAndNil(cBitmapNiceScroll);
|
||||
end;
|
||||
|
||||
302
ATSynEdit/atsynedit/atsynedit_ranges.pas
Normal file
@@ -0,0 +1,302 @@
|
||||
unit ATSynEdit_Ranges;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
//{$define show_unfold_rng}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, Dialogs,
|
||||
ATStringProc;
|
||||
|
||||
type
|
||||
{ TATSynRange }
|
||||
|
||||
TATSynRange = class
|
||||
private
|
||||
FX, //start column
|
||||
FY, //start line
|
||||
FY2: integer; //end line which is fully folded (can't partially fold)
|
||||
FFolded: boolean;
|
||||
FStaple: boolean;
|
||||
FHint: string;
|
||||
public
|
||||
property X: integer read FX;
|
||||
property Y: integer read FY;
|
||||
property Y2: integer read FY2;
|
||||
property Folded: boolean read FFolded write FFolded;
|
||||
property Staple: boolean read FStaple;
|
||||
property Hint: string read FHint write FHint;
|
||||
constructor Create(AX, AY, AY2: integer; AStaple: boolean; const AHint: string); virtual;
|
||||
function IsSimple: boolean;
|
||||
function IsLineInside(ALine: integer): boolean;
|
||||
function MessageText: string;
|
||||
end;
|
||||
|
||||
type
|
||||
TATRangeHasLines = (
|
||||
cRngIgnore,
|
||||
cRngHasAllLines,
|
||||
cRngHasAnyOfLines,
|
||||
cRngExceptThisRange
|
||||
);
|
||||
|
||||
type
|
||||
{ TATSynRanges }
|
||||
|
||||
TATSynRanges = class
|
||||
private
|
||||
FList: TList;
|
||||
function GetCount: integer;
|
||||
function GetItems(Index: integer): TATSynRange;
|
||||
function MessageTextForIndexList(L: TList): string;
|
||||
public
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
property Count: integer read GetCount;
|
||||
function IsIndexValid(N: integer): boolean;
|
||||
procedure Clear;
|
||||
procedure Add(AX, AY, AY2: integer; AWithStaple: boolean; const AHint: string);
|
||||
procedure Insert(Index: integer; AX, AY, AY2: integer; AWithStaple: boolean;
|
||||
const AHint: string);
|
||||
procedure Delete(Index: integer);
|
||||
property Items[Index: integer]: TATSynRange read GetItems; default;
|
||||
function IsRangeInsideOther(R1, R2: TATSynRange): boolean;
|
||||
function IsRangesSame(R1, R2: TATSynRange): boolean;
|
||||
function FindRangesContainingLines(ALineFrom, ALineTo: integer;
|
||||
AInRange: TATSynRange; AOnlyFolded, ATopLevelOnly: boolean;
|
||||
ALineMode: TATRangeHasLines): TATIntArray;
|
||||
function FindRangeWithPlusAtLine(ALine: integer): TATSynRange;
|
||||
function MessageText(Cnt: integer): string;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math,
|
||||
ATSynEdit_Carets;
|
||||
|
||||
//we allow one block to hangout 1 line by Y2 from outer block:
|
||||
//it's needed for Pascal econtrol lexer
|
||||
//(don't know why it gives such blocks)
|
||||
const
|
||||
cAllowHangoutLines = 1; //0 or 1, do not bigger
|
||||
|
||||
|
||||
{ TATSynRange }
|
||||
|
||||
constructor TATSynRange.Create(AX, AY, AY2: integer; AStaple: boolean;
|
||||
const AHint: string);
|
||||
begin
|
||||
if (AX<=0) then raise Exception.Create('Incorrect range with x<=0: '+MessageText);
|
||||
if (AY<0) then raise Exception.Create('Incorrect range with y<0: '+MessageText);
|
||||
if (AY>AY2) then raise Exception.Create('Incorrect range with y>y2: '+MessageText);
|
||||
|
||||
FX:= AX;
|
||||
FY:= AY;
|
||||
FY2:= AY2;
|
||||
FStaple:= AStaple;
|
||||
FHint:= AHint;
|
||||
end;
|
||||
|
||||
function TATSynRange.IsSimple: boolean;
|
||||
begin
|
||||
Result:= Y=Y2;
|
||||
end;
|
||||
|
||||
function TATSynRange.IsLineInside(ALine: integer): boolean;
|
||||
begin
|
||||
Result:= (ALine>=Y) and (ALine<=Y2);
|
||||
end;
|
||||
|
||||
function TATSynRange.MessageText: string;
|
||||
begin
|
||||
Result:= Format('%d..%d', [Y+1, Y2+1]);
|
||||
end;
|
||||
|
||||
{ TATSynRanges }
|
||||
|
||||
function TATSynRanges.IsIndexValid(N: integer): boolean;
|
||||
begin
|
||||
Result:= (N>=0) and (N<FList.Count);
|
||||
end;
|
||||
|
||||
function TATSynRanges.GetCount: integer;
|
||||
begin
|
||||
Result:= FList.Count;
|
||||
end;
|
||||
|
||||
function TATSynRanges.GetItems(Index: integer): TATSynRange;
|
||||
begin
|
||||
Result:= TATSynRange(FList[Index]);
|
||||
{
|
||||
if IsIndexValid(Index) then
|
||||
Result:= TATSynRange(FList[Index])
|
||||
else
|
||||
Result:= nil;
|
||||
}
|
||||
end;
|
||||
|
||||
constructor TATSynRanges.Create;
|
||||
begin
|
||||
FList:= TList.Create;
|
||||
FList.Capacity:= 4000;
|
||||
end;
|
||||
|
||||
destructor TATSynRanges.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
FreeAndNil(FList);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TATSynRanges.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= Count-1 downto 0 do
|
||||
TObject(FList[i]).Free;
|
||||
FList.Clear;
|
||||
end;
|
||||
|
||||
procedure TATSynRanges.Add(AX, AY, AY2: integer; AWithStaple: boolean; const AHint: string);
|
||||
begin
|
||||
FList.Add(TATSynRange.Create(AX, AY, AY2, AWithStaple, AHint));
|
||||
end;
|
||||
|
||||
procedure TATSynRanges.Insert(Index: integer; AX, AY, AY2: integer; AWithStaple: boolean; const AHint: string);
|
||||
begin
|
||||
FList.Insert(Index, TATSynRange.Create(AX, AY, AY2, AWithStaple, AHint));
|
||||
end;
|
||||
|
||||
procedure TATSynRanges.Delete(Index: integer);
|
||||
begin
|
||||
if IsIndexValid(Index) then
|
||||
begin
|
||||
TObject(FList[Index]).Free;
|
||||
FList.Delete(Index);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynRanges.IsRangeInsideOther(R1, R2: TATSynRange): boolean;
|
||||
begin
|
||||
Result:=
|
||||
IsPosSorted(R2.X, R2.Y, R1.X, R1.Y, true)
|
||||
and (R1.Y2-cAllowHangoutLines<=R2.Y2);
|
||||
end;
|
||||
|
||||
function TATSynRanges.IsRangesSame(R1, R2: TATSynRange): boolean;
|
||||
begin
|
||||
if R1=R2 then
|
||||
begin Result:= true; Exit end;
|
||||
if (R1.X=R2.X) and (R1.Y=R2.Y) and (Abs(R1.Y2-R2.Y2)<=cAllowHangoutLines) then
|
||||
begin Result:= true; Exit end;
|
||||
|
||||
Result:= false;
|
||||
end;
|
||||
|
||||
function TATSynRanges.FindRangesContainingLines(ALineFrom, ALineTo: integer;
|
||||
AInRange: TATSynRange; AOnlyFolded, ATopLevelOnly: boolean; ALineMode: TATRangeHasLines): TATIntArray;
|
||||
var
|
||||
L: TList;
|
||||
R: TATSynRange;
|
||||
i, j: integer;
|
||||
Ok: boolean;
|
||||
begin
|
||||
SetLength(Result, 0);
|
||||
L:= TList.Create;
|
||||
L.Capacity:= 512;
|
||||
try
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
R:= Items[i];
|
||||
if (not R.IsSimple) then
|
||||
if (not AOnlyFolded or R.Folded) then
|
||||
begin
|
||||
case ALineMode of
|
||||
cRngIgnore: Ok:= true;
|
||||
cRngHasAllLines: Ok:= (R.Y<=ALineFrom) and (R.Y2>=ALineTo);
|
||||
cRngHasAnyOfLines: Ok:= (R.Y<=ALineTo) and (R.Y2>=ALineFrom);
|
||||
cRngExceptThisRange: Ok:= R<>AInRange;
|
||||
else raise Exception.Create('unknown LineMode');
|
||||
end;
|
||||
if not Ok then Continue;
|
||||
|
||||
if AInRange=nil then
|
||||
Ok:= true
|
||||
else
|
||||
Ok:= not IsRangesSame(AInRange, R) and IsRangeInsideOther(R, AInRange);
|
||||
|
||||
if Ok then
|
||||
L.Add(pointer(i));
|
||||
end;
|
||||
end;
|
||||
|
||||
if ATopLevelOnly then
|
||||
begin
|
||||
{$ifdef show_unfold_rng}
|
||||
s1:= 'toplevel: ranges shortlist'#13+MessageTextForIndexList(L);
|
||||
{$endif}
|
||||
|
||||
for i:= L.Count-1 downto 1 do
|
||||
for j:= 0 to i-1 do
|
||||
if IsRangeInsideOther(Items[integer(L[i])], Items[integer(L[j])]) then
|
||||
begin
|
||||
L.Delete(i);
|
||||
Break
|
||||
end;
|
||||
|
||||
{$ifdef show_unfold_rng}
|
||||
s2:= 'toplevel: ranges done'#13+MessageTextForIndexList(L);
|
||||
if l.count>0 then
|
||||
showmessage(s1+#13+s2);
|
||||
{$endif}
|
||||
end;
|
||||
|
||||
SetLength(Result, L.Count);
|
||||
for i:= 0 to L.Count-1 do
|
||||
Result[i]:= integer(L[i]);
|
||||
finally
|
||||
FreeAndNil(L);
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynRanges.FindRangeWithPlusAtLine(ALine: integer): TATSynRange;
|
||||
var
|
||||
i: integer;
|
||||
R: TATSynRange;
|
||||
begin
|
||||
Result:= nil;
|
||||
for i:= 0 to Count-1 do
|
||||
begin
|
||||
R:= Items[i];
|
||||
if (not R.IsSimple) and (R.Y=ALine) then
|
||||
begin
|
||||
Result:= R;
|
||||
Break
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TATSynRanges.MessageText(Cnt: integer): string;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= '';
|
||||
for i:= 0 to Min(Count-1, Cnt) do
|
||||
Result:= Result+Items[i].MessageText+#13;
|
||||
end;
|
||||
|
||||
function TATSynRanges.MessageTextForIndexList(L: TList): string;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
Result:= '';
|
||||
if L.Count=0 then exit;
|
||||
for i:= 0 to L.Count-1 do
|
||||
Result:= Result+items[integer(L[i])].MessageText+#13;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
28
ATSynEdit/atsynedit/atsynedit_register.pas
Normal file
@@ -0,0 +1,28 @@
|
||||
unit ATSynEdit_Register;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, ATSynEdit, ATSynEdit_Edits,
|
||||
LResources;
|
||||
|
||||
procedure Register;
|
||||
|
||||
implementation
|
||||
|
||||
{ Registration }
|
||||
procedure Register;
|
||||
begin
|
||||
RegisterComponents('Misc', [TATSynEdit, TATEdit, TATComboEdit]);
|
||||
end;
|
||||
|
||||
initialization
|
||||
|
||||
//lrs file must be made by command:
|
||||
// ~/lazarus/tools/lazres icons.lrs *.png
|
||||
{$I res/ide/icons.lrs}
|
||||
|
||||
end.
|
||||
|
||||
360
ATSynEdit/atsynedit/atsynedit_sel.inc
Normal file
@@ -0,0 +1,360 @@
|
||||
{$ifdef nnn}begin end;{$endif}
|
||||
|
||||
function TATSynEdit.IsPosSelected(AX, AY: integer): boolean;
|
||||
var
|
||||
NPosLeft,
|
||||
NPosRight: integer;
|
||||
SLine: atString;
|
||||
begin
|
||||
if not IsSelRectEmpty then
|
||||
begin
|
||||
if not ((AY>=FSelRect.Top) and (AY<=FSelRect.Bottom)) then exit(False);
|
||||
|
||||
SLine:= Strings.Lines[AY];
|
||||
NPosLeft:= SColumnPosToCharPos(SLine, FSelRect.Left, OptTabSize);
|
||||
NPosRight:= SColumnPosToCharPos(SLine, FSelRect.Right, OptTabSize);
|
||||
Result:= (AX>=NPosLeft) and (AX<NPosRight);
|
||||
end
|
||||
else
|
||||
Result:= Carets.IsPosSelected(AX, AY);
|
||||
end;
|
||||
|
||||
function TATSynEdit.IsSelRectEmpty: boolean;
|
||||
begin
|
||||
Result:= EqualRect(FSelRect, cRectEmpty);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_Word(P: TPoint);
|
||||
var
|
||||
N1, N2: integer;
|
||||
begin
|
||||
if not Strings.IsIndexValid(P.Y) then Exit;
|
||||
SFindWordBounds(Strings.Lines[P.Y], P.X, N1, N2, FOptWordChars);
|
||||
if N1<>N2 then
|
||||
begin
|
||||
DoCaretSingle(P.X, P.Y);
|
||||
with Carets[0] do
|
||||
begin
|
||||
EndY:= P.Y;
|
||||
EndX:= N1;
|
||||
PosX:= N2;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_CharRange(ACaretIndex: integer; Pnt: TPoint);
|
||||
begin
|
||||
if not Carets.IsIndexValid(ACaretIndex) then Exit;
|
||||
Carets[ACaretIndex].SelectToPoint(Pnt.X, Pnt.Y);
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_WordRange(ACaretIndex: integer; P1, P2: TPoint);
|
||||
begin
|
||||
if not Carets.IsIndexValid(ACaretIndex) then Exit;
|
||||
if not Strings.IsIndexValid(P1.Y) then Exit;
|
||||
if not Strings.IsIndexValid(P2.Y) then Exit;
|
||||
|
||||
if not IsPosSorted(P1.X, P1.Y, P2.X, P2.Y, true) then
|
||||
begin
|
||||
SwapInt(P1.X, P2.X);
|
||||
SwapInt(P1.Y, P2.Y);
|
||||
end;
|
||||
|
||||
P1.X:= SFindWordOffset(Strings.Lines[P1.Y], P1.X, false, false, FOptWordChars);
|
||||
P2.X:= SFindWordOffset(Strings.Lines[P2.Y], P2.X, true, false, FOptWordChars);
|
||||
|
||||
with Carets[ACaretIndex] do
|
||||
begin
|
||||
PosX:= P2.X;
|
||||
PosY:= P2.Y;
|
||||
EndX:= P1.X;
|
||||
EndY:= P1.Y;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_Line(P: TPoint);
|
||||
var
|
||||
PLast: TPoint;
|
||||
begin
|
||||
if not Strings.IsIndexValid(P.Y) then Exit;
|
||||
|
||||
DoCaretSingle(P.X, P.Y);
|
||||
with Carets[0] do
|
||||
begin
|
||||
if P.Y<Strings.Count-1 then
|
||||
begin
|
||||
PosX:= 0;
|
||||
PosY:= P.Y+1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
PLast:= GetEndOfFilePos;
|
||||
PosX:= PLast.X;
|
||||
PosY:= PLast.Y;
|
||||
end;
|
||||
EndX:= 0;
|
||||
EndY:= P.Y;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_All;
|
||||
var
|
||||
P: TPoint;
|
||||
begin
|
||||
P:= GetEndOfFilePos;
|
||||
DoCaretSingle(P.X, P.Y);
|
||||
with Carets[0] do
|
||||
begin
|
||||
EndX:= 0;
|
||||
EndY:= 0;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_Inverted;
|
||||
var
|
||||
NewCarets: TATCarets;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
XPrev, YPrev: integer;
|
||||
i: integer;
|
||||
Sel: boolean;
|
||||
PosLast: TPoint;
|
||||
begin
|
||||
XPrev:= 0;
|
||||
YPrev:= 0;
|
||||
NewCarets:= TATCarets.Create;
|
||||
try
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Carets[i].GetRange(X1, Y1, X2, Y2, Sel);
|
||||
if not Sel then Continue;
|
||||
|
||||
//add range
|
||||
NewCarets.Add(XPrev, YPrev, X1, Y1);
|
||||
XPrev:= X2;
|
||||
YPrev:= Y2;
|
||||
end;
|
||||
|
||||
//add range after last caret
|
||||
PosLast:= GetEndOfFilePos;
|
||||
NewCarets.Add(XPrev, YPrev, PosLast.X, PosLast.Y);
|
||||
|
||||
DoCaretsAssign(NewCarets);
|
||||
finally
|
||||
FreeAndNil(NewCarets);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_SplitSelectionToLines;
|
||||
var
|
||||
NewCarets: TATCarets;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
i, j: integer;
|
||||
Sel: boolean;
|
||||
begin
|
||||
NewCarets:= TATCarets.Create;
|
||||
try
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Carets[i].GetRange(X1, Y1, X2, Y2, Sel);
|
||||
if not Sel then
|
||||
begin
|
||||
NewCarets.Add(X1, Y1);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
if Y1=Y2 then
|
||||
begin
|
||||
NewCarets.Add(X1, Y1, X2, Y2);
|
||||
Continue;
|
||||
end;
|
||||
|
||||
//add first part
|
||||
if X1<Length(Strings.Lines[Y1]) then
|
||||
NewCarets.Add(X1, Y1, Length(Strings.Lines[Y1]), Y1)
|
||||
else
|
||||
NewCarets.Add(X1, Y1);
|
||||
|
||||
//add middle parts
|
||||
for j:= Y1+1 to Y2-1 do
|
||||
begin
|
||||
if Strings.Lines[j]='' then
|
||||
NewCarets.Add(0, j)
|
||||
else
|
||||
NewCarets.Add(0, j, Length(Strings.Lines[j]), j);
|
||||
end;
|
||||
|
||||
//add last part
|
||||
NewCarets.Add(0, Y2, X2, Y2);
|
||||
end;
|
||||
|
||||
DoCaretsAssign(NewCarets);
|
||||
finally
|
||||
FreeAndNil(NewCarets);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_ExtendSelectionByLine;
|
||||
var
|
||||
NewCarets: TATCarets;
|
||||
X1, Y1, X2, Y2: integer;
|
||||
i: integer;
|
||||
Sel: boolean;
|
||||
PosLast: TPoint;
|
||||
begin
|
||||
NewCarets:= TATCarets.Create;
|
||||
try
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
begin
|
||||
Carets[i].GetRange(X1, Y1, X2, Y2, Sel);
|
||||
if not Sel then
|
||||
begin X2:= X1; Y2:= Y1; end;
|
||||
|
||||
X1:= 0; //select entire 1st line
|
||||
if Y2<Strings.Count-1 then
|
||||
begin
|
||||
//select till start of next ln
|
||||
X2:= 0;
|
||||
Y2:= Y2+1;
|
||||
end
|
||||
else
|
||||
begin
|
||||
//select till eof
|
||||
PosLast:= GetEndOfFilePos;
|
||||
X2:= PosLast.X;
|
||||
Y2:= PosLast.Y;
|
||||
end;
|
||||
|
||||
NewCarets.Add(X1, Y1, X2, Y2);
|
||||
end;
|
||||
|
||||
DoCaretsAssign(NewCarets);
|
||||
finally
|
||||
FreeAndNil(NewCarets);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_LineRange(ALineFrom: integer; P: TPoint);
|
||||
var
|
||||
CItem: TATCaretItem;
|
||||
begin
|
||||
DoCaretSingle(P.X, P.Y);
|
||||
CItem:= Carets[0];
|
||||
|
||||
if P.Y<ALineFrom then
|
||||
begin
|
||||
CItem.EndX:= 0;
|
||||
CItem.EndY:= ALineFrom+1;
|
||||
end
|
||||
else
|
||||
if P.Y>ALineFrom then
|
||||
begin
|
||||
CItem.EndX:= 0;
|
||||
CItem.EndY:= ALineFrom;
|
||||
end
|
||||
else
|
||||
if P.Y=ALineFrom then
|
||||
begin
|
||||
DoSelect_Line(P);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_None;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
FSelRect:= cRectEmpty;
|
||||
FSelRectBegin:= Point(-1, -1);
|
||||
|
||||
for i:= 0 to Carets.Count-1 do
|
||||
with Carets[i] do
|
||||
begin
|
||||
EndX:= -1;
|
||||
EndY:= -1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_ColumnBlock(P1, P2: TPoint);
|
||||
var
|
||||
NPosLeft, NPosRight: integer;
|
||||
i: integer;
|
||||
begin
|
||||
if P1.Y>P2.Y then
|
||||
SwapInt(P1.Y, P2.Y);
|
||||
|
||||
FSelRect.Left:= Min(P1.X, P2.X);
|
||||
FSelRect.Right:= Max(P1.X, P2.X);
|
||||
FSelRect.Top:= P1.Y;
|
||||
FSelRect.Bottom:= P2.Y;
|
||||
|
||||
for i:= P1.Y to P2.Y do
|
||||
begin
|
||||
if i=P1.Y then Carets.Clear;
|
||||
Carets.Add(0, 0);
|
||||
with Carets[Carets.Count-1] do
|
||||
begin
|
||||
PosX:= SColumnPosToCharPos(Strings.Lines[i], FSelRect.Right, OptTabSize);
|
||||
PosY:= i;
|
||||
EndX:= SColumnPosToCharPos(Strings.Lines[i], FSelRect.Left, OptTabSize);
|
||||
EndY:= i;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelectionDeleteOrReset;
|
||||
begin
|
||||
if FOptOverwriteSel then
|
||||
DoCommand_TextDeleteSelection
|
||||
else
|
||||
DoSelect_None;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelect_NormalSelToColumnSel(out ABegin, AEnd: TPoint);
|
||||
var
|
||||
Caret: TATCaretItem;
|
||||
begin
|
||||
Caret:= Carets[0];
|
||||
if (Caret.EndY>=0) and (Caret.EndX>=0) then
|
||||
begin
|
||||
ABegin.X:= SCharPosToColumnPos(Strings.Lines[Caret.EndY], Caret.EndX, OptTabSize);
|
||||
ABegin.Y:= Caret.EndY;
|
||||
AEnd.X:= SCharPosToColumnPos(Strings.Lines[Caret.PosY], Caret.PosX, OptTabSize);
|
||||
AEnd.Y:= Caret.PosY;
|
||||
end
|
||||
else
|
||||
begin
|
||||
ABegin.X:= SCharPosToColumnPos(Strings.Lines[Caret.PosY], Caret.PosX, OptTabSize);
|
||||
ABegin.Y:= Caret.PosY;
|
||||
AEnd:= ABegin;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TATSynEdit.DoSelectionDeleteColumnBlock;
|
||||
var
|
||||
X1, X2, i: Integer;
|
||||
Str, StrNew: atString;
|
||||
begin
|
||||
if IsSelRectEmpty then exit;
|
||||
|
||||
Strings.BeginUndoGroup;
|
||||
try
|
||||
for i:= FSelRect.Top to FSelRect.Bottom do
|
||||
begin
|
||||
Str:= Strings.Lines[i];
|
||||
X1:= SColumnPosToCharPos(Str, FSelRect.Left, OptTabSize);
|
||||
X2:= SColumnPosToCharPos(Str, FSelRect.Right, OptTabSize);
|
||||
|
||||
StrNew:= Str;
|
||||
Delete(StrNew, X1+1, X2-X1);
|
||||
if StrNew<>Str then
|
||||
Strings.Lines[i]:= StrNew;
|
||||
end;
|
||||
finally
|
||||
Strings.EndUndoGroup;
|
||||
end;
|
||||
|
||||
DoSelect_None;
|
||||
|
||||
if Carets.Count>0 then
|
||||
Carets[0].PosX:= X1;
|
||||
end;
|
||||
|
||||
226
ATSynEdit/atsynedit/atsynedit_wrapinfo.pas
Normal file
@@ -0,0 +1,226 @@
|
||||
unit ATSynEdit_WrapInfo;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils;
|
||||
|
||||
type
|
||||
{ TATSynWrapItem }
|
||||
|
||||
TATSynWrapFinal = (cWrapItemFinal, cWrapItemCollapsed, cWrapItemMiddle);
|
||||
TATSynWrapItem = class
|
||||
public
|
||||
NLineIndex,
|
||||
NCharIndex,
|
||||
NLength: integer;
|
||||
NIndent: integer;
|
||||
NFinal: TATSynWrapFinal;
|
||||
constructor Create(ALineIndex, ACharIndex, ALength: integer;
|
||||
AIndent: integer; AFinal: TATSynWrapFinal); virtual;
|
||||
procedure Assign(Item: TATSynWrapItem);
|
||||
end;
|
||||
|
||||
type
|
||||
TATCheckLineCollapsedEvent = function(ALineNum: integer): boolean of object;
|
||||
|
||||
type
|
||||
{ TATSynWrapInfo }
|
||||
|
||||
TATSynWrapInfo = class
|
||||
private
|
||||
FList: TList;
|
||||
FOnCheckCollapsed: TATCheckLineCollapsedEvent;
|
||||
function GetItem(N: integer): TATSynWrapItem;
|
||||
public
|
||||
constructor Create; virtual;
|
||||
destructor Destroy; override;
|
||||
procedure Clear;
|
||||
function Count: integer;
|
||||
function IsIndexValid(N: integer): boolean;
|
||||
function IsItemInitial(N: integer): boolean;
|
||||
property Items[N: integer]: TATSynWrapItem read GetItem; default;
|
||||
procedure Add(AItem: TATSynWrapItem);
|
||||
procedure Delete(N: integer);
|
||||
procedure Insert(N: integer; AItem: TATSynWrapItem);
|
||||
procedure FindIndexesOfLineNumber(ALineNum: integer; out AFrom, ATo: integer);
|
||||
procedure SetCapacity(N: integer);
|
||||
procedure ReplaceItems(AFrom, ATo: integer; AItems: TList);
|
||||
property OnCheckLineCollapsed: TATCheckLineCollapsedEvent read FOnCheckCollapsed write FOnCheckCollapsed;
|
||||
end;
|
||||
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
Math, Dialogs, Forms;
|
||||
|
||||
{ TATSynWrapItem }
|
||||
|
||||
constructor TATSynWrapItem.Create(ALineIndex, ACharIndex, ALength: integer;
|
||||
AIndent: integer; AFinal: TATSynWrapFinal);
|
||||
begin
|
||||
NLineIndex:= ALineIndex;
|
||||
NCharIndex:= ACharIndex;
|
||||
NLength:= ALength;
|
||||
NIndent:= AIndent;
|
||||
NFinal:= AFinal;
|
||||
end;
|
||||
|
||||
procedure TATSynWrapItem.Assign(Item: TATSynWrapItem);
|
||||
begin
|
||||
NLineIndex:= Item.NLineIndex;
|
||||
NCharIndex:= Item.NCharIndex;
|
||||
NLength:= Item.NLength;
|
||||
NIndent:= Item.NIndent;
|
||||
NFinal:= Item.NFinal;
|
||||
end;
|
||||
|
||||
{ TATSynWrapInfo }
|
||||
|
||||
function TATSynWrapInfo.GetItem(N: integer): TATSynWrapItem;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
Result:= TATSynWrapItem(FList[N])
|
||||
else
|
||||
Result:= nil;
|
||||
end;
|
||||
|
||||
constructor TATSynWrapInfo.Create;
|
||||
begin
|
||||
FList:= TList.Create;
|
||||
end;
|
||||
|
||||
destructor TATSynWrapInfo.Destroy;
|
||||
begin
|
||||
Clear;
|
||||
FreeAndNil(FList);
|
||||
inherited;
|
||||
end;
|
||||
|
||||
procedure TATSynWrapInfo.Clear;
|
||||
var
|
||||
i: integer;
|
||||
begin
|
||||
for i:= FList.Count-1 downto 0 do
|
||||
begin
|
||||
TObject(FList[i]).Free;
|
||||
end;
|
||||
FList.Clear;
|
||||
end;
|
||||
|
||||
function TATSynWrapInfo.Count: integer;
|
||||
begin
|
||||
Result:= FList.Count;
|
||||
end;
|
||||
|
||||
function TATSynWrapInfo.IsIndexValid(N: integer): boolean;
|
||||
begin
|
||||
Result:= (N>=0) and (N<FList.Count);
|
||||
end;
|
||||
|
||||
function TATSynWrapInfo.IsItemInitial(N: integer): boolean;
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
begin
|
||||
if N=0 then
|
||||
Result:= true
|
||||
else
|
||||
Result:= Items[N].NLineIndex<>Items[N-1].NLineIndex;
|
||||
end
|
||||
else
|
||||
Result:= true;
|
||||
end;
|
||||
|
||||
procedure TATSynWrapInfo.Add(AItem: TATSynWrapItem);
|
||||
begin
|
||||
FList.Add(AItem);
|
||||
end;
|
||||
|
||||
procedure TATSynWrapInfo.Delete(N: integer);
|
||||
begin
|
||||
if IsIndexValid(N) then
|
||||
FList.Delete(N);
|
||||
end;
|
||||
|
||||
procedure TATSynWrapInfo.Insert(N: integer; AItem: TATSynWrapItem);
|
||||
begin
|
||||
if N>=Count then
|
||||
FList.Add(AItem)
|
||||
else
|
||||
FList.Insert(N, AItem);
|
||||
end;
|
||||
|
||||
procedure TATSynWrapInfo.FindIndexesOfLineNumber(ALineNum: integer; out AFrom, ATo: integer);
|
||||
var
|
||||
a, b, m, dif: integer;
|
||||
begin
|
||||
AFrom:= -1;
|
||||
ATo:= -1;
|
||||
|
||||
if Assigned(FOnCheckCollapsed) then
|
||||
if FOnCheckCollapsed(ALineNum) then Exit;
|
||||
|
||||
a:= 0;
|
||||
b:= Count-1;
|
||||
if b<0 then Exit;
|
||||
|
||||
repeat
|
||||
dif:= Items[a].NLineIndex-ALineNum;
|
||||
if dif=0 then begin m:= a; Break end;
|
||||
|
||||
//middle, which is near b if not exact middle
|
||||
m:= (a+b+1) div 2;
|
||||
|
||||
dif:= Items[m].NLineIndex-ALineNum;
|
||||
if dif=0 then Break;
|
||||
|
||||
if Abs(a-b)<=1 then Exit;
|
||||
if dif>0 then b:= m else a:= m;
|
||||
until false;
|
||||
|
||||
AFrom:= m;
|
||||
ATo:= m;
|
||||
while (AFrom>0) and (Items[AFrom-1].NLineIndex=ALineNum) do Dec(AFrom);
|
||||
while (ATo<Count-1) and (Items[ATo+1].NLineIndex=ALineNum) do Inc(ATo);
|
||||
end;
|
||||
|
||||
procedure TATSynWrapInfo.SetCapacity(N: integer);
|
||||
begin
|
||||
FList.Capacity:= Max(1024, N);
|
||||
end;
|
||||
|
||||
//optimized; don't just del/ins
|
||||
procedure TATSynWrapInfo.ReplaceItems(AFrom, ATo: integer; AItems: TList);
|
||||
var
|
||||
Dif, i: integer;
|
||||
begin
|
||||
Dif:= AItems.Count - (ATo-AFrom+1);
|
||||
|
||||
//adjust count of items
|
||||
if Dif<0 then
|
||||
begin
|
||||
for i:= 1 to Abs(Dif) do
|
||||
Delete(AFrom);
|
||||
end
|
||||
else
|
||||
if Dif>0 then
|
||||
begin
|
||||
for i:= 1 to Dif do
|
||||
Insert(AFrom, TATSynWrapItem.Create(0, 0, 0, 0, Low(TATSynWrapFinal)));
|
||||
end;
|
||||
|
||||
//overwrite N items
|
||||
for i:= 0 to AItems.Count-1 do
|
||||
TATSynWrapItem(FList[AFrom+i]).Assign(TATSynWrapItem(AItems[i]));
|
||||
|
||||
//must free list
|
||||
for i:= 0 to AItems.Count-1 do
|
||||
TObject(AItems[i]).Free;
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
4180
ATSynEdit/atsynedit/regexpr.pas
Normal file
BIN
ATSynEdit/atsynedit/res/fold_minus.bmp
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
ATSynEdit/atsynedit/res/fold_plus.bmp
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
2
ATSynEdit/atsynedit/res/foldbar.rc
Normal file
@@ -0,0 +1,2 @@
|
||||
FOLDBAR_P BITMAP fold_plus.bmp
|
||||
FOLDBAR_M BITMAP fold_minus.bmp
|
||||
BIN
ATSynEdit/atsynedit/res/ide/tatcomboedit.png
Normal file
|
After Width: | Height: | Size: 428 B |
BIN
ATSynEdit/atsynedit/res/ide/tatedit.png
Normal file
|
After Width: | Height: | Size: 601 B |
BIN
ATSynEdit/atsynedit/res/ide/tatsynedit.png
Normal file
|
After Width: | Height: | Size: 913 B |
BIN
ATSynEdit/atsynedit/res/mouse_scroll.bmp
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
ATSynEdit/atsynedit/res/mouse_scroll.cur
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
ATSynEdit/atsynedit/res/mouse_scroll_down.cur
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
ATSynEdit/atsynedit/res/mouse_scroll_left.cur
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
ATSynEdit/atsynedit/res/mouse_scroll_right.cur
Normal file
|
After Width: | Height: | Size: 766 B |
BIN
ATSynEdit/atsynedit/res/mouse_scroll_up.cur
Normal file
|
After Width: | Height: | Size: 766 B |
7
ATSynEdit/atsynedit/res/nicescroll.rc
Normal file
@@ -0,0 +1,7 @@
|
||||
AB_MOVE BITMAP mouse_scroll.bmp
|
||||
|
||||
AB_MOVE CURSOR mouse_scroll.cur
|
||||
AB_MOVE_U CURSOR mouse_scroll_up.cur
|
||||
AB_MOVE_D CURSOR mouse_scroll_down.cur
|
||||
AB_MOVE_L CURSOR mouse_scroll_left.cur
|
||||
AB_MOVE_R CURSOR mouse_scroll_right.cur
|
||||