mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 15:58:07 +02:00
1398 lines
36 KiB
ObjectPascal
1398 lines
36 KiB
ObjectPascal
unit SynEditMarks;
|
|
|
|
{$I synedit.inc}
|
|
{$IFOPT C+}
|
|
{$DEFINE AssertSynMemIndex}
|
|
{$ENDIF}
|
|
{$IFDEF SynAssert}
|
|
{$DEFINE AssertSynMemIndex}
|
|
{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, Controls, SysUtils, Math, SynEditMiscClasses, LazSynEditText,
|
|
LazLoggerBase, ImgList;
|
|
|
|
type
|
|
|
|
TSynEditMark = class;
|
|
TSynEditMarkLine = class;
|
|
TSynEditMarkLineList = class;
|
|
TSynEditMarkList = class;
|
|
|
|
TSynEditMarkChangeReason =
|
|
( smcrAdded, smcrRemoved,
|
|
smcrLine, smcrColumn,
|
|
smcrVisible,
|
|
smcrChanged
|
|
);
|
|
TSynEditMarkChangeReasons = set of TSynEditMarkChangeReason;
|
|
|
|
TSynEditMarkSortOrder = (smsoUnsorted, smsoColumn, smsoPriority, smsoBookmarkFirst, smsoBookMarkLast);
|
|
|
|
TSynEditMarkChangeEvent = procedure(Sender: TSynEditMark; Changes: TSynEditMarkChangeReasons)
|
|
of object;
|
|
|
|
TPlaceMarkEvent = procedure(Sender: TObject; var Mark: TSynEditMark) of object;
|
|
|
|
{ TSynEditMarkChangedHandlerList }
|
|
|
|
TSynEditMarkChangedHandlerList = Class(TSynFilteredMethodList)
|
|
public
|
|
procedure Add(AHandler: TSynEditMarkChangeEvent; Changes: TSynEditMarkChangeReasons);
|
|
procedure Remove(AHandler: TSynEditMarkChangeEvent);
|
|
procedure CallMarkChangedHandlers(Sender: TSynEditMark; Changes: TSynEditMarkChangeReasons);
|
|
end;
|
|
|
|
|
|
{ TSynEditMark }
|
|
|
|
TSynEditMark = class
|
|
private
|
|
FImageList: TCustomImageList;
|
|
FMarkLine: TSynEditMarkLine;
|
|
FMarkList: TSynEditMarkList;
|
|
FLine: Integer; // Only valid, if not part of a TSynEditMarkLine
|
|
FOldLine: integer;
|
|
FOwnerEdit: TSynEditBase;
|
|
function GetLine: integer;
|
|
procedure SetBookmarkNum(AValue: integer);
|
|
procedure SetMarkLine(const AValue: TSynEditMarkLine);
|
|
procedure SetMarkList(const AValue: TSynEditMarkList); virtual;
|
|
procedure SetOwnerEdit(const AValue: TSynEditBase);
|
|
protected
|
|
FColumn, FImage, FPriority: Integer;
|
|
FVisible: boolean;
|
|
FInternalImage: boolean;
|
|
FBookmarkNum: integer;
|
|
FChangeLock: Integer;
|
|
FChanges: TSynEditMarkChangeReasons;
|
|
procedure SetColumn(const Value: Integer); virtual;
|
|
procedure SetImage(const Value: Integer); virtual;
|
|
procedure SetLine(const Value: Integer); virtual;
|
|
procedure SetPriority(const AValue: integer); virtual;
|
|
procedure SetVisible(const Value: boolean); virtual;
|
|
procedure SetInternalImage(const Value: boolean);
|
|
function GetIsBookmark: boolean; virtual;
|
|
|
|
procedure DoChange(AChanges: TSynEditMarkChangeReasons); virtual;
|
|
procedure ForceChange(AChanges: TSynEditMarkChangeReasons);
|
|
|
|
property MarkLine: TSynEditMarkLine read FMarkLine write SetMarkLine;
|
|
property MarkList: TSynEditMarkList read FMarkList write SetMarkList;
|
|
public
|
|
constructor Create(ASynEdit: TSynEditBase);
|
|
destructor Destroy; override;
|
|
procedure IncChangeLock;
|
|
procedure DecChangeLock;
|
|
|
|
property OwnerEdit: TSynEditBase read FOwnerEdit write SetOwnerEdit;
|
|
|
|
property OldLine: integer read FOldLine; // not used, if synedit insert/delete lines
|
|
property Line: integer read GetLine write SetLine;
|
|
property Column: integer read FColumn write SetColumn; // Logical position
|
|
property Priority: integer read FPriority write SetPriority;
|
|
property Visible: boolean read FVisible write SetVisible;
|
|
|
|
property BookmarkNumber: integer read FBookmarkNum write SetBookmarkNum;
|
|
property IsBookmark: boolean read GetIsBookmark;
|
|
|
|
// InternalImage: Use Internal bookmark image 0..9;
|
|
// Ignore "BookMarkOpt.BookmarkImages" or "ImageList"
|
|
property InternalImage: boolean read FInternalImage write SetInternalImage;
|
|
// ImageIndex: Index in "BookMarkOpt.BookmarkImages" or "ImageList"
|
|
property ImageIndex: integer read FImage write SetImage;
|
|
// ImageList: If assigned, then use instead of "BookMarkOpt.BookmarkImages"
|
|
// Must have same width as "BookMarkOpt.BookmarkImages"
|
|
property ImageList: TCustomImageList read FImageList write FImageList;
|
|
end;
|
|
|
|
{ TSynEditBookMark }
|
|
|
|
TSynEditBookMark = class(TSynEditMark)
|
|
private type
|
|
|
|
{ TSynEditTopLeftMark }
|
|
|
|
TSynEditTopLeftMark = class(TSynEditMark)
|
|
private
|
|
FBookMark: TSynEditBookMark;
|
|
public
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
private
|
|
FTopLeftMark: TSynEditMark;
|
|
procedure SetMarkList(const AValue: TSynEditMarkList); override;
|
|
protected
|
|
function GetIsBookmark: boolean; override;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure SetTopLeft(ATop, ALeft: integer);
|
|
property TopLeftMark: TSynEditMark read FTopLeftMark;
|
|
end;
|
|
|
|
{ TSynEditMarkLine }
|
|
|
|
TSynEditMarkLine = class(TSynSizedDifferentialAVLNode)
|
|
private
|
|
FMarks: TFPList;
|
|
FLineList: TSynEditMarkLineList;
|
|
function GetLineNum: Integer;
|
|
function GetMark(Index: Integer): TSynEditMark;
|
|
function GetLeft: TSynEditMarkLine;
|
|
function GetRight: TSynEditMarkLine;
|
|
procedure SetMark(Index: Integer; const AValue: TSynEditMark);
|
|
protected
|
|
FLockChangeSize: Integer;
|
|
FCurrentSort1, FCurrentSort2: TSynEditMarkSortOrder;
|
|
procedure ChangeSize;
|
|
procedure IncLockChangeSize; // used in global destruction
|
|
{$IFDEF SynDebug}
|
|
function Debug: String; override;
|
|
{$ENDIF}
|
|
protected
|
|
property Left: TSynEditMarkLine read GetLeft;
|
|
property Right: TSynEditMarkLine read GetRight;
|
|
property Size: Integer read FSize;
|
|
property LeftSizeSum: Integer read FLeftSizeSum;
|
|
property LineOffset: Integer read FPositionOffset write FPositionOffset;
|
|
public
|
|
constructor Create(ALineNum: Integer; AOwner: TSynEditMarkLineList);
|
|
destructor Destroy; override;
|
|
|
|
procedure Sort(PrimaryOrder: TSynEditMarkSortOrder;
|
|
SecondaryOrder: TSynEditMarkSortOrder = smsoUnsorted);
|
|
function Add(Item: TSynEditMark): Integer;
|
|
procedure Delete(Index: Integer);
|
|
function Remove(Item: TSynEditMark): Integer;
|
|
procedure Clear(FreeMarks: Boolean = False);
|
|
function Count: Integer;
|
|
function VisibleCount: Integer;
|
|
function IndexOf(AMark: TSynEditMark): Integer;
|
|
property Items[Index: Integer]: TSynEditMark read GetMark write SetMark; default;
|
|
property LineNum: Integer read GetLineNum;
|
|
end;
|
|
|
|
{ TSynEditMarkIterator }
|
|
|
|
TSynEditMarkIterator = class
|
|
private
|
|
FMarkList: TSynEditMarkList;
|
|
FCurrentItem: TSynEditMark;
|
|
FCurrentIndex: Integer;
|
|
FBOL, FEOL: Boolean;
|
|
public
|
|
constructor Create(AOwner: TSynEditMarkList);
|
|
|
|
procedure Invalidate;
|
|
procedure GotoMark(AMark: TSynEditMark);
|
|
procedure GotoBOL;
|
|
procedure GotoEOL;
|
|
|
|
function First: Boolean;
|
|
function Last: Boolean;
|
|
function Next: Boolean;
|
|
function Previous: Boolean;
|
|
|
|
function NextLine: Boolean;
|
|
function PreviousLine: Boolean;
|
|
|
|
function IsValid: Boolean;
|
|
function IsValidAndNotModified: Boolean;
|
|
|
|
property BOL: Boolean read FBOL;
|
|
property EOL: Boolean read FEOL;
|
|
|
|
property Mark: TSynEditMark read FCurrentItem;
|
|
end;
|
|
|
|
{ TSynEditMarkLineList }
|
|
|
|
TSynEditMarkLineList = class(TSynSizedDifferentialAVLTree)
|
|
private
|
|
FMarkList: TSynEditMarkList;
|
|
FIndexIterator: TSynEditMarkIterator;
|
|
FInClear: boolean;
|
|
// marks
|
|
function GetMark(Index : Integer): TSynEditMark;
|
|
function GetMarkCount: Integer;
|
|
procedure SetMark(Index : Integer; const AValue: TSynEditMark);
|
|
// lines
|
|
function GetMarkLineByMarkIndex(var AMarkIndex: Integer): TSynEditMarkLine;
|
|
function GetMarkLine(LineNum: Integer): TSynEditMarkLine;
|
|
function GetOrAddMarkLine(LineNum: Integer; AddIfNotExist: Boolean;
|
|
UseNext: Boolean = False): TSynEditMarkLine;
|
|
protected
|
|
// will be reset by TSynEditMarkLine.ChangeSize;
|
|
FLastIteratorIndex: Integer;
|
|
function CreateNode(APosition: Integer): TSynSizedDifferentialAVLNode; override;
|
|
public
|
|
constructor Create(AOwner: TSynEditMarkList);
|
|
destructor Destroy; override;
|
|
|
|
procedure Remove(Item: TSynEditMarkLine; FreeMarks: Boolean = False);
|
|
procedure Clear; override; // FreeMarks=False;
|
|
procedure Clear(FreeMarks: Boolean);
|
|
function GetOrAddLine(LineNum: Integer): TSynEditMarkLine;
|
|
function GetLineOrNext(LineNum: Integer): TSynEditMarkLine;
|
|
property Lines[LineNum: Integer]: TSynEditMarkLine read GetMarkLine;
|
|
public
|
|
function AddMark(Item: TSynEditMark): Integer;
|
|
procedure DeleteMark(Index: Integer);
|
|
function RemoveMark(Item: TSynEditMark): Integer;
|
|
function IndexOfMark(AMark: TSynEditMark): Integer;
|
|
property MarkCount: Integer read GetMarkCount;
|
|
property Mark[Index : Integer]: TSynEditMark read GetMark write SetMark;
|
|
end;
|
|
|
|
{ TSynEditMarkList
|
|
A list of mark objects. Each object cause a litle picture to be drawn in the
|
|
gutter.
|
|
}
|
|
|
|
TSynEditMarkList = class
|
|
private
|
|
function GetMarkLine(LineNum: Integer): TSynEditMarkLine;
|
|
protected
|
|
FLines: TSynEditStringsLinked;
|
|
FOwnerList: TFPList;
|
|
FMarkLines: TSynEditMarkLineList;
|
|
fOnChange: TNotifyEvent;
|
|
FChangeHandlers: TSynEditMarkChangedHandlerList;
|
|
FInternalIterator: TSynEditMarkIterator;
|
|
procedure DoChange;
|
|
procedure MarkChanged(Sender: TSynEditMark; AChanges: TSynEditMarkChangeReasons); virtual;
|
|
function Get(Index: Integer): TSynEditMark;
|
|
procedure Put(Index: Integer; Item: TSynEditMark);
|
|
procedure DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
|
|
aLineBrkCnt: Integer; aText: String);
|
|
function HasOwnerEdit(AEdit: TSynEditBase): Boolean;
|
|
public
|
|
constructor Create(AOwner: TSynEditBase; ALines: TSynEditStringsLinked);
|
|
destructor Destroy; override;
|
|
{$IFDEF SynDebug}
|
|
procedure Debug;
|
|
{$ENDIF}
|
|
|
|
|
|
procedure Add(Item: TSynEditMark);
|
|
procedure Delete(Index: Integer);
|
|
function Remove(Item: TSynEditMark): Integer;
|
|
function IndexOf(Item: TSynEditMark): Integer;
|
|
function Count: Integer;
|
|
|
|
procedure ClearLine(line: integer);
|
|
|
|
procedure RegisterChangeHandler(Handler: TSynEditMarkChangeEvent; Filter: TSynEditMarkChangeReasons);
|
|
procedure UnRegisterChangeHandler(Handler: TSynEditMarkChangeEvent);
|
|
public
|
|
property Items[Index: Integer]: TSynEditMark read Get write Put; default;
|
|
property Line[LineNum: Integer]: TSynEditMarkLine read GetMarkLine;
|
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
|
end;
|
|
|
|
function DoMarksCompareBookmarksFirst(Item1, Item2: Pointer): Integer;
|
|
function DoMarksCompareBookmarksLast(Item1, Item2: Pointer): Integer;
|
|
|
|
implementation
|
|
|
|
function DoMarksCompareBookmarksFirst(Item1, Item2: Pointer): Integer;
|
|
var
|
|
Mark1: TSynEditMark absolute Item1;
|
|
Mark2: TSynEditMark absolute Item2;
|
|
begin
|
|
Result := 0;
|
|
if Mark1 = Mark2 then Exit;
|
|
|
|
if Mark1.IsBookmark then
|
|
Result := -1
|
|
else
|
|
if Mark2.IsBookmark then
|
|
Result := 1
|
|
else
|
|
if Mark1.Priority <> Mark2.Priority then
|
|
Result := Mark2.Priority - Mark1.Priority
|
|
else
|
|
Result := Mark2.Column - Mark1.Column;
|
|
end;
|
|
|
|
function DoMarksCompareBookmarksLast(Item1, Item2: Pointer): Integer;
|
|
var
|
|
Mark1: TSynEditMark absolute Item1;
|
|
Mark2: TSynEditMark absolute Item2;
|
|
begin
|
|
Result := 0;
|
|
if Mark1 = Mark2 then Exit;
|
|
|
|
if Mark1.IsBookmark then
|
|
Result := 1
|
|
else
|
|
if Mark2.IsBookmark then
|
|
Result := -1
|
|
else
|
|
if Mark1.Priority <> Mark2.Priority then
|
|
Result := Mark2.Priority - Mark1.Priority
|
|
else
|
|
Result := Mark2.Column - Mark1.Column;
|
|
end;
|
|
|
|
{ TSynEditMark }
|
|
|
|
procedure TSynEditMark.SetPriority(const AValue: integer);
|
|
begin
|
|
FPriority := AValue;
|
|
end;
|
|
|
|
procedure TSynEditMark.SetMarkList(const AValue: TSynEditMarkList);
|
|
begin
|
|
if AValue = FMarkList then
|
|
exit;
|
|
if FMarkList <> nil then begin
|
|
DoChange([smcrRemoved]);
|
|
ForceChange(FChanges);
|
|
end;
|
|
FMarkList := AValue;
|
|
if FMarkList <> nil then
|
|
DoChange([smcrAdded]);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetOwnerEdit(const AValue: TSynEditBase);
|
|
begin
|
|
if FOwnerEdit = AValue then exit;
|
|
if (AValue = nil) or (FMarkList = nil) or
|
|
(not FMarkList.HasOwnerEdit(AValue))
|
|
then
|
|
raise Exception.Create('Invalid Owner');
|
|
FOwnerEdit := AValue;
|
|
end;
|
|
|
|
function TSynEditMark.GetLine: integer;
|
|
begin
|
|
if FMarkLine <> nil then
|
|
Result := FMarkLine.LineNum
|
|
else
|
|
Result := FLine;
|
|
end;
|
|
|
|
procedure TSynEditMark.SetBookmarkNum(AValue: integer);
|
|
begin
|
|
assert(Self is TSynEditBookMark, 'TSynEditMark.SetBookmarkNum: Self is TSynEditBookMark');
|
|
FBookmarkNum := AValue;
|
|
end;
|
|
|
|
procedure TSynEditMark.SetMarkLine(const AValue: TSynEditMarkLine);
|
|
begin
|
|
if FMarkLine = AValue then exit;
|
|
// keep the current line, if we loose the markline
|
|
if (AValue = nil) and (FMarkLine <> nil) then
|
|
FLine := FMarkLine.LineNum;
|
|
FMarkLine := AValue;
|
|
end;
|
|
|
|
function TSynEditMark.GetIsBookmark: boolean;
|
|
begin
|
|
Result := False;
|
|
end;
|
|
|
|
procedure TSynEditMark.DoChange(AChanges: TSynEditMarkChangeReasons);
|
|
begin
|
|
if FChangeLock > 0 then begin
|
|
FChanges := FChanges + AChanges;
|
|
exit;
|
|
end;
|
|
ForceChange(AChanges);
|
|
end;
|
|
|
|
procedure TSynEditMark.ForceChange(AChanges: TSynEditMarkChangeReasons);
|
|
begin
|
|
if (FMarkList <> nil) and (AChanges <> []) then
|
|
FMarkList.MarkChanged(Self, AChanges);
|
|
FChanges := [];
|
|
end;
|
|
|
|
procedure TSynEditMark.SetColumn(const Value: Integer);
|
|
begin
|
|
if FColumn = Value then
|
|
exit;
|
|
FColumn := Value;
|
|
DoChange([smcrColumn]);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetImage(const Value: Integer);
|
|
begin
|
|
if FImage = Value then
|
|
exit;
|
|
FImage := Value;
|
|
DoChange([smcrChanged]);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetInternalImage(const Value: boolean);
|
|
begin
|
|
if FInternalImage = Value then
|
|
exit;
|
|
FInternalImage := Value;
|
|
DoChange([smcrChanged]);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetLine(const Value: Integer);
|
|
var
|
|
f: Boolean;
|
|
begin
|
|
if Line = Value then
|
|
exit;
|
|
f := FMarkLine <> nil;
|
|
if f then
|
|
FMarkLine.Remove(self);
|
|
FMarkLine := nil;;
|
|
FOldLine := FLine;
|
|
FLine := Value;
|
|
if f and (FMarkList <> nil) then
|
|
FMarkList.FMarkLines.AddMark(self);
|
|
DoChange([smcrLine]);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetVisible(const Value: boolean);
|
|
begin
|
|
if FVisible = Value then
|
|
exit;
|
|
FVisible := Value;
|
|
DoChange([smcrVisible]);
|
|
end;
|
|
|
|
constructor TSynEditMark.Create(ASynEdit: TSynEditBase);
|
|
begin
|
|
inherited Create;
|
|
FOwnerEdit := ASynEdit;
|
|
FBookmarkNum := -1;
|
|
FPriority := 0;
|
|
end;
|
|
|
|
destructor TSynEditMark.Destroy;
|
|
begin
|
|
if FMarkList <> nil then begin
|
|
DoChange([smcrRemoved]);
|
|
FMarkList.Remove(self); // includes MarkLine
|
|
end
|
|
else
|
|
if FMarkLine <> nil then
|
|
FMarkLine.Remove(Self);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSynEditMark.IncChangeLock;
|
|
begin
|
|
inc(FChangeLock);
|
|
end;
|
|
|
|
procedure TSynEditMark.DecChangeLock;
|
|
begin
|
|
dec(FChangeLock);
|
|
if (FChangeLock = 0) and (FChanges <> []) then
|
|
DoChange(FChanges);
|
|
end;
|
|
|
|
{ TSynEditBookMark }
|
|
|
|
procedure TSynEditBookMark.SetMarkList(const AValue: TSynEditMarkList);
|
|
begin
|
|
inherited SetMarkList(AValue);
|
|
if (FMarkList <> nil) and (FTopLeftMark <> nil) then
|
|
FMarkList.Add(FTopLeftMark);
|
|
end;
|
|
|
|
function TSynEditBookMark.GetIsBookmark: boolean;
|
|
begin
|
|
Result := True;
|
|
end;
|
|
|
|
destructor TSynEditBookMark.Destroy;
|
|
begin
|
|
if FTopLeftMark <> nil then begin
|
|
TSynEditTopLeftMark(FTopLeftMark).FBookMark := nil;
|
|
if FMarkList <> nil then // Otherwise TSynEditMarkLineList.Clear will take care.
|
|
FTopLeftMark.Free;
|
|
end;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSynEditBookMark.SetTopLeft(ATop, ALeft: integer);
|
|
begin
|
|
if (ATop <= 0) or (ALeft <= 0) then
|
|
exit;
|
|
|
|
if FTopLeftMark = nil then begin
|
|
FTopLeftMark := TSynEditTopLeftMark.Create(OwnerEdit);
|
|
TSynEditTopLeftMark(FTopLeftMark).FBookMark := Self;
|
|
|
|
FTopLeftMark.Line := ATop;
|
|
FTopLeftMark.Column := ALeft;
|
|
|
|
if (FMarkList <> nil) then
|
|
FMarkList.Add(FTopLeftMark);
|
|
end
|
|
else
|
|
if (FTopLeftMark.Line <> ATop) or (FTopLeftMark.Column <> ALeft) then begin
|
|
FTopLeftMark.Line := ATop;
|
|
FTopLeftMark.Column := ALeft;
|
|
end;
|
|
end;
|
|
|
|
{ TSynEditBookMark.TSynEditTopLeftMark }
|
|
|
|
destructor TSynEditBookMark.TSynEditTopLeftMark.Destroy;
|
|
begin
|
|
if FBookMark <> nil then
|
|
FBookMark.FTopLeftMark := nil;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TSynEditMarkLine }
|
|
|
|
function TSynEditMarkLine.GetMark(Index: Integer): TSynEditMark;
|
|
begin
|
|
Result := TSynEditMark(FMarks[Index]);
|
|
end;
|
|
|
|
function TSynEditMarkLine.GetLeft: TSynEditMarkLine;
|
|
begin
|
|
Result := TSynEditMarkLine(FLeft);
|
|
end;
|
|
|
|
function TSynEditMarkLine.GetLineNum: Integer;
|
|
begin
|
|
Result := LineOffset;
|
|
if FParent <> nil then
|
|
Result := Result + TSynEditMarkLine(FParent).GetLineNum;
|
|
end;
|
|
|
|
function TSynEditMarkLine.GetRight: TSynEditMarkLine;
|
|
begin
|
|
Result := TSynEditMarkLine(FRight);
|
|
end;
|
|
|
|
procedure TSynEditMarkLine.SetMark(Index: Integer; const AValue: TSynEditMark);
|
|
begin
|
|
if Items[Index] = AValue then
|
|
exit;
|
|
Items[Index].MarkLine := nil;
|
|
FMarks[Index] := AValue;
|
|
AValue.MarkLine := Self;
|
|
end;
|
|
|
|
procedure TSynEditMarkLine.ChangeSize;
|
|
var
|
|
Cnt: SmallInt;
|
|
begin
|
|
Cnt := Count;
|
|
if (FLockChangeSize > 0) or (FSize = Cnt) then exit;
|
|
FLineList.FLastIteratorIndex := -1;
|
|
if Cnt = 0 then begin
|
|
inc(FLockChangeSize);
|
|
FLineList.Remove(Self);
|
|
end else begin
|
|
AdjustParentLeftCount(Cnt - FSize);
|
|
FSize := Cnt;
|
|
end;
|
|
end;
|
|
|
|
procedure TSynEditMarkLine.IncLockChangeSize;
|
|
begin
|
|
inc(FLockChangeSize);
|
|
end;
|
|
|
|
{$IFDEF SynDebug}
|
|
function TSynEditMarkLine.Debug: String;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := inherited;
|
|
Result := Result
|
|
+ Format('Cnt=%d, LineNm=%d ', [Count, LineNum]);
|
|
for i := 0 to Count-1 do
|
|
Result := Result
|
|
+ Format(': L=%d, C=%d, Bk=%d %s, Img=%d %s :',
|
|
[Items[i].Line, Items[i].Column,
|
|
Items[i].BookmarkNumber, dbgs(Items[i].IsBookmark),
|
|
Items[i].ImageIndex, dbgs(Items[i].InternalImage) ]);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
constructor TSynEditMarkLine.Create(ALineNum: Integer; AOwner: TSynEditMarkLineList);
|
|
begin
|
|
inherited Create;;
|
|
FMarks := TFPList.Create;
|
|
FLineList := AOwner;
|
|
FPositionOffset := ALineNum;
|
|
FLockChangeSize := 0;
|
|
FCurrentSort1 := smsoUnsorted;
|
|
end;
|
|
|
|
destructor TSynEditMarkLine.Destroy;
|
|
begin
|
|
Clear(True);
|
|
inherited Destroy;
|
|
FreeAndNil(FMarks);
|
|
end;
|
|
|
|
function CompareSynEditMarks(Mark1, Mark2: Pointer): Integer;
|
|
var
|
|
m1: TSynEditMark absolute Mark1;
|
|
m2: TSynEditMark absolute Mark2;
|
|
begin
|
|
case m1.MarkLine.FCurrentSort1 of
|
|
smsoColumn: Result := m2.Column - m1.Column;
|
|
smsoPriority: Result := m2.Priority - m1.Priority;
|
|
smsoBookmarkFirst:
|
|
if (m1.IsBookmark) and (not m2.IsBookmark) then Result := -1
|
|
else if (not m1.IsBookmark) and (m2.IsBookmark) then Result := 1
|
|
else Result := 0;
|
|
smsoBookMarkLast:
|
|
if (m1.IsBookmark) and (not m2.IsBookmark) then Result := 1
|
|
else if (not m1.IsBookmark) and (m2.IsBookmark) then Result := -1
|
|
else Result := 0;
|
|
else
|
|
Result := 0;
|
|
end;
|
|
if Result <> 0 then
|
|
exit;
|
|
|
|
case m1.MarkLine.FCurrentSort2 of
|
|
smsoColumn: Result := m2.Column - m1.Column;
|
|
smsoPriority: Result := m2.Priority - m1.Priority;
|
|
smsoBookmarkFirst:
|
|
if (m1.IsBookmark) and (not m2.IsBookmark) then Result := -1
|
|
else if (not m1.IsBookmark) and (m2.IsBookmark) then Result := 1
|
|
else Result := 0;
|
|
smsoBookMarkLast:
|
|
if (m1.IsBookmark) and (not m2.IsBookmark) then Result := 1
|
|
else if (not m1.IsBookmark) and (m2.IsBookmark) then Result := -1
|
|
else Result := 0;
|
|
else
|
|
Result := 0;
|
|
end;
|
|
if Result <> 0 then
|
|
exit;
|
|
|
|
Result := m2.Column - m1.Column;
|
|
if Result <> 0 then
|
|
exit;
|
|
|
|
Result := m2.Priority - m1.Priority;
|
|
if Result <> 0 then
|
|
exit;
|
|
|
|
if Mark2 > Mark1 then
|
|
Result := 1
|
|
else if Mark1 > Mark2 then
|
|
Result := -1
|
|
else
|
|
Result := 0;
|
|
end;
|
|
|
|
procedure TSynEditMarkLine.Sort(PrimaryOrder: TSynEditMarkSortOrder;
|
|
SecondaryOrder: TSynEditMarkSortOrder);
|
|
begin
|
|
if PrimaryOrder = smsoUnsorted then
|
|
PrimaryOrder := SecondaryOrder;
|
|
if PrimaryOrder = SecondaryOrder then
|
|
SecondaryOrder := smsoUnsorted;
|
|
|
|
if (PrimaryOrder = FCurrentSort1) and (SecondaryOrder = FCurrentSort2) then
|
|
exit;
|
|
|
|
FCurrentSort1 := PrimaryOrder;
|
|
FCurrentSort2 := SecondaryOrder;
|
|
|
|
if PrimaryOrder = smsoUnsorted then
|
|
exit;
|
|
|
|
FMarks.Sort(@CompareSynEditMarks);
|
|
end;
|
|
|
|
function TSynEditMarkLine.Add(Item: TSynEditMark): Integer;
|
|
begin
|
|
FCurrentSort1 := smsoUnsorted;
|
|
Result := FMarks.Add(Item);
|
|
Item.MarkLine := Self;
|
|
ChangeSize;
|
|
end;
|
|
|
|
procedure TSynEditMarkLine.Delete(Index: Integer);
|
|
begin
|
|
Items[Index].MarkLine := nil;
|
|
FMarks.Delete(Index);
|
|
ChangeSize;
|
|
end;
|
|
|
|
function TSynEditMarkLine.Remove(Item: TSynEditMark): Integer;
|
|
begin
|
|
Item.MarkLine := nil;
|
|
Result := FMarks.Remove(Item);
|
|
ChangeSize;
|
|
end;
|
|
|
|
procedure TSynEditMarkLine.Clear(FreeMarks: Boolean = False);
|
|
var
|
|
m: TSynEditMark;
|
|
begin
|
|
inc(FLockChangeSize);
|
|
try
|
|
while Count > 0 do begin
|
|
m := Items[0];
|
|
FMarks.Delete(0);
|
|
if FreeMarks then begin
|
|
m.MarkList := nil; // stop destroy from removing item from list
|
|
m.FMarkLine := nil; // stop destroy from removing item from self
|
|
m.Free
|
|
end else
|
|
m.MarkLine := nil;
|
|
end;
|
|
finally
|
|
dec(FLockChangeSize);
|
|
end;
|
|
ChangeSize;
|
|
end;
|
|
|
|
function TSynEditMarkLine.Count: Integer;
|
|
begin
|
|
Result := FMarks.Count;
|
|
end;
|
|
|
|
function TSynEditMarkLine.VisibleCount: Integer;
|
|
var
|
|
i: Integer;
|
|
begin
|
|
Result := 0;
|
|
for i := 0 to FMarks.Count - 1 do
|
|
if Items[i].Visible then
|
|
inc(Result);
|
|
end;
|
|
|
|
function TSynEditMarkLine.IndexOf(AMark: TSynEditMark): Integer;
|
|
begin
|
|
Result := FMarks.IndexOf(AMark);
|
|
end;
|
|
|
|
{ TSynEditMarkLineList }
|
|
|
|
function TSynEditMarkLineList.GetMark(Index : Integer): TSynEditMark;
|
|
var
|
|
MLine: TSynEditMarkLine;
|
|
i: Integer;
|
|
begin
|
|
(* If any Mark was added/removed from it's line then FLastIteratorIndex will be -1
|
|
If Items in the current line changed order then IsValidAndNotModified is False
|
|
If Items in another line changed order, it is valid to use the iterator
|
|
*)
|
|
if (FLastIteratorIndex >= 0) and
|
|
( (FLastIteratorIndex - 1 = Index) or // Return previous
|
|
(FLastIteratorIndex = Index) or // Return current
|
|
(FLastIteratorIndex + 1 = Index) ) and // Return next
|
|
(FIndexIterator.IsValidAndNotModified)
|
|
then begin
|
|
// can use iterator for quick access
|
|
if (FLastIteratorIndex + 1 = Index) then
|
|
FIndexIterator.Next
|
|
else if (FLastIteratorIndex - 1 = Index) then
|
|
FIndexIterator.Previous;
|
|
Result := FIndexIterator.Mark;
|
|
FLastIteratorIndex := Index;
|
|
exit;
|
|
end;
|
|
|
|
i := Index;
|
|
MLine := GetMarkLineByMarkIndex(i);
|
|
Result := MLine[i];
|
|
|
|
FIndexIterator.GotoMark(Result);
|
|
FLastIteratorIndex := Index;
|
|
end;
|
|
|
|
function TSynEditMarkLineList.GetMarkCount: Integer;
|
|
var
|
|
n: TSynEditMarkLine;
|
|
begin
|
|
n := TSynEditMarkLine(FRoot);
|
|
Result := 0;
|
|
while n <> nil do begin
|
|
Result := Result + n.LeftSizeSum + n.Size;
|
|
n := n.Right;
|
|
end;
|
|
end;
|
|
|
|
function TSynEditMarkLineList.GetMarkLineByMarkIndex(var AMarkIndex: Integer): TSynEditMarkLine;
|
|
var
|
|
d1, d2: Integer;
|
|
begin
|
|
Result := TSynEditMarkLine(FindNodeAtLeftSize(AMarkIndex, d1, d2));
|
|
AMarkIndex := AMarkIndex - d2;
|
|
end;
|
|
|
|
function TSynEditMarkLineList.GetMarkLine(LineNum: Integer): TSynEditMarkLine;
|
|
begin
|
|
Result := GetOrAddMarkLine(LineNum, False);
|
|
end;
|
|
|
|
function TSynEditMarkLineList.GetOrAddMarkLine(LineNum: Integer;
|
|
AddIfNotExist: Boolean; UseNext: Boolean = False): TSynEditMarkLine;
|
|
var
|
|
d1, d2: Integer;
|
|
begin
|
|
if AddIfNotExist then
|
|
Result := TSynEditMarkLine(FindNodeAtPosition(LineNum, afmCreate, d1, d2))
|
|
else
|
|
if UseNext then
|
|
Result := TSynEditMarkLine(FindNodeAtPosition(LineNum, afmNext, d1, d2))
|
|
else
|
|
Result := TSynEditMarkLine(FindNodeAtPosition(LineNum, afmNil, d1, d2));
|
|
end;
|
|
|
|
function TSynEditMarkLineList.CreateNode(APosition: Integer): TSynSizedDifferentialAVLNode;
|
|
begin
|
|
Result := TSynEditMarkLine.Create(APosition, Self);
|
|
end;
|
|
|
|
constructor TSynEditMarkLineList.Create(AOwner: TSynEditMarkList);
|
|
begin
|
|
inherited Create;
|
|
FMarkList := AOwner;
|
|
FIndexIterator := TSynEditMarkIterator.Create(FMarkList);
|
|
FLastIteratorIndex := -1;
|
|
end;
|
|
|
|
procedure TSynEditMarkLineList.SetMark(Index : Integer; const AValue: TSynEditMark);
|
|
var
|
|
l: TSynEditMarkLine;
|
|
begin
|
|
l := GetMarkLineByMarkIndex(Index);
|
|
l[Index] := AValue;
|
|
end;
|
|
|
|
destructor TSynEditMarkLineList.Destroy;
|
|
begin
|
|
Clear(True);
|
|
FreeAndNil(FIndexIterator);
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSynEditMarkLineList.Remove(Item: TSynEditMarkLine; FreeMarks: Boolean);
|
|
begin
|
|
if FInClear then
|
|
exit;
|
|
Item.Clear(FreeMarks);
|
|
RemoveNode(Item);
|
|
Item.Free;
|
|
end;
|
|
|
|
procedure TSynEditMarkLineList.Clear;
|
|
begin
|
|
Clear(False);
|
|
end;
|
|
|
|
procedure TSynEditMarkLineList.Clear(FreeMarks: Boolean);
|
|
procedure DeleteNode(ANode: TSynEditMarkLine);
|
|
begin
|
|
ANode.IncLockChangeSize;
|
|
ANode.Clear(FreeMarks);
|
|
if ANode.Left <> nil then DeleteNode(ANode.Left);
|
|
if ANode.Right <> nil then DeleteNode(ANode.Right);
|
|
DisposeNode(TSynSizedDifferentialAVLNode(ANode));
|
|
end;
|
|
begin
|
|
FInClear := True;
|
|
if FRoot <> nil then DeleteNode(TSynEditMarkLine(FRoot));
|
|
SetRoot(nil);
|
|
FInClear := False;
|
|
end;
|
|
|
|
function TSynEditMarkLineList.GetOrAddLine(LineNum: Integer): TSynEditMarkLine;
|
|
begin
|
|
Result := GetOrAddMarkLine(LineNum, True);
|
|
end;
|
|
|
|
function TSynEditMarkLineList.GetLineOrNext(LineNum: Integer): TSynEditMarkLine;
|
|
begin
|
|
Result := GetOrAddMarkLine(LineNum, False, True);
|
|
end;
|
|
|
|
function TSynEditMarkLineList.AddMark(Item: TSynEditMark): Integer;
|
|
var
|
|
l: TSynEditMarkLine;
|
|
begin
|
|
l := GetOrAddLine(Item.Line);
|
|
Result := l.Add(Item);
|
|
Result := Result + l.GetSizesBeforeSum;
|
|
end;
|
|
|
|
procedure TSynEditMarkLineList.DeleteMark(Index: Integer);
|
|
var
|
|
m: TSynEditMark;
|
|
begin
|
|
m := Mark[Index];
|
|
m.MarkLine.Remove(m)
|
|
end;
|
|
|
|
function TSynEditMarkLineList.RemoveMark(Item: TSynEditMark): Integer;
|
|
begin
|
|
if Item.MarkLine = nil then
|
|
exit(-1);
|
|
Result := Item.MarkLine.GetSizesBeforeSum;
|
|
Result := Result + Item.MarkLine.Remove(Item);
|
|
end;
|
|
|
|
function TSynEditMarkLineList.IndexOfMark(AMark: TSynEditMark): Integer;
|
|
var
|
|
l: TSynEditMarkLine;
|
|
begin
|
|
l := AMark.MarkLine;
|
|
Result := l.GetSizesBeforeSum + l.IndexOf(AMark);
|
|
end;
|
|
|
|
{ TSynEditMarkList }
|
|
|
|
procedure TSynEditMarkList.Add(Item: TSynEditMark);
|
|
begin
|
|
FMarkLines.AddMark(Item);
|
|
Item.MarkList := Self;
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.ClearLine(Line: integer);
|
|
var
|
|
l: TSynEditMarkLine;
|
|
begin
|
|
// TODO: move to marklinelist
|
|
l := FMarkLines.Lines[Line];
|
|
if l <> nil then
|
|
l.Clear(True);
|
|
end;
|
|
|
|
constructor TSynEditMarkList.Create(AOwner: TSynEditBase;
|
|
ALines: TSynEditStringsLinked);
|
|
begin
|
|
FOwnerList := TFPList.Create;
|
|
FOwnerList.Add(AOwner);
|
|
FMarkLines := TSynEditMarkLineList.Create(Self);
|
|
FChangeHandlers := TSynEditMarkChangedHandlerList.Create;
|
|
inherited Create;
|
|
FLines := ALines;
|
|
FLines.AddEditHandler(@DoLinesEdited);
|
|
FInternalIterator := TSynEditMarkIterator.Create(Self);
|
|
end;
|
|
|
|
destructor TSynEditMarkList.Destroy;
|
|
begin
|
|
FLines.RemoveEditHandler(@DoLinesEdited);
|
|
inherited Destroy;
|
|
// Todo: clear changehandlers first?
|
|
FreeAndNil(FMarkLines); // will free all Marks
|
|
FreeAndNil(FChangeHandlers);
|
|
FreeAndNil(FInternalIterator);
|
|
FreeAndNil(FOwnerList);
|
|
end;
|
|
|
|
{$IFDEF SynDebug}
|
|
procedure TSynEditMarkList.Debug;
|
|
begin
|
|
FMarkLines.Debug;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
function TSynEditMarkList.GetMarkLine(LineNum: Integer): TSynEditMarkLine;
|
|
begin
|
|
Result := FMarkLines.Lines[LineNum];
|
|
end;
|
|
|
|
procedure TSynEditMarkList.DoChange;
|
|
begin
|
|
if Assigned(FOnChange) then
|
|
FOnChange(Self);
|
|
end;
|
|
|
|
procedure TSynEditMarkList.MarkChanged(Sender: TSynEditMark;
|
|
AChanges: TSynEditMarkChangeReasons);
|
|
begin
|
|
FChangeHandlers.CallMarkChangedHandlers(Sender, AChanges);
|
|
end;
|
|
|
|
function TSynEditMarkList.Get(Index: Integer): TSynEditMark;
|
|
begin
|
|
Result := FMarkLines.Mark[Index];
|
|
end;
|
|
|
|
//Returns up to maxMarks book/gutter marks for a chosen line.
|
|
|
|
procedure TSynEditMarkList.Delete(Index: Integer);
|
|
var
|
|
Mrk: TSynEditMark;
|
|
begin
|
|
Mrk := FMarkLines.Mark[Index];
|
|
Mrk.MarkList := Self;
|
|
FMarkLines.RemoveMark(Mrk);
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.Put(Index: Integer; Item: TSynEditMark);
|
|
begin
|
|
FMarkLines.Mark[Index] := Item;
|
|
Item.MarkList := Self;
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
|
|
aLineBrkCnt: Integer; aText: String);
|
|
var
|
|
i: Integer;
|
|
CurLine, NextLine: TSynEditMarkLine;
|
|
LinePos, LineBSize: Integer;
|
|
f: Boolean;
|
|
Mrk: TSynEditMark;
|
|
begin
|
|
CurLine := FMarkLines.GetLineOrNext(aLinePos);
|
|
if (CurLine = nil) then
|
|
exit;
|
|
|
|
LinePos := CurLine.LineNum;
|
|
LineBSize := 0;
|
|
FInternalIterator.Invalidate; // TODO: better notification system
|
|
|
|
if aLineBrkCnt > 0 then begin
|
|
FMarkLines.AdjustForLinesInserted(aLinePos + 1, aLineBrkCnt);
|
|
FInternalIterator.GotoMark(CurLine[0]); // TODO: better notification system
|
|
if (LinePos = aLinePos) then begin
|
|
i := CurLine.Count - 1;
|
|
while i >= 0 do begin
|
|
Mrk := CurLine.Items[i];
|
|
if (Mrk.Column > aBytePos) then begin
|
|
Mrk.Line := Mrk.Line + aLineBrkCnt;
|
|
Mrk.Column := Mrk.Column - aBytePos + 1;
|
|
end;
|
|
dec(i);
|
|
end;
|
|
end;
|
|
end
|
|
|
|
else
|
|
if aLineBrkCnt < 0 then begin
|
|
if (LinePos = aLinePos) then
|
|
CurLine := TSynEditMarkLine(CurLine.Successor(LinePos, LineBSize));
|
|
while (CurLine <> nil) and (LinePos <= aLinePos - aLineBrkCnt) do begin
|
|
f := LinePos = aLinePos - aLineBrkCnt;
|
|
NextLine := TSynEditMarkLine(CurLine.Successor(LinePos, LineBSize));
|
|
i := CurLine.Count - 1;
|
|
while i >= 0 do begin
|
|
Mrk := CurLine.Items[i];
|
|
Mrk.Line := aLinePos;
|
|
if f then
|
|
Mrk.Column := Mrk.Column + aBytePos - 1
|
|
else
|
|
Mrk.Column := aBytePos; // or delete ?
|
|
dec(i);
|
|
end;
|
|
CurLine := NextLine;
|
|
end;
|
|
if CurLine <> nil then FInternalIterator.GotoMark(CurLine[0]); // TODO: better notification system
|
|
FMarkLines.AdjustForLinesDeleted(aLinePos + 1, -aLineBrkCnt);
|
|
end
|
|
|
|
else
|
|
if aLinePos = LinePos then begin
|
|
if aCount > 0 then begin
|
|
for i := 0 to CurLine.Count - 1 do
|
|
if (CurLine.Items[i].Column > aBytePos) then
|
|
CurLine.Items[i].Column := CurLine.Items[i].Column + aCount
|
|
end
|
|
else if aCount < 0 then begin
|
|
for i := 0 to CurLine.Count - 1 do
|
|
if (CurLine.Items[i].Column > aBytePos) then
|
|
CurLine.Items[i].Column := Max(aBytePos, CurLine.Items[i].Column + aCount);
|
|
end;
|
|
end;
|
|
|
|
if FInternalIterator.Mark <> nil then begin // TODO: better notification system
|
|
repeat
|
|
FInternalIterator.Mark.DoChange([smcrLine]);
|
|
until not FInternalIterator.Next;
|
|
end;
|
|
|
|
end;
|
|
|
|
function TSynEditMarkList.HasOwnerEdit(AEdit: TSynEditBase): Boolean;
|
|
begin
|
|
Result := FOwnerList.IndexOf(AEdit) >= 0;
|
|
end;
|
|
|
|
function TSynEditMarkList.Remove(Item: TSynEditMark): Integer;
|
|
begin
|
|
Item.MarkList := nil;
|
|
Result := FMarkLines.RemoveMark(Item);
|
|
DoChange;
|
|
end;
|
|
|
|
function TSynEditMarkList.IndexOf(Item: TSynEditMark): Integer;
|
|
begin
|
|
Result := FMarkLines.IndexOfMark(Item);
|
|
end;
|
|
|
|
function TSynEditMarkList.Count: Integer;
|
|
begin
|
|
Result := FMarkLines.MarkCount;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.RegisterChangeHandler(Handler: TSynEditMarkChangeEvent;
|
|
Filter: TSynEditMarkChangeReasons);
|
|
begin
|
|
FChangeHandlers.Add(Handler, Filter);
|
|
end;
|
|
|
|
procedure TSynEditMarkList.UnRegisterChangeHandler(Handler: TSynEditMarkChangeEvent);
|
|
begin
|
|
FChangeHandlers.Remove(Handler);
|
|
end;
|
|
|
|
{ TSynEditMarkChangedHandlerList }
|
|
|
|
procedure TSynEditMarkChangedHandlerList.Add(AHandler: TSynEditMarkChangeEvent;
|
|
Changes: TSynEditMarkChangeReasons);
|
|
begin
|
|
AddBitFilter(TMethod(AHandler), LongInt(Changes));
|
|
end;
|
|
|
|
procedure TSynEditMarkChangedHandlerList.Remove(AHandler: TSynEditMarkChangeEvent);
|
|
begin
|
|
inherited Remove(TMethod(AHandler));
|
|
end;
|
|
|
|
procedure TSynEditMarkChangedHandlerList.CallMarkChangedHandlers(Sender: TSynEditMark;
|
|
Changes: TSynEditMarkChangeReasons);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
i:=Count;
|
|
while NextDownIndexBitFilter(i, LongInt(Changes)) do
|
|
TSynEditMarkChangeEvent(FItems[i].FHandler)(Sender, Changes);
|
|
end;
|
|
|
|
{ TSynEditMarkIterator }
|
|
|
|
procedure TSynEditMarkIterator.GotoBOL;
|
|
begin
|
|
FBOL := True;
|
|
FEOL := False;
|
|
FCurrentItem := nil;
|
|
end;
|
|
|
|
procedure TSynEditMarkIterator.GotoEOL;
|
|
begin
|
|
FBOL := False;
|
|
FEOL := True;
|
|
FCurrentItem := nil;
|
|
end;
|
|
|
|
constructor TSynEditMarkIterator.Create(AOwner: TSynEditMarkList);
|
|
begin
|
|
inherited Create;
|
|
FMarkList := AOwner;
|
|
FCurrentItem := nil;
|
|
FBOL := False;
|
|
FEOL := False;
|
|
end;
|
|
|
|
procedure TSynEditMarkIterator.Invalidate;
|
|
begin
|
|
FCurrentItem := nil;
|
|
FBOL := False;
|
|
FEOL := False;
|
|
end;
|
|
|
|
procedure TSynEditMarkIterator.GotoMark(AMark: TSynEditMark);
|
|
begin
|
|
if (AMark <> nil) and (AMark.FMarkList <> FMarkList) then
|
|
raise Exception.Create('Invalid list');
|
|
|
|
FCurrentItem := AMark;
|
|
FBOL := False;
|
|
FEOL := False;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.First: Boolean;
|
|
var
|
|
ML: TSynEditMarkLine;
|
|
begin
|
|
ML := TSynEditMarkLine(FMarkList.FMarkLines.First);
|
|
if ML <> nil then
|
|
FCurrentItem := ML[0]
|
|
else
|
|
FCurrentItem := nil;
|
|
FCurrentIndex := 0;
|
|
FBOL := FCurrentItem = nil;
|
|
FEOL := FCurrentItem = nil;
|
|
Result := FCurrentItem <> nil;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.Last: Boolean;
|
|
var
|
|
ML: TSynEditMarkLine;
|
|
begin
|
|
ML := TSynEditMarkLine(FMarkList.FMarkLines.Last);
|
|
if ML <> nil then
|
|
FCurrentItem := ML[ML.Count - 1]
|
|
else
|
|
FCurrentItem := nil;
|
|
FCurrentIndex := -1;
|
|
FBOL := FCurrentItem = nil;
|
|
FEOL := FCurrentItem = nil;
|
|
Result := FCurrentItem <> nil;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.Next: Boolean;
|
|
var
|
|
ML: TSynEditMarkLine;
|
|
begin
|
|
if FBOL then begin
|
|
First;
|
|
Result := FCurrentItem <> nil;
|
|
exit;
|
|
end
|
|
else if FCurrentItem = nil then begin
|
|
FBOL := False;
|
|
FEOL := False;
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
|
|
if (FCurrentIndex < 0) or (FCurrentIndex >= FCurrentItem.FMarkLine.Count) or
|
|
(FCurrentItem.FMarkLine[FCurrentIndex] <> FCurrentItem)
|
|
then
|
|
FCurrentIndex := FCurrentItem.FMarkLine.IndexOf(FCurrentItem);
|
|
|
|
inc(FCurrentIndex);
|
|
if FCurrentIndex < FCurrentItem.FMarkLine.Count then begin
|
|
FCurrentItem := FCurrentItem.FMarkLine[FCurrentIndex];
|
|
Result := True;
|
|
end else begin
|
|
ML := TSynEditMarkLine(FCurrentItem.FMarkLine.Successor);
|
|
if ML <> nil then begin
|
|
FCurrentIndex := 0;
|
|
FCurrentItem := ML[FCurrentIndex];
|
|
Result := True;
|
|
end else begin
|
|
FCurrentItem := nil;
|
|
FEOL := True;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.Previous: Boolean;
|
|
var
|
|
ML: TSynEditMarkLine;
|
|
begin
|
|
if FEOL then begin
|
|
Last;
|
|
Result := FCurrentItem <> nil;
|
|
exit;
|
|
end
|
|
else if FCurrentItem = nil then begin
|
|
FBOL := False;
|
|
FEOL := False;
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
|
|
if (FCurrentIndex < 0) or (FCurrentIndex >= FCurrentItem.FMarkLine.Count) or
|
|
(FCurrentItem.FMarkLine[FCurrentIndex] <> FCurrentItem)
|
|
then
|
|
FCurrentIndex := FCurrentItem.FMarkLine.IndexOf(FCurrentItem);
|
|
|
|
dec(FCurrentIndex);
|
|
if FCurrentIndex >= 0 then begin
|
|
FCurrentItem := FCurrentItem.FMarkLine[FCurrentIndex];
|
|
Result := True;
|
|
end else begin
|
|
ML := TSynEditMarkLine(FCurrentItem.FMarkLine.Precessor);
|
|
if ML <> nil then begin
|
|
FCurrentIndex := ML.Count - 1;
|
|
FCurrentItem := ML[FCurrentIndex];
|
|
Result := True;
|
|
end else begin
|
|
FCurrentItem := nil;
|
|
FBOL := True;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.NextLine: Boolean;
|
|
var
|
|
ML: TSynEditMarkLine;
|
|
begin
|
|
if FBOL then begin
|
|
First;
|
|
Result := FCurrentItem <> nil;
|
|
exit;
|
|
end
|
|
else if FCurrentItem = nil then begin
|
|
FBOL := False;
|
|
FEOL := False;
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
|
|
ML := TSynEditMarkLine(FCurrentItem.FMarkLine.Successor);
|
|
if ML <> nil then begin
|
|
FCurrentIndex := 0;
|
|
FCurrentItem := ML[FCurrentIndex];
|
|
Result := True;
|
|
end else begin
|
|
FCurrentItem := nil;
|
|
FEOL := True;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.PreviousLine: Boolean;
|
|
var
|
|
ML: TSynEditMarkLine;
|
|
begin
|
|
if FEOL then begin
|
|
Last;
|
|
Result := FCurrentItem <> nil;
|
|
exit;
|
|
end
|
|
else if FCurrentItem = nil then begin
|
|
FBOL := False;
|
|
FEOL := False;
|
|
Result := False;
|
|
exit;
|
|
end;
|
|
|
|
ML := TSynEditMarkLine(FCurrentItem.FMarkLine.Precessor);
|
|
if ML <> nil then begin
|
|
FCurrentIndex := ML.Count - 1;
|
|
FCurrentItem := ML[FCurrentIndex];
|
|
Result := True;
|
|
end else begin
|
|
FCurrentItem := nil;
|
|
FBOL := True;
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.IsValid: Boolean;
|
|
begin
|
|
Result := (FCurrentItem <> nil) or FBOL or FEOL;
|
|
end;
|
|
|
|
function TSynEditMarkIterator.IsValidAndNotModified: Boolean;
|
|
begin
|
|
Result := IsValid and
|
|
(FCurrentIndex >= 0) and (FCurrentIndex < FCurrentItem.FMarkLine.Count) and
|
|
(FCurrentItem.FMarkLine[FCurrentIndex] = FCurrentItem);
|
|
end;
|
|
|
|
end.
|
|
|