mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-15 06:02:46 +02:00
393 lines
9.9 KiB
ObjectPascal
393 lines
9.9 KiB
ObjectPascal
unit SynEditMarks;
|
|
|
|
{$I synedit.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, math, SynEditMiscClasses, SynEditTextBase;
|
|
|
|
const
|
|
// Max number of book/gutter marks returned from GetEditMarksForLine - that
|
|
// really should be enough.
|
|
maxMarks = 16;
|
|
|
|
type
|
|
|
|
{ TSynEditMark }
|
|
|
|
TSynEditMark = class
|
|
protected
|
|
FLine, FColumn, FImage, FPriority: Integer;
|
|
FEdit: TSynEditBase;
|
|
FVisible: boolean;
|
|
FInternalImage: boolean;
|
|
FBookmarkNum: integer;
|
|
function GetEdit: TSynEditBase; virtual;
|
|
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; //MWE: Laz needs to know when a line gets visible, so the editor color can be updated
|
|
procedure SetInternalImage(const Value: boolean);
|
|
function GetIsBookmark: boolean;
|
|
public
|
|
constructor Create(AOwner: TSynEditBase);
|
|
property Line: integer read FLine write SetLine;
|
|
property Column: integer read FColumn write SetColumn;
|
|
property Priority: integer read FPriority write SetPriority;
|
|
property ImageIndex: integer read FImage write SetImage;
|
|
property BookmarkNumber: integer read FBookmarkNum write fBookmarkNum;
|
|
property Visible: boolean read FVisible write SetVisible;
|
|
property InternalImage: boolean read FInternalImage write SetInternalImage;
|
|
property IsBookmark: boolean read GetIsBookmark;
|
|
end;
|
|
|
|
TPlaceMarkEvent = procedure(Sender: TObject; var Mark: TSynEditMark) of object;
|
|
|
|
TSynEditMarks = array[1..maxMarks] of TSynEditMark;
|
|
|
|
{ A list of mark objects. Each object cause a litle picture to be drawn in the
|
|
gutter. }
|
|
|
|
{ TSynEditMarkList }
|
|
|
|
TSynEditMarkList = class(TList)
|
|
protected
|
|
FEdit: TSynEditBase;
|
|
FLines: TSynEditStrings;
|
|
fOnChange: TNotifyEvent;
|
|
procedure DoChange;
|
|
function Get(Index: Integer): TSynEditMark;
|
|
procedure Put(Index: Integer; Item: TSynEditMark);
|
|
procedure DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
|
|
aLineBrkCnt: Integer; aText: String);
|
|
public
|
|
constructor Create(AOwner: TSynEditBase; ALines: TSynEditStrings);
|
|
destructor Destroy; override;
|
|
function Add(Item: TSynEditMark): Integer;
|
|
procedure ClearLine(line: integer);
|
|
procedure Delete(Index: Integer);
|
|
function First: TSynEditMark;
|
|
procedure GetMarksForLine(line: integer; var Marks: TSynEditMarks);
|
|
procedure Insert(Index: Integer; Item: TSynEditMark);
|
|
function Last: TSynEditMark;
|
|
procedure Place(Mark: TSynEditMark);
|
|
function Remove(Item: TSynEditMark): Integer;
|
|
public
|
|
property Items[Index: Integer]: TSynEditMark read Get write Put; default;
|
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
|
end;
|
|
|
|
function DoMarksCompareBookmarksFirst(Item1, Item2: Pointer): Integer;
|
|
function DoMarksCompareBookmarksLast(Item1, Item2: Pointer): Integer;
|
|
|
|
implementation
|
|
uses SynEdit;
|
|
|
|
type // This is until InvalidateGutterLines, can be moved to an accessible place
|
|
SynEditAccess = Class(TCustomSynEdit);
|
|
|
|
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 := 1
|
|
else
|
|
if Mark1.Priority > Mark2.Priority then
|
|
Result := -1;
|
|
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 := 1
|
|
else
|
|
if Mark1.Priority > Mark2.Priority then
|
|
Result := -1;
|
|
end;
|
|
|
|
procedure SortMarks(var Marks: TSynEditMarks; Compare: TListSortCompare);
|
|
var
|
|
i, j, LastMark: Integer;
|
|
P: Pointer;
|
|
begin
|
|
for i := Low(Marks) to High(Marks) do
|
|
if Marks[i] = nil then
|
|
begin
|
|
LastMark := i - 1;
|
|
break;
|
|
end;
|
|
// insert sort is the best for our items count
|
|
for i := Low(Marks) + 1 to LastMark do
|
|
begin
|
|
P := Marks[i];
|
|
j := i - 1;
|
|
while (j >= Low(Marks)) and (Compare(P, Marks[j]) < 1) do
|
|
begin
|
|
Marks[j + 1] := Marks[j];
|
|
j := j - 1;
|
|
end;
|
|
Marks[j + 1] := TSynEditMark(P);
|
|
end;
|
|
end;
|
|
|
|
{ TSynEditMark }
|
|
|
|
procedure TSynEditMark.SetPriority(const AValue: integer);
|
|
begin
|
|
FPriority := AValue;
|
|
end;
|
|
|
|
function TSynEditMark.GetEdit: TSynEditBase;
|
|
begin
|
|
if FEdit <> nil then try
|
|
if TCustomSynEdit(FEdit).Marks.IndexOf(self) = -1 then
|
|
FEdit := nil;
|
|
except
|
|
FEdit := nil;
|
|
end;
|
|
Result := FEdit;
|
|
end;
|
|
|
|
function TSynEditMark.GetIsBookmark: boolean;
|
|
begin
|
|
Result := (fBookmarkNum >= 0);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetColumn(const Value: Integer);
|
|
begin
|
|
FColumn := Value;
|
|
end;
|
|
|
|
procedure TSynEditMark.SetImage(const Value: Integer);
|
|
begin
|
|
FImage := Value;
|
|
if FVisible and Assigned(FEdit) then
|
|
SynEditAccess(Pointer(FEdit)).InvalidateGutterLines(FLine, FLine);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetInternalImage(const Value: boolean);
|
|
begin
|
|
FInternalImage := Value;
|
|
if FVisible and Assigned(FEdit) then
|
|
SynEditAccess(Pointer(FEdit)).InvalidateGutterLines(FLine, FLine);
|
|
end;
|
|
|
|
procedure TSynEditMark.SetLine(const Value: Integer);
|
|
begin
|
|
if FVisible and Assigned(FEdit) then
|
|
begin
|
|
if FLine > 0 then
|
|
SynEditAccess(Pointer(FEdit)).InvalidateGutterLines(FLine, FLine);
|
|
FLine := Value;
|
|
SynEditAccess(Pointer(FEdit)).InvalidateGutterLines(FLine, FLine);
|
|
end else
|
|
FLine := Value;
|
|
end;
|
|
|
|
procedure TSynEditMark.SetVisible(const Value: boolean);
|
|
begin
|
|
if FVisible <> Value then
|
|
begin
|
|
FVisible := Value;
|
|
if Assigned(FEdit) then
|
|
SynEditAccess(Pointer(FEdit)).InvalidateGutterLines(FLine, FLine);
|
|
end;
|
|
end;
|
|
|
|
constructor TSynEditMark.Create(AOwner: TSynEditBase);
|
|
begin
|
|
inherited Create;
|
|
FBookmarkNum := -1;
|
|
FEdit := AOwner;
|
|
FPriority := 0;
|
|
end;
|
|
|
|
{ TSynEditMarkList }
|
|
|
|
function TSynEditMarkList.Add(Item: TSynEditMark): Integer;
|
|
begin
|
|
Result := inherited Add(Item);
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.ClearLine(Line: integer);
|
|
var
|
|
i: integer;
|
|
begin
|
|
for i := Count - 1 downto 0 do
|
|
if not Items[i].IsBookmark and (Items[i].Line = Line) then Delete(i);
|
|
end;
|
|
|
|
constructor TSynEditMarkList.Create(AOwner: TSynEditBase; ALines: TSynEditStrings);
|
|
begin
|
|
inherited Create;
|
|
FEdit := AOwner;
|
|
FLines := ALines;
|
|
FLines.AddEditHandler(@DoLinesEdited);
|
|
end;
|
|
|
|
destructor TSynEditMarkList.Destroy;
|
|
var
|
|
i: integer;
|
|
begin
|
|
FLines.RemoveEditHandler(@DoLinesEdited);
|
|
for i := 0 to Pred(Count) do
|
|
Get(i).Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.Delete(Index: Integer);
|
|
begin
|
|
inherited Delete(Index);
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.DoChange;
|
|
begin
|
|
if Assigned(FOnChange) then
|
|
FOnChange(Self);
|
|
end;
|
|
|
|
function TSynEditMarkList.First: TSynEditMark;
|
|
begin
|
|
Result := TSynEditMark(inherited First);
|
|
end;
|
|
|
|
function TSynEditMarkList.Get(Index: Integer): TSynEditMark;
|
|
begin
|
|
Result := TSynEditMark(inherited Get(Index));
|
|
end;
|
|
|
|
//Returns up to maxMarks book/gutter marks for a chosen line.
|
|
|
|
procedure TSynEditMarkList.GetMarksForLine(line: integer;
|
|
var marks: TSynEditMarks);
|
|
var
|
|
cnt: integer;
|
|
i: integer;
|
|
begin
|
|
FillChar(marks, SizeOf(marks), 0);
|
|
cnt := 0;
|
|
for i := 0 to Count - 1 do
|
|
begin
|
|
if Items[i].Line = line then
|
|
begin
|
|
Inc(cnt);
|
|
marks[cnt] := Items[i];
|
|
if cnt = maxMarks then break;
|
|
end;
|
|
end;
|
|
if Assigned(FEdit) then
|
|
if TSynEdit(FEdit).BookMarkOptions.DrawBookmarksFirst then
|
|
SortMarks(marks, @DoMarksCompareBookmarksFirst)
|
|
else
|
|
SortMarks(marks, @DoMarksCompareBookmarksLast);
|
|
end;
|
|
|
|
procedure TSynEditMarkList.Insert(Index: Integer; Item: TSynEditMark);
|
|
begin
|
|
inherited Insert(Index, Item);
|
|
DoChange;
|
|
end;
|
|
|
|
function TSynEditMarkList.Last: TSynEditMark;
|
|
begin
|
|
Result := TSynEditMark(inherited Last);
|
|
end;
|
|
|
|
procedure TSynEditMarkList.Place(mark: TSynEditMark);
|
|
begin
|
|
if assigned(FEdit) then
|
|
if assigned(TSynEdit(FEdit).OnPlaceBookmark) then
|
|
TSynEdit(FEdit).OnPlaceBookmark(TSynEdit(FEdit), mark);
|
|
if assigned(mark) then
|
|
Add(mark);
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.Put(Index: Integer; Item: TSynEditMark);
|
|
begin
|
|
inherited Put(Index, Item);
|
|
DoChange;
|
|
end;
|
|
|
|
procedure TSynEditMarkList.DoLinesEdited(Sender: TSynEditStrings; aLinePos, aBytePos, aCount,
|
|
aLineBrkCnt: Integer; aText: String);
|
|
var
|
|
i, Col: Integer;
|
|
begin
|
|
if Count = 0 then exit;
|
|
Col := FLines.LogicalToPhysicalPos(Point(aBytePos, aLinePos)).x;
|
|
if aLineBrkCnt > 0 then
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
if (Items[i].Line > aLinePos) then
|
|
Items[i].Line := Items[i].Line + aLineBrkCnt
|
|
else
|
|
if (Items[i].Line = aLinePos) and (Items[i].Column > col) then begin
|
|
Items[i].Line := Items[i].Line + aLineBrkCnt;
|
|
Items[i].Column := Items[i].Column - Col + 1;
|
|
end;
|
|
end
|
|
else
|
|
if aLineBrkCnt < 0 then
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
if (Items[i].Line > aLinePos - aLineBrkCnt) then
|
|
Items[i].Line := Items[i].Line + aLineBrkCnt
|
|
else
|
|
if (Items[i].Line > aLinePos) then begin
|
|
Items[i].Line := aLinePos;
|
|
Items[i].Column := Items[i].Column + Col - 1;
|
|
end;
|
|
end
|
|
else
|
|
if aCount > 0 then
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
if (Items[i].Line = aLinePos) and (Items[i].Column > col) then
|
|
Items[i].Column := Items[i].Column + aCount;
|
|
end
|
|
else
|
|
if aCount < 0 then
|
|
begin
|
|
for i := 0 to Count - 1 do
|
|
if (Items[i].Line = aLinePos) and (Items[i].Column > col) then
|
|
Items[i].Column := Max(Col, Items[i].Column + aCount);
|
|
end;
|
|
end;
|
|
|
|
function TSynEditMarkList.Remove(Item: TSynEditMark): Integer;
|
|
begin
|
|
Result := inherited Remove(Item);
|
|
DoChange;
|
|
end;
|
|
|
|
end.
|
|
|