lazarus/components/synedit/syngutterlineoverview.pp
2025-03-01 13:28:22 +01:00

1586 lines
44 KiB
ObjectPascal

{-------------------------------------------------------------------------------
The contents of this file are subject to the Mozilla Public License
Version 1.1 (the "License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
the specific language governing rights and limitations under the License.
Alternatively, the contents of this file may be used under the terms of the
GNU General Public License Version 2 or later (the "GPL"), in which case
the provisions of the GPL are applicable instead of those above.
If you wish to allow use of your version of this file only under the terms
of the GPL and not to allow others to use your version of this file
under the MPL, indicate your decision by deleting the provisions above and
replace them with the notice and other provisions required by the GPL.
If you do not delete the provisions above, a recipient may use your version
of this file under either the MPL or the GPL.
-------------------------------------------------------------------------------}
unit SynGutterLineOverview;
{$I synedit.inc}
interface
uses
Classes, sysutils, math, FPCanvas,
// LCL
Graphics, Controls, Forms, LCLType, LCLIntf, LMessages,
// LazUtils
LazUtilities,
// SynEdit
SynGutterBase, SynEditTypes, LazSynEditText, SynEditTextBuffer, SynEditMarks,
SynEditMiscClasses, SynEditMouseCmds;
type
TSynGutterLineOverview = class;
TSynGutterLineOverviewProviderList = class;
TSynGutterLOvLineMarks = class;
{ TSynGutterLOvMark }
TSynGutterLOvMark = class
private
FColor: TColor;
FColumn: Integer;
FLine: Integer;
FLineMarks: TSynGutterLOvLineMarks;
FOnChange: TNotifyEvent;
FOnDestroy: TNotifyEvent;
FPriority: Integer;
protected
FData: Pointer;
protected
// for TSynGutterLOvLineMarks
function CompareByPrior(Other: TSynGutterLOvMark): Integer;
function CompareByLine(Other: TSynGutterLOvMark): Integer;
procedure DoChange;
property LineMarks: TSynGutterLOvLineMarks read FLineMarks write FLineMarks;
public
destructor Destroy; override;
property Line: Integer read FLine;
property Column: Integer read FColumn;
property Color: TColor read FColor;
property Priority: Integer read FPriority;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
end;
{ TSynGutterLOvMarkList }
TSynGutterLOvMarkList = class(TFPList)
private
FOwnMarks: Boolean;
FLockCount: Integer;
FNeedSort: Boolean;
function GetMark(Index: Integer): TSynGutterLOvMark;
procedure PutMark(Index: Integer; const AValue: TSynGutterLOvMark);
protected
procedure ReSort; virtual;
public
constructor Create;
destructor Destroy; override;
procedure Lock;
procedure UnLock;
procedure Add(AValue: TSynGutterLOvMark); virtual;
property Items[Index: Integer]: TSynGutterLOvMark read GetMark write PutMark; default;
end;
{ TSynGutterLOvLineMarks
List of TSynGutterLOvMark on a given Line (PixelLine)
}
TSynGutterLOvLineMarks = class(TSynGutterLOvMarkList)
private
FPixLine: Integer;
protected
procedure ReSort; override; // by prior
procedure ReSortByLine;
function Compare(Other: TSynGutterLOvLineMarks): Integer;
public
constructor Create;
procedure Paint(Canvas: TCanvas; AClip: TRect; TopOffset: integer; AnItemHeight: Integer);
procedure Add(AValue: TSynGutterLOvMark); override;
property PixLine: Integer read FPixLine;
end;
{ TSynGutterLOvLineMarksList
List of (Pixel-)Lines with Marks
}
TSynGutterLOvLineMarksList = class(TFPList)
private
FItemHeight: Integer;
FPixelHeight: Integer;
FPixelPerLine: Integer;
FTextLineCount: Integer;
function GetLineMarks(Index: Integer): TSynGutterLOvLineMarks;
procedure PutLineMarks(Index: Integer; const AValue: TSynGutterLOvLineMarks);
function ItemForLine(ALine: Integer; CreateIfNotExists: Boolean = False): TSynGutterLOvLineMarks;
procedure SetItemHeight(const AValue: Integer);
procedure SetPixelHeight(const AValue: Integer);
procedure SetTextLineCount(const AValue: Integer);
protected
function IndexForLine(ALine: Integer; PreviousIfNotExist: Boolean = False;
UseItemHeight: Boolean = False): Integer;
procedure ReSort;
procedure MarkChanged(Sender: TObject);
procedure MarkDestroying(Sender: TObject);
function TextLineToPixLine(ATxtLine: Integer): Integer;
procedure ReBuild(AFromIndex: Integer = -1);
public
constructor Create;
destructor Destroy; override;
procedure AddMark(AMark: TSynGutterLOvMark);
property Items[Index: Integer]: TSynGutterLOvLineMarks
read GetLineMarks write PutLineMarks; default;
property PixelHeight: Integer read FPixelHeight write SetPixelHeight;
property TextLineCount: Integer read FTextLineCount write SetTextLineCount;
property ItemHeight: Integer read FItemHeight write SetItemHeight;
end;
{ TSynGutterLineOverviewProvider }
TSynGutterLineOverviewProvider = class(TSynObjectListItem)
private
FColor: TColor;
FHeight: Integer;
FGutterPart: TSynGutterLineOverview;
FPriority: Integer;
FRGBColor: TColor;
function GetList: TSynGutterLineOverviewProviderList;
procedure SetColor(const AValue: TColor);
procedure SetHeight(const AValue: Integer);
procedure SetPriority(const AValue: Integer);
protected
function Compare(Other: TSynObjectListItem): Integer; override;
procedure DoChange(Sender: TObject);
procedure InvalidateTextLines(AFromLine, AToLine: Integer);
procedure InvalidatePixelLines(AFromLine, AToLine: Integer);
function TextLineToPixel(ALine: Integer): Integer;
function TextLineToPixelEnd(ALine: Integer): Integer;
function PixelLineToText(ALineIdx: Integer): Integer;
procedure ReCalc; virtual; // Does not invalidate
function SynEdit: TSynEditBase;
property Owner: TSynGutterLineOverviewProviderList read GetList; //the list
property GutterPart: TSynGutterLineOverview read FGutterPart;
property RGBColor: TColor read FRGBColor;
procedure Paint(Canvas: TCanvas; AClip: TRect; TopOffset: integer); virtual;
private
FMarkList: TSynGutterLOvMarkList;
function GetMarks(AnIndex: Integer): TSynGutterLOvMark; virtual;
protected
function MarksCount: Integer;
property Marks[AnIndex: Integer]: TSynGutterLOvMark read GetMarks;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property Height: Integer read FHeight write SetHeight;
published
property Priority: Integer read FPriority write SetPriority;
property Color: TColor read FColor write SetColor;
end;
{ TSynGutterLineOverviewProviderList }
TSynGutterLineOverviewProviderList = class(TSynObjectList)
private
function GetGutterPart: TSynGutterLineOverview;
function GetProviders(AIndex: Integer): TSynGutterLineOverviewProvider;
public
constructor Create(AOwner: TComponent); override;
property Owner: TSynGutterLineOverview read GetGutterPart;
property Providers[AIndex: Integer]: TSynGutterLineOverviewProvider
read GetProviders; default;
end;
{ TSynGutterLOvProviderCurrentPage }
TSynGutterLOvProviderCurrentPage = class(TSynGutterLineOverviewProvider)
private
FCurTopLine, FCurBottomLine: Integer;
FPixelTopLine, FPixelBottomLine: Integer;
protected
procedure SynStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
procedure FoldChanged(Sender: TSynEditStrings; aIndex, aCount: Integer);
procedure Paint(Canvas: TCanvas; AClip: TRect; TopOffset: integer); override;
procedure ReCalc; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
{ TSynGutterLOvProviderModifiedLines }
TSynGutterLOvProviderModifiedLines = class(TSynGutterLineOverviewProvider)
private
FColorSaved, FRGBColorSaved: TColor;
FPixLineStates: Array of TSynEditStringFlags;
FFirstTextLineChanged, FLastTextLineChanged: Integer;
procedure SetColorSaved(const AValue: TColor);
protected
procedure Paint(Canvas: TCanvas; AClip: TRect; TopOffset: integer); override;
procedure ReScan;
procedure ReCalc; override;
procedure LineModified(Sender: TSynEditStrings; aIndex, aNewCount, aOldCount: Integer);
procedure SynStatusChanged(Sender: TObject; Changes: TSynStatusChanges);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property ColorSaved: TColor read FColorSaved write SetColorSaved;
end;
{ TSynGutterLOvProviderBookmarks }
TSynGutterLOvProviderBookmarks = class;
TSynGutterColorforMarkEvent = procedure(ASender: TSynGutterLOvProviderBookmarks;
AMark: TSynEditMark;
var AColor: TColor;
var APriority: Integer) of object;
TSynGutterLOvProviderBookmarks = class(TSynGutterLineOverviewProvider)
private
FOnColorForMark: TSynGutterColorforMarkEvent;
protected
procedure AdjustColorForMark(AMark: TSynEditMark; var AColor: TColor; var APriority: Integer); virtual;
procedure DoMarkChange(Sender: TSynEditMark; Changes: TSynEditMarkChangeReasons);
function CreateGutterMark(ASynMark: TSynEditMark): TSynGutterLOvMark;
function IndexOfSynMark(ASynMark: TSynEditMark): Integer;
procedure BufferChanging(Sender: TObject);
procedure BufferChanged(Sender: TObject);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property OnColorForMark: TSynGutterColorforMarkEvent read FOnColorForMark write FOnColorForMark;
end;
TSynGutterLOvProviderCustom = class(TSynGutterLineOverviewProvider)
end;
{ TSynChildWinControl
Allow individual invalidates, for less painting
}
TSynChildWinControl = class(TCustomControl)
protected
procedure WMNCHitTest(var Message: TLMessage); message LM_NCHITTEST;
public
constructor Create(AOwner: TComponent); override;
end;
{ TSynGutterLineOverview }
TSynGutterLOvStateFlag = (losLineCountChanged, losResized, losASyncScheduled, losWaitForPaint);
TSynGutterLOvStateFlags = set of TSynGutterLOvStateFlag;
TSynGutterLineOverview = class(TSynGutterPartBase)
private
FProviders: TSynGutterLineOverviewProviderList;
FWinControl: TSynChildWinControl;
FLineMarks: TSynGutterLOvLineMarksList;
FMouseActionsForMarks: TSynEditMouseInternalActions;
FState: TSynGutterLOvStateFlags;
FPpiPenWidth: Integer;
function GetMarkHeight: Integer;
function GetMouseActionsForMarks: TSynEditMouseActions;
procedure SetMarkHeight(const AValue: Integer);
procedure ScheduleASync(AStates: TSynGutterLOvStateFlags);
procedure ExecASync(Data: PtrInt);
procedure SetMouseActionsForMarks(AValue: TSynEditMouseActions);
protected
function PreferedWidth: Integer; override;
procedure Init; override;
procedure LineCountChanged(Sender: TSynEditStrings; AIndex, ACount: Integer);
procedure BufferChanged(Sender: TObject);
procedure SetVisible(const AValue : boolean); override;
procedure GutterVisibilityChanged; override;
procedure DoChange(Sender: TObject); override;
protected
procedure InvalidateTextLines(AFromLine, AToLine: Integer);
procedure InvalidatePixelLines(AFromLine, AToLine: Integer);
function PixelLineToText(ALineIdx: Integer): Integer;
function TextLineToPixel(ALine: Integer): Integer;
function TextLineToPixelEnd(ALine: Integer): Integer;
procedure DoResize(Sender: TObject); override;
Procedure PaintWinControl(Sender: TObject);
//function CreateMouseActions: TSynEditMouseInternalActions; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure Paint(Canvas: TCanvas; AClip: TRect; FirstLine, LastLine: integer); override;
procedure AddMark(AMark: TSynGutterLOvMark);
procedure ScalePPI(const AScaleFactor: Double); override;
function MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo;
HandleActionProc: TSynEditMouseActionHandler): Boolean; override;
function DoHandleMouseAction(AnAction: TSynEditMouseAction;
var AnInfo: TSynEditMouseActionInfo): Boolean; override;
property Providers: TSynGutterLineOverviewProviderList read FProviders;
published
property MarkHeight: Integer read GetMarkHeight write SetMarkHeight;
property MarkupInfo;
property MouseActionsForMarks: TSynEditMouseActions
read GetMouseActionsForMarks write SetMouseActionsForMarks;
end;
implementation
{ TSynGutterLOvMark }
function TSynGutterLOvMark.CompareByPrior(Other: TSynGutterLOvMark): Integer;
begin
Result := Priority - Other.Priority;
if Result <> 0 then exit;
Result := Line - Other.Line;
if Result <> 0 then exit;
Result := Column - Other.Column;
if Result <> 0 then exit;
Result := ComparePointers(Pointer(self), Pointer(Other));
end;
function TSynGutterLOvMark.CompareByLine(Other: TSynGutterLOvMark): Integer;
begin
Result := Line - Other.Line;
if Result <> 0 then exit;
Result := Column - Other.Column;
if Result <> 0 then exit;
Result := Priority - Other.Priority;
if Result <> 0 then exit;
Result := ComparePointers(Pointer(self), Pointer(Other));
end;
procedure TSynGutterLOvMark.DoChange;
begin
if Assigned(FOnChange) then
FOnChange(Self);
end;
destructor TSynGutterLOvMark.Destroy;
begin
if Assigned(FOnDestroy) then
FOnDestroy(Self);
inherited Destroy;
end;
{ TSynGutterLOvMarkList }
function TSynGutterLOvMarkList.GetMark(Index: Integer): TSynGutterLOvMark;
begin
Result := TSynGutterLOvMark(Get(Index));
end;
procedure TSynGutterLOvMarkList.PutMark(Index: Integer; const AValue: TSynGutterLOvMark);
begin
Put(Index, AValue);
ReSort;
end;
procedure TSynGutterLOvMarkList.Add(AValue: TSynGutterLOvMark);
begin
inherited Add(AValue);
ReSort;
end;
procedure TSynGutterLOvMarkList.ReSort;
begin
// nothing
end;
constructor TSynGutterLOvMarkList.Create;
begin
inherited;
FOwnMarks := True;
end;
destructor TSynGutterLOvMarkList.Destroy;
begin
if FOwnMarks then begin
while Count > 0 do begin
Items[0].Destroy;
Delete(0);
end;
end;
inherited Destroy;
end;
procedure TSynGutterLOvMarkList.Lock;
begin
inc(FLockCount);
end;
procedure TSynGutterLOvMarkList.UnLock;
begin
dec(FLockCount);
if (FLockCount = 0) and FNeedSort then
ReSort;
end;
{ TSynGutterLOvLineMarks }
function SynGutterLOvProviderLineMarksSort(Item1, Item2: Pointer): Integer;
begin
Result := TSynGutterLOvMark(Item1).CompareByPrior(TSynGutterLOvMark(Item2));
end;
procedure TSynGutterLOvLineMarks.ReSort;
begin
FNeedSort := FLockCount > 0;
if FLockCount = 0 then
Sort(@SynGutterLOvProviderLineMarksSort);
end;
function SynGutterLOvProviderLineMarksSortByLine(Item1, Item2: Pointer): Integer;
begin
Result := TSynGutterLOvMark(Item1).CompareByLine(TSynGutterLOvMark(Item2));
end;
procedure TSynGutterLOvLineMarks.ReSortByLine;
begin
if FLockCount > 0 then
FNeedSort := True;
Sort(@SynGutterLOvProviderLineMarksSortByLine);
end;
function TSynGutterLOvLineMarks.Compare(Other: TSynGutterLOvLineMarks): Integer;
begin
Result := PixLine - Other.PixLine;
if Result <> 0 then exit;
Result := ComparePointers(Pointer(self), Pointer(Other));
end;
constructor TSynGutterLOvLineMarks.Create;
begin
inherited;
FOwnMarks := False;
end;
procedure TSynGutterLOvLineMarks.Paint(Canvas: TCanvas; AClip: TRect; TopOffset: integer;
AnItemHeight: Integer);
var
bs: TFPBrushStyle;
begin
if (FPixLine + AnItemHeight < AClip.Top - TopOffset) or
(FPixLine > AClip.Bottom - TopOffset)
then
exit;
AClip.Top := FPixLine;
AClip.Bottom := Max(FPixLine+1, FPixLine + AnItemHeight - 1);
inc(AClip.Left);
dec(AClip.Right);
Canvas.Pen.Color := Items[0].Color;
bs := Canvas.Brush.Style;
Canvas.Brush.Style := bsClear;
Canvas.Rectangle(AClip);
Canvas.Brush.Style := bs;
end;
procedure TSynGutterLOvLineMarks.Add(AValue: TSynGutterLOvMark);
begin
AValue.LineMarks := Self;
inherited Add(AValue);
end;
{ TSynGutterLOvLineMarksList }
function TSynGutterLOvLineMarksList.GetLineMarks(Index: Integer): TSynGutterLOvLineMarks;
begin
Result := TSynGutterLOvLineMarks(inherited Items[Index]);
end;
procedure TSynGutterLOvLineMarksList.PutLineMarks(Index: Integer;
const AValue: TSynGutterLOvLineMarks);
begin
inherited Items[Index] := AValue;
Resort;
end;
function SynGutterLOvProviderLineMarksListSort(Item1, Item2: Pointer): Integer;
begin
Result := TSynGutterLOvLineMarks(Item1).Compare(TSynGutterLOvLineMarks(Item2));
end;
procedure TSynGutterLOvLineMarksList.ReSort;
begin
Sort(@SynGutterLOvProviderLineMarksListSort);
end;
procedure TSynGutterLOvLineMarksList.MarkChanged(Sender: TObject);
var
LMark: TSynGutterLOvLineMarks;
j: Integer;
begin
LMark := TSynGutterLOvMark(Sender).LineMarks;
j := TextLineToPixLine(TSynGutterLOvMark(Sender).Line);
if (j >= LMark.PixLine) and (j < LMark.PixLine + ItemHeight) then begin
LMark.ReSort;
exit;
end;
LMark.Remove(Sender);
if LMark.Count = 0 then begin
Remove(LMark);
LMark.Free;
end;
AddMark(TSynGutterLOvMark(Sender));
end;
procedure TSynGutterLOvLineMarksList.MarkDestroying(Sender: TObject);
var
LMark: TSynGutterLOvLineMarks;
begin
LMark := TSynGutterLOvMark(Sender).LineMarks;
LMark.Remove(Sender);
if LMark.Count = 0 then begin
Remove(LMark);
LMark.Free;
end;
end;
function TSynGutterLOvLineMarksList.TextLineToPixLine(ATxtLine: Integer): Integer;
begin
if PixelHeight < 1 then exit(0);
Result := (Int64(ATxtLine) - 1) * Int64(PixelHeight) div TextLineCount;
If FPixelPerLine * 2 < ItemHeight then
dec(Result)
else
if FPixelPerLine > ItemHeight + 2 then
inc(Result);
if Result + ItemHeight > PixelHeight then Result := PixelHeight - ItemHeight;
if Result < 0 then Result := 0;
end;
procedure TSynGutterLOvLineMarksList.ReBuild(AFromIndex: Integer = -1);
procedure PushItemsDown(ASrc, ADest: TSynGutterLOvLineMarks);
var
i, j: Integer;
begin
i := ASrc.Count -1;
while i >= 0 do begin
j := TextLineToPixLine(ASrc[i].Line);
if j >= ASrc.PixLine +ItemHeight then begin
if (ADest.Count = 0) or (ADest.PixLine > j) then
ADest.FPixLine := j;
ADest.Add(ASrc[i]);
ASrc.Delete(i);
end;
dec(i);
end;
end;
procedure PullItemsUp(ADest, ASrc: TSynGutterLOvLineMarks);
var
i, j, NewLine: Integer;
begin
i := ASrc.Count -1;
NewLine := MaxInt;
while i >= 0 do begin
j := TextLineToPixLine(ASrc[i].Line);
if j < ADest.PixLine + ItemHeight then begin
ADest.Add(ASrc[i]);
ASrc.Delete(i);
end
else
if j < NewLine then NewLine := j;
dec(i);
end;
ASrc.FPixLine := NewLine;
end;
var
i, j, NewIdx: Integer;
CurItem, NextItem, TmpItem: TSynGutterLOvLineMarks;
begin
if Count = 0 then exit;
if AFromIndex < 0 then begin
NewIdx := -1;
for i := 0 to Items[0].Count -1 do begin
j := TextLineToPixLine(Items[0][i].Line);
if (i = 0) or (j < NewIdx) then
NewIdx := j;
end;
Items[0].FPixLine := NewIdx;
AFromIndex := 0;
end;
TmpItem := TSynGutterLOvLineMarks.Create;
TmpItem.Lock;
NextItem := Items[AFromIndex];
NextItem.Lock;
while NextItem <> nil do begin
if (TmpItem.Count > 0) and (TmpItem.PixLine < NextItem.PixLine) then begin
Insert(AFromIndex, TmpItem);
CurItem := TmpItem;
TmpItem := TSynGutterLOvLineMarks.Create;
TmpItem.Lock;
end
else
CurItem := NextItem;
inc(AFromIndex);
if AFromIndex < Count then begin
NextItem := Items[AFromIndex];
NextItem.Lock;
if NextItem.PixLine < CurItem.PixLine + ItemHeight then
NextItem.FPixLine := CurItem.PixLine + ItemHeight;
end
else
NextItem := nil;
PushItemsDown(CurItem, TmpItem);
PullItemsUp(CurItem, TmpItem);
while NextItem <> nil do begin
PullItemsUp(CurItem, NextItem);
if NextItem.Count > 0 then
break;
Delete(AFromIndex);
NextItem.Free;
if AFromIndex < Count then begin
NextItem := Items[AFromIndex];
NextItem.Lock;
end
else
NextItem := nil;
end;
if (TmpItem.Count > 0) and (NextItem = nil) then begin
Insert(AFromIndex, TmpItem);
NextItem := TmpItem;
TmpItem := TSynGutterLOvLineMarks.Create;
TmpItem.Lock;
end;
CurItem.UnLock;
if CurItem.Count = 0 then begin
Delete(AFromIndex - 1);
dec(AFromIndex);
CurItem.Free;
end;
end;
TmpItem.Free;
end;
constructor TSynGutterLOvLineMarksList.Create;
begin
FTextLineCount := 1;
FItemHeight := 4;
inherited;
end;
destructor TSynGutterLOvLineMarksList.Destroy;
begin
while Count > 0 do begin
Items[0].Destroy;
Delete(0);
end;
inherited Destroy;
end;
procedure TSynGutterLOvLineMarksList.AddMark(AMark: TSynGutterLOvMark);
var
i, PixLine: Integer;
LMarks: TSynGutterLOvLineMarks;
begin
AMark.OnChange := @MarkChanged;
AMark.OnDestroy := @MarkDestroying;
PixLine := TextLineToPixLine(AMark.Line);
i := IndexForLine(PixLine, True);
if i >= 0 then begin
LMarks := Items[i];
if (PixLine >= LMarks.PixLine) and (PixLine < LMarks.PixLine + ItemHeight) then begin
LMarks.Add(AMark);
// sendinvalidate
exit;
end;
end;
inc(i);
LMarks := TSynGutterLOvLineMarks.Create;
LMarks.FPixLine := PixLine;
Insert(i, LMarks);
LMarks.Add(AMark);
// sendinvalidate
if (i < Count - 1) and (Items[i+1].PixLine < LMarks.PixLine + ItemHeight) then
ReBuild(i);
end;
function TSynGutterLOvLineMarksList.IndexForLine(ALine: Integer;
PreviousIfNotExist: Boolean; UseItemHeight: Boolean): Integer;
var
l, h, m: Integer;
begin
l := 0;
h := Count - 1;
if h < 0 then
exit(-1);
while h > l do begin
m := (h+l) div 2;
if Items[m].PixLine <= ALine then
l := m + 1
else
h := m;
end;
Result := h;
if Items[Result].PixLine > ALine then begin
dec(Result);
if Result < 0 then exit;
end;
Assert(Items[Result].PixLine <= ALine);
if UseItemHeight and (Items[Result].PixLine + ItemHeight > ALine) then
exit;
if (Items[Result].PixLine = ALine) or (PreviousIfNotExist) then
exit;
Result := -1;
end;
function TSynGutterLOvLineMarksList.ItemForLine(ALine: Integer;
CreateIfNotExists: Boolean): TSynGutterLOvLineMarks;
var
i: Integer;
LMarks: TSynGutterLOvLineMarks;
begin
i := IndexForLine(ALine, CreateIfNotExists);
if CreateIfNotExists and ( (i < 0) or (Items[i].PixLine <> ALine) ) then begin
inc(i);
LMarks := TSynGutterLOvLineMarks.Create;
LMarks.FPixLine := i;
Insert(i, LMarks);
end;
if i >= 0 then
Result := Items[i]
else
Result := nil;
end;
procedure TSynGutterLOvLineMarksList.SetItemHeight(const AValue: Integer);
begin
if FItemHeight = AValue then exit;
FItemHeight := AValue;
ReBuild;
end;
procedure TSynGutterLOvLineMarksList.SetPixelHeight(const AValue: Integer);
begin
if FPixelHeight = AValue then exit;
FPixelHeight := AValue;
FPixelPerLine := FPixelHeight div TextLineCount;
ReBuild;
end;
procedure TSynGutterLOvLineMarksList.SetTextLineCount(const AValue: Integer);
begin
if FTextLineCount = AValue then exit;
FTextLineCount := Max(1, AValue);
ReBuild;
end;
{ TSynGutterLineOverviewProvider }
constructor TSynGutterLineOverviewProvider.Create(AOwner: TComponent);
begin
inherited;
FGutterPart := Owner.Owner;
FColor := clLtGray;
FriendEdit := SynEdit;
FMarkList := TSynGutterLOvMarkList.Create;
end;
destructor TSynGutterLineOverviewProvider.Destroy;
begin
FreeAndNil(FMarkList);
inherited Destroy;
end;
function TSynGutterLineOverviewProvider.GetList: TSynGutterLineOverviewProviderList;
begin
Result := TSynGutterLineOverviewProviderList(inherited Owner);
end;
function TSynGutterLineOverviewProvider.GetMarks(AnIndex: Integer): TSynGutterLOvMark;
begin
Result := FMarkList[Index];
end;
procedure TSynGutterLineOverviewProvider.SetColor(const AValue: TColor);
begin
if FColor = AValue then exit;
FColor := AValue;
FRGBColor := TColor(ColorToRGB(AValue));
DoChange(Self);
end;
procedure TSynGutterLineOverviewProvider.SetHeight(const AValue: Integer);
begin
if FHeight = AValue then exit;
FHeight := AValue;
ReCalc;
end;
procedure TSynGutterLineOverviewProvider.SetPriority(const AValue: Integer);
begin
if FPriority = AValue then exit;
FPriority := AValue;
Owner.Sort;
end;
function TSynGutterLineOverviewProvider.SynEdit: TSynEditBase;
begin
Result := FGutterPart.SynEdit;
end;
function TSynGutterLineOverviewProvider.Compare(Other: TSynObjectListItem): Integer;
begin
Result := Priority - TSynGutterLineOverviewProvider(Other).Priority;
if Result = 0 then
Result := inherited Compare(Other);
end;
procedure TSynGutterLineOverviewProvider.DoChange(Sender: TObject);
begin
FGutterPart.DoChange(Sender);
end;
procedure TSynGutterLineOverviewProvider.InvalidateTextLines(AFromLine, AToLine: Integer);
begin
FGutterPart.InvalidateTextLines(AFromLine, AToLine);
end;
procedure TSynGutterLineOverviewProvider.InvalidatePixelLines(AFromLine, AToLine: Integer);
begin
FGutterPart.InvalidatePixelLines(AFromLine, AToLine);
end;
function TSynGutterLineOverviewProvider.TextLineToPixel(ALine: Integer): Integer;
var
c: Integer;
begin
if ALine < 0 then exit(-1);
c := Max(1, TextBuffer.Count);
if c = 0 then
Result := -1
else
Result := (Int64(ALine - 1) * Int64(Height)) div c;
end;
function TSynGutterLineOverviewProvider.TextLineToPixelEnd(ALine: Integer): Integer;
var
c, n: Integer;
begin
if ALine < 0 then exit(-1);
c := Max(1, TextBuffer.Count);
Result := (Int64(ALine - 1) * Int64(Height)) div c;
n := (Int64(ALine) * Int64(Height)) div c - 1; // next line - 1 pix
if n > Result then
Result := n;
end;
function TSynGutterLineOverviewProvider.PixelLineToText(ALineIdx: Integer): Integer;
var
c: Int64;
begin
c := Max(1, TextBuffer.Count);
if c = 0 then
Result := -1
else begin
Result := (Int64(ALineIdx) * c) div Height + 1;
if (Int64(Result - 1) * Int64(Height)) div c <> ALineIdx then
inc(Result);
end;
end;
procedure TSynGutterLineOverviewProvider.ReCalc;
begin
// nothing
end;
procedure TSynGutterLineOverviewProvider.Paint(Canvas: TCanvas; AClip: TRect;
TopOffset: integer);
begin
// nothing
end;
function TSynGutterLineOverviewProvider.MarksCount: Integer;
begin
Result := FMarkList.Count;
end;
{ TSynGutterLineOverviewProviderList }
function TSynGutterLineOverviewProviderList.GetGutterPart: TSynGutterLineOverview;
begin
Result := TSynGutterLineOverview(inherited Owner);
end;
function TSynGutterLineOverviewProviderList.GetProviders(AIndex: Integer): TSynGutterLineOverviewProvider;
begin
Result := TSynGutterLineOverviewProvider(BaseItems[AIndex]);
end;
constructor TSynGutterLineOverviewProviderList.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Sorted := True;
end;
{ TSynGutterLOvProviderCurrentPage }
procedure TSynGutterLOvProviderCurrentPage.FoldChanged(Sender: TSynEditStrings;
aIndex, aCount: Integer);
begin
SynStatusChanged(nil, []);
end;
procedure TSynGutterLOvProviderCurrentPage.SynStatusChanged(Sender: TObject;
Changes: TSynStatusChanges);
begin
InvalidatePixelLines(FPixelTopLine, FPixelBottomLine);
FCurTopLine := SynEdit.TopLine;
FCurBottomLine := SynEdit.ScreenRowToRow(SynEdit.LinesInWindow);
ReCalc;
InvalidatePixelLines(FPixelTopLine, FPixelBottomLine);
end;
procedure TSynGutterLOvProviderCurrentPage.ReCalc;
begin
FPixelTopLine := TextLineToPixel(FCurTopLine);
FPixelBottomLine := TextLineToPixelEnd(FCurBottomLine - 1);
end;
procedure TSynGutterLOvProviderCurrentPage.Paint(Canvas: TCanvas; AClip: TRect;
TopOffset: integer);
begin
if (FPixelBottomLine < AClip.Top - TopOffset) or
(FPixelTopLine > AClip.Bottom - TopOffset)
then
exit;
AClip.Top := Max(AClip.Top, FPixelTopLine+TopOffset);
AClip.Bottom := Min(AClip.Bottom, FPixelBottomLine+TopOffset);
Canvas.Brush.Color := FRGBColor;
Canvas.FillRect(AClip);
end;
constructor TSynGutterLOvProviderCurrentPage.Create(AOwner: TComponent);
begin
inherited;
FColor := 0;
Color := $C0C0C0;
ViewedTextBuffer.AddChangeHandler(senrLineMappingChanged, @FoldChanged);
SynEdit.RegisterStatusChangedHandler(@SynStatusChanged,
[scTopLine, scLinesInWindow]);
end;
destructor TSynGutterLOvProviderCurrentPage.Destroy;
begin
SynEdit.UnRegisterStatusChangedHandler(@SynStatusChanged);
ViewedTextBuffer.RemoveChangeHandler(senrLineMappingChanged, @FoldChanged);
inherited;
end;
{ TSynGutterLOvProviderModifiedLines }
procedure TSynGutterLOvProviderModifiedLines.SetColorSaved(const AValue: TColor);
begin
if FColorSaved = AValue then exit;
FColorSaved := AValue;
FRGBColorSaved := TColor(ColorToRGB(AValue));
DoChange(Self);
end;
procedure TSynGutterLOvProviderModifiedLines.Paint(Canvas: TCanvas; AClip: TRect;
TopOffset: integer);
var
i, i2, imax: Integer;
State: TSynEditStringFlags;
begin
if FFirstTextLineChanged > 0 then ReScan;
AClip.Right := AClip.Left + Round((AClip.Right - AClip.Left) / 3);
i := AClip.Top - TopOffset;
imax := AClip.Bottom - TopOffset;
if imax > high(FPixLineStates) then imax := high(FPixLineStates);
while i <= imax do begin
i2 := i+1;
State := FPixLineStates[i];
while (i2 <= imax) and (FPixLineStates[i2] = State) do
inc(i2);
if State <> [] then begin
AClip.Top := i;
AClip.Bottom := i2;
if sfSaved in State then
Canvas.Brush.Color := FRGBColorSaved
else
Canvas.Brush.Color := FRGBColor;
Canvas.FillRect(AClip);
end;
i := i2;
end;
end;
procedure TSynGutterLOvProviderModifiedLines.ReScan;
var
i, PixLine, PixLineEnd, CurPixLineEnd, TxtIndex, TxtIndexEnd: Integer;
NewState: TSynEditStringFlags;
begin
SetLength(FPixLineStates, Height);
PixLine := TextLineToPixel(FFirstTextLineChanged);
if PixLine < 0 then PixLine := 0;
if FLastTextLineChanged = 0 then
PixLineEnd := TextLineToPixelEnd(TextBuffer.Count)
else
PixLineEnd := TextLineToPixelEnd(FLastTextLineChanged);
if PixLineEnd < PixLine then PixLineEnd := PixLine;
if PixLineEnd >= Height then PixLineEnd := Height - 1;
while PixLine <= PixLineEnd do begin
NewState := [];
TxtIndex := PixelLineToText(PixLine) - 1;
CurPixLineEnd := TextLineToPixelEnd(TxtIndex + 1);
TxtIndexEnd := PixelLineToText(CurPixLineEnd + 1) - 1;
while TxtIndex < TxtIndexEnd do begin
NewState := NewState + TSynEditStringList(TextBuffer).Flags[TxtIndex];
inc(TxtIndex);
end;
for i := PixLine to CurPixLineEnd do
FPixLineStates[i] := NewState;
PixLine := CurPixLineEnd + 1;
end;
FFirstTextLineChanged := -1;
FLastTextLineChanged := -1;
end;
procedure TSynGutterLOvProviderModifiedLines.ReCalc;
begin
FFirstTextLineChanged := 1;
FLastTextLineChanged := 0;
end;
procedure TSynGutterLOvProviderModifiedLines.LineModified(Sender: TSynEditStrings; aIndex,
aNewCount, aOldCount: Integer);
begin
if (FFirstTextLineChanged < 0) or (AIndex + 1 < FFirstTextLineChanged) then
FFirstTextLineChanged := AIndex + 1;
if aOldCount = aNewCount then
aIndex := aIndex + aOldCount
else
aIndex := TextBuffer.Count;
if (FLastTextLineChanged <> 0) and (AIndex + 1 > FLastTextLineChanged) then
FLastTextLineChanged := AIndex + 1;
InvalidateTextLines(FFirstTextLineChanged, FLastTextLineChanged);
end;
procedure TSynGutterLOvProviderModifiedLines.SynStatusChanged(Sender: TObject;
Changes: TSynStatusChanges);
begin
if (scModified in Changes) and not SynEdit.Modified then begin;
FFirstTextLineChanged := 1;
FLastTextLineChanged := 0; // open end
InvalidateTextLines(FFirstTextLineChanged, TextBuffer.Count);
end;
end;
constructor TSynGutterLOvProviderModifiedLines.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ViewedTextBuffer.AddModifiedHandler(senrLinesModified, @LineModified);
SynEdit.RegisterStatusChangedHandler(@SynStatusChanged, [scModified]);
FFirstTextLineChanged := -1;
FLastTextLineChanged := -1;
Color := clYellow;
ColorSaved := clGreen;
end;
destructor TSynGutterLOvProviderModifiedLines.Destroy;
begin
ViewedTextBuffer.RemoveHandlers(self);
SynEdit.UnRegisterStatusChangedHandler(@SynStatusChanged);
inherited Destroy;
end;
{ TSynGutterLOvProviderBookmarks }
procedure TSynGutterLOvProviderBookmarks.DoMarkChange(Sender: TSynEditMark;
Changes: TSynEditMarkChangeReasons);
var
i: Integer;
c: TColor;
p: Integer;
begin
if (smcrAdded in Changes) or
( (smcrVisible in Changes) and Sender.Visible )
then begin
if Sender.Visible then
CreateGutterMark(Sender);
InvalidateTextLines(0, TextBuffer.Count); // Todo
end
else
if (smcrRemoved in Changes) or
( (smcrVisible in Changes) and not Sender.Visible )
then begin
i := IndexOfSynMark(Sender);
if i >= 0 then begin
FMarkList[i].Destroy;
FMarkList.Delete(i);
end;
InvalidateTextLines(0, TextBuffer.Count); // Todo
end
else
if (smcrLine in Changes) then begin
i := IndexOfSynMark(Sender);
if i >= 0 then begin
FMarkList[i].FLine := Sender.Line;
FMarkList[i].DoChange;
end;
FMarkList.ReSort;
InvalidateTextLines(0, TextBuffer.Count); // Todo
end
else
begin
i := IndexOfSynMark(Sender);
if i >= 0 then begin
c := Color;
p := Priority;
AdjustColorForMark(Sender, c, p);
FMarkList[i].FPriority := p;
FMarkList[i].FColor := c;
FMarkList[i].DoChange;
end;
InvalidateTextLines(0, TextBuffer.Count); // Todo
end
end;
procedure TSynGutterLOvProviderBookmarks.AdjustColorForMark(AMark: TSynEditMark;
var AColor: TColor; var APriority: Integer);
begin
if assigned(FOnColorForMark) then
FOnColorForMark(Self, AMark, AColor, APriority);
end;
function TSynGutterLOvProviderBookmarks.CreateGutterMark(ASynMark: TSynEditMark): TSynGutterLOvMark;
var
c: TColor;
p: Integer;
begin
Result := TSynGutterLOvMark.Create;
Result.FData := ASynMark;
Result.FLine := ASynMark.Line;
Result.FColumn := ASynMark.Column;
c := Color;
p := Priority;
AdjustColorForMark(ASynMark, c, p);
Result.FPriority := p;
Result.FColor := c;
FMarkList.Add(Result);
GutterPart.AddMark(Result);
end;
function TSynGutterLOvProviderBookmarks.IndexOfSynMark(ASynMark: TSynEditMark): Integer;
begin
Result := FMarkList.Count - 1;
while Result >= 0 do begin
if FMarkList[Result].FData = Pointer(ASynMark) then exit;
dec(Result);
end;
end;
procedure TSynGutterLOvProviderBookmarks.BufferChanging(Sender: TObject);
begin
(SynEdit.Marks as TSynEditMarkList).UnRegisterChangeHandler(@DoMarkChange);
end;
procedure TSynGutterLOvProviderBookmarks.BufferChanged(Sender: TObject);
var
i: Integer;
LMarks: TSynEditMarkList;
begin
LMarks := (SynEdit.Marks as TSynEditMarkList);
LMarks.RegisterChangeHandler(@DoMarkChange,
[smcrAdded, smcrRemoved, smcrLine, smcrVisible, smcrChanged]);
while FMarkList.Count > 0 do begin
FMarkList[0].Destroy;
FMarkList.Delete(0);
end;
for i := 0 to LMarks.Count - 1 do
if LMarks[i].Visible then
CreateGutterMark(LMarks[i]);
end;
constructor TSynGutterLOvProviderBookmarks.Create(AOwner: TComponent);
var
i: Integer;
LMarks: TSynEditMarkList;
begin
inherited Create(AOwner);
Color := clBlue;
ViewedTextBuffer.AddNotifyHandler(senrTextBufferChanging, @BufferChanging);
ViewedTextBuffer.AddNotifyHandler(senrTextBufferChanged, @BufferChanged);
LMarks := (SynEdit.Marks as TSynEditMarkList);
LMarks.RegisterChangeHandler(@DoMarkChange,
[smcrAdded, smcrRemoved, smcrLine, smcrVisible, smcrChanged]);
for i := 0 to LMarks.Count - 1 do
if LMarks[i].Visible then
CreateGutterMark(LMarks[i]);
end;
destructor TSynGutterLOvProviderBookmarks.Destroy;
begin
ViewedTextBuffer.RemoveHandlers(self);
(SynEdit.Marks as TSynEditMarkList).UnRegisterChangeHandler(@DoMarkChange);
inherited Destroy;
end;
{ TSynChildWinControl }
procedure TSynChildWinControl.WMNCHitTest(var Message: TLMessage);
begin
Message.Result := HTTRANSPARENT;
end;
constructor TSynChildWinControl.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
BorderStyle := bsNone;
end;
{ TSynGutterLineOverview }
procedure TSynGutterLineOverview.Init;
begin
inherited Init;
ViewedTextBuffer.AddChangeHandler(senrLineCount, @LineCountChanged);
ViewedTextBuffer.AddNotifyHandler(senrTextBufferChanged, @BufferChanged);
FWinControl := TSynChildWinControl.Create(Self);
FWinControl.Parent := SynEdit;
FWinControl.OnPaint := @PaintWinControl;
FLineMarks := TSynGutterLOvLineMarksList.Create;
FProviders := TSynGutterLineOverviewProviderList.Create(Self);
MarkupInfo.Background := $E0E0E0;
DoResize(Self);
LineCountchanged(nil, 0, 0);
end;
destructor TSynGutterLineOverview.Destroy;
begin
Application.RemoveAsyncCalls(Self);
ViewedTextBuffer.RemoveHandlers(self);
FreeAndNil(FProviders);
FreeAndNil(FWinControl);
FreeAndNil(FLineMarks);
FreeAndNil(FMouseActionsForMarks);
inherited Destroy;
end;
procedure TSynGutterLineOverview.LineCountChanged(Sender: TSynEditStrings; AIndex,
ACount: Integer);
begin
FLineMarks.TextLineCount := TextBuffer.Count;
if (not SynEdit.HandleAllocated) or (not Self.Visible) then exit;
ScheduleASync([losLineCountChanged]);
end;
procedure TSynGutterLineOverview.BufferChanged(Sender: TObject);
begin
LineCountChanged(nil, 0, 0);
end;
procedure TSynGutterLineOverview.SetVisible(const AValue: boolean);
begin
inherited SetVisible(AValue);
FWinControl.Visible := Visible and Gutter.Visible;
if FWinControl.Visible then
ScheduleASync([losResized, losLineCountChanged]);
end;
procedure TSynGutterLineOverview.GutterVisibilityChanged;
begin
inherited GutterVisibilityChanged;
FWinControl.Visible := Visible and Gutter.Visible;
if FWinControl.Visible then
ScheduleASync([losResized, losLineCountChanged]);
end;
procedure TSynGutterLineOverview.DoChange(Sender: TObject);
begin
inherited;
FWinControl.Invalidate;
end;
procedure TSynGutterLineOverview.InvalidateTextLines(AFromLine, AToLine: Integer);
begin
InvalidatePixelLines(TextLineToPixel(AFromLine), TextLineToPixelEnd(AToLine));
end;
procedure TSynGutterLineOverview.InvalidatePixelLines(AFromLine, AToLine: Integer);
var
r: TRect;
begin
if not SynEdit.HandleAllocated then exit;
r := Rect(0, Top, Width, Top + Height);
r.Top := AFromLine;
r.Bottom := AToLine + 1;
InvalidateRect(FWinControl.Handle, @r, False);
end;
function TSynGutterLineOverview.PixelLineToText(ALineIdx: Integer): Integer;
var
c: Integer;
begin
if ALineIdx < 0 then exit(-1);
c := TextBuffer.Count;
if c = 0 then
Result := -1
else
Result := Min(Int64(ALineIdx) * Int64(c) div Height, c-1);
end;
function TSynGutterLineOverview.TextLineToPixel(ALine: Integer): Integer;
var
c: Integer;
begin
if ALine < 0 then exit(-1);
c := Max(1, TextBuffer.Count);
if c = 0 then
Result := -1
else
Result := Int64(ALine - 1) * Int64(Height) div c;
end;
function TSynGutterLineOverview.TextLineToPixelEnd(ALine: Integer): Integer;
var
c, n: Integer;
begin
if ALine < 0 then exit(-1);
c := Max(1, TextBuffer.Count);
Result := (Int64(ALine - 1) * Int64(Height)) div c;
n := (Int64(ALine) * Int64(Height)) div c - 1; // next line - 1 pix
if n > Result then
Result := n;
end;
procedure TSynGutterLineOverview.DoResize(Sender: TObject);
var
i: Integer;
begin
inherited DoResize(Sender);
if (not SynEdit.HandleAllocated) or (not Self.Visible) then exit;
FWinControl.BoundsRect := Bounds(Left+LeftOffset,Top,Width,Height);
{$IFDEF DARWIN}
FLineMarks.PixelHeight := Height;
for i := 0 to FProviders.Count - 1 do
FProviders[i].Height := Height;
FWinControl.Invalidate;
{$ELSE}
ScheduleASync([losResized]); // May only be executed after mouse up
if not (losWaitForPaint in FState) then begin
FLineMarks.PixelHeight := Height;
for i := 0 to FProviders.Count - 1 do
FProviders[i].Height := Height;
FWinControl.Invalidate;
FState := FState + [losWaitForPaint];
end;
{$ENDIF}
end;
procedure TSynGutterLineOverview.PaintWinControl(Sender: TObject);
var
i: Integer;
AClip: TRect;
begin
FState := FState - [losWaitForPaint];
if not Visible then exit;
AClip := FWinControl.Canvas.ClipRect;
AClip.Left := 0;
AClip.Right := Width;
FWinControl.Canvas.Brush.Color := MarkupInfo.Background;
FWinControl.Canvas.FillRect(AClip);
FWinControl.Canvas.Pen.Width := FPpiPenWidth;
FWinControl.Canvas.Pen.JoinStyle := pjsMiter;
for i := 0 to Providers.Count - 1 do
Providers[i].Paint(FWinControl.Canvas, AClip, 0);
for i := 0 to FLineMarks.Count - 1 do
FLineMarks[i].Paint(FWinControl.Canvas, AClip, 0, MarkHeight);
end;
constructor TSynGutterLineOverview.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPpiPenWidth := 1;
FMouseActionsForMarks := TSynEditMouseInternalActions.Create(Self);
end;
function TSynGutterLineOverview.GetMarkHeight: Integer;
begin
Result := FLineMarks.ItemHeight;
end;
function TSynGutterLineOverview.GetMouseActionsForMarks: TSynEditMouseActions;
begin
Result := FMouseActionsForMarks.UserActions;
end;
procedure TSynGutterLineOverview.SetMarkHeight(const AValue: Integer);
begin
FLineMarks.ItemHeight := AValue;
end;
procedure TSynGutterLineOverview.ScheduleASync(AStates: TSynGutterLOvStateFlags);
begin
if not (losASyncScheduled in FState) then
Application.QueueAsyncCall(@ExecASync, 0);
FState := FState + [losASyncScheduled] + AStates;
end;
procedure TSynGutterLineOverview.ExecASync(Data: PtrInt);
var
i: Integer;
begin
if losLineCountChanged in FState then begin
for i := 0 to FProviders.Count - 1 do
FProviders[i].ReCalc;
FLineMarks.ReBuild;
end;
if losResized in FState then begin
FLineMarks.PixelHeight := Height;
for i := 0 to FProviders.Count - 1 do
FProviders[i].Height := Height;
end;
FWinControl.Invalidate;
FState := FState - [losASyncScheduled, losResized, losLineCountChanged];
end;
procedure TSynGutterLineOverview.SetMouseActionsForMarks(
AValue: TSynEditMouseActions);
begin
FMouseActionsForMarks.UserActions := AValue;
end;
function TSynGutterLineOverview.PreferedWidth: Integer;
begin
Result := 10;
end;
procedure TSynGutterLineOverview.ScalePPI(const AScaleFactor: Double);
begin
FLineMarks.ItemHeight := Round(FLineMarks.ItemHeight*AScaleFactor);
FPpiPenWidth := Max(1, Scale96ToFont(1));
inherited ScalePPI(AScaleFactor);
end;
procedure TSynGutterLineOverview.Assign(Source : TPersistent);
begin
if Source is TSynGutterLineOverview then
begin
inherited;
// Todo: assign providerlist?
end;
inherited;
end;
procedure TSynGutterLineOverview.Paint(Canvas : TCanvas; AClip : TRect; FirstLine, LastLine : integer);
begin
// do nothing
end;
procedure TSynGutterLineOverview.AddMark(AMark: TSynGutterLOvMark);
begin
FLineMarks.AddMark(AMark);
end;
function TSynGutterLineOverview.MaybeHandleMouseAction(
var AnInfo: TSynEditMouseActionInfo;
HandleActionProc: TSynEditMouseActionHandler): Boolean;
begin
Result := False;
if FLineMarks.IndexForLine(AnInfo.MouseY, False, True) >= 0 then
Result := HandleActionProc(FMouseActionsForMarks.GetActionsForOptions(SynEdit.MouseOptions), AnInfo);
if not Result then
Result := inherited MaybeHandleMouseAction(AnInfo, HandleActionProc);
end;
function TSynGutterLineOverview.DoHandleMouseAction(
AnAction: TSynEditMouseAction; var AnInfo: TSynEditMouseActionInfo): Boolean;
var
i, TextLine: Integer;
begin
Result := False;
if AnAction = nil then exit;
case AnAction.Command of
emcOverViewGutterScrollTo: begin
TextLine := PixelLineToText(AnInfo.MouseY);
AnInfo.NewCaret.BytePos := 1;
Result := True;
end;
emcOverViewGutterGotoMark: begin
i := FLineMarks.IndexForLine(AnInfo.MouseY, False, True);
if (i < 0) or (FLineMarks.Items[i].Count = 0) then
exit;
TextLine := FLineMarks.Items[i].Items[0].Line;
AnInfo.NewCaret.BytePos := Max(FLineMarks.Items[i].Items[0].Column, 1);
Result := True;
end;
end;
if Result then begin
if ({%H-}TextLine < SynEdit.TopLine) or
(TextLine > SynEdit.TopLine + SynEdit.LinesInWindow)
then
SynEdit.TopLine := Max(1, TextLine - SynEdit.LinesInWindow div 2);
AnInfo.NewCaret.LinePos := TextLine;
end;
end;
end.