unit SynGutterBase;

{$I synedit.inc}

interface

uses
  Classes, SysUtils, math,
  // LCL
  Graphics, Controls, Menus, LCLIntf, LCLType,
  // LazUtils
  LazMethodList,
  // SynEdit
  SynEditMarks, SynEditMiscClasses, SynTextDrawer, SynEditMouseCmds,
  LazSynTextArea;

type

  TGutterClickEvent = procedure(Sender: TObject; X, Y, Line: integer;
    mark: TSynEditMark) of object;

  TSynGutterPartBase = class;
  TSynGutterPartBaseClass = class of TSynGutterPartBase;
  TSynGutterPartListBase = class;

  { TSynGutterBase }

  TSynGutterSide = (gsLeft, gsRight);

  TSynGutterBase = class(TPersistent)
  private
    FGutterArea: TLazSynSurfaceWithText;
    FGutterPartList: TSynGutterPartListBase;
    FSide: TSynGutterSide;
    FSynEdit: TSynEditBase;
    FTextDrawer: TheTextDrawer;
    FColor: TColor;
    FLeft, FWidth, FHeight, FTop: Integer;
    FVisible: boolean;
    FAutoSize: boolean;
    FRightOffset, FLeftOffset: integer;
    FInDoChange: Boolean;
    FChangeLock: Integer;
    FNeedOnChange, FNeedOnResize: Boolean;
    FFlags: set of (gfNeedChildBounds);
    FOnResize: TNotifyEvent;
    FOnChange: TNotifyEvent;
    FMouseActions: TSynEditMouseInternalActions;
    FOnResizeHandler: TMethodList;
    FOnChangeHandler: TMethodList;

    function GetMouseActions: TSynEditMouseActions;
    procedure SetAutoSize(const AValue: boolean);
    procedure SetColor(const Value: TColor);
    procedure SetGutterParts(const AValue: TSynGutterPartListBase);
    procedure SetLeftOffset(const AValue: integer);
    procedure SetMouseActions(const AValue: TSynEditMouseActions);
    procedure SetRightOffset(const AValue: integer);
    procedure SetVisible(const AValue: boolean);
    procedure SetWidth(Value: integer);
  protected
    procedure SetChildBounds;
    procedure DoChange(Sender: TObject);
    procedure DoResize(Sender: TObject);
    procedure IncChangeLock;
    procedure DecChangeLock;
    procedure DoDefaultGutterClick(Sender: TObject; X, Y, Line: integer;
      mark: TSynEditMark); virtual;
    procedure RegisterNewGutterPartList(APartList: TSynGutterPartListBase);
    function  PartCount: integer;
    function  CreatePartList: TSynGutterPartListBase; virtual; abstract;
    function  CreateMouseActions: TSynEditMouseInternalActions; virtual;
    procedure Clear;
    function GetOwner: TPersistent; override;
    property GutterArea: TLazSynSurfaceWithText read FGutterArea write FGutterArea;
  public
    constructor Create(AOwner : TSynEditBase; ASide: TSynGutterSide; ATextDrawer: TheTextDrawer);
    destructor Destroy; override;
    procedure Assign(Source: TPersistent); override;
    procedure RecalcBounds;
    procedure ScalePPI(const AScaleFactor: Double);
    procedure DoAutoSize;
    function MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo;
                 HandleActionProc: TSynEditMouseActionHandler): Boolean; virtual;
    procedure ResetMouseActions; virtual; // set mouse-actions according to current Options / may clear them
    procedure RegisterResizeHandler(AHandler: TNotifyEvent);
    procedure UnregisterResizeHandler(AHandler: TNotifyEvent);
    procedure RegisterChangeHandler(AHandler: TNotifyEvent);
    procedure UnregisterChangeHandler(AHandler: TNotifyEvent);
    property Left: Integer read FLeft;
    property Top: Integer read FTop;
    property Height:Integer read FHeight;
    property Width: integer read FWidth write SetWidth;
    property Side:TSynGutterSide read FSide;
    property AutoSize: boolean read FAutoSize write SetAutoSize default True;
    property Visible: boolean read FVisible write SetVisible default True;
    property LeftOffset: integer read FLeftOffset write SetLeftOffset
      default 0;
    property RightOffset: integer read FRightOffset write SetRightOffset
      default 0;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property OnResize: TNotifyEvent read FOnResize write FOnResize;
  public
    property Parts: TSynGutterPartListBase read FGutterPartList write SetGutterParts;
  public
    // properties available for the GutterPartClasses
    property SynEdit: TSynEditBase read FSynEdit;
    property TextDrawer: TheTextDrawer read FTextDrawer;
    property Color: TColor read FColor write SetColor default clBtnFace;
    property MouseActions: TSynEditMouseActions
      read GetMouseActions write SetMouseActions;
  end;

  { TSynGutterPartListBase }

  TSynGutterPartListBase = class(TSynObjectList)
  private
    FGutter: TSynGutterBase;
    function GetByClass(AClass: TSynGutterPartBaseClass; Index: Integer): TSynGutterPartBase;
    function GetByClassCount(AClass: TSynGutterPartBaseClass): Integer;
    function GetPart(Index: Integer): TSynGutterPartBase;
    function GetSynEdit: TSynEditBase;
    procedure PutPart(Index: Integer; const AValue: TSynGutterPartBase);
  protected
    procedure RegisterItem(AnItem: TSynObjectListItem); override;
    property Gutter: TSynGutterBase read FGutter;
    property SynEdit:TSynEditBase read GetSynEdit;
  public
    constructor Create(AOwner: TComponent); override;
    constructor Create(AOwner: TComponent; AGutter: TSynGutterBase);
    destructor  Destroy; override;
    property Part[Index: Integer]: TSynGutterPartBase
      read GetPart write PutPart; default;
    property ByClassCount[AClass: TSynGutterPartBaseClass]: Integer
      read GetByClassCount;
    property ByClass[AClass: TSynGutterPartBaseClass; Index: Integer]: TSynGutterPartBase
      read GetByClass;
  end;

  { TSynGutterPartList
    GutterPartList for the left side Gutter. Historically the left Gutter is reffered to as Gutter without prefix }

  TSynGutterPartList = class(TSynGutterPartListBase);

  { TSynRightGutterPartList
    GutterPartList for the right side Gutter. }

  TSynRightGutterPartList = class(TSynGutterPartListBase);

  { TSynGutterPartBase }

  TSynGutterPartBase = class(TSynObjectListItem)
  private
    FLeft, FWidth, FHeight, FTop: Integer;
    FLeftOffset: integer;
    FRightOffset: integer;
    FAutoSize : boolean;
    FVisible: Boolean;
    FSynEdit: TSynEditBase;
    FGutter: TSynGutterBase;
    FMarkupInfo: TSynSelectedColor;
    FCursor: TCursor;
    FOnChange: TNotifyEvent;
    FOnGutterClick: TGutterClickEvent;
    FMouseActions: TSynEditMouseInternalActions;
    function GetFullWidth: integer;
    function GetGutterArea: TLazSynSurfaceWithText;
    function GetGutterParts: TSynGutterPartListBase;
    function GetMouseActions: TSynEditMouseActions;
    procedure SetLeftOffset(AValue: integer);
    procedure SetMarkupInfo(const AValue: TSynSelectedColor);
    procedure SetMouseActions(const AValue: TSynEditMouseActions);
    procedure SetRightOffset(AValue: integer);
  protected
    function  CreateMouseActions: TSynEditMouseInternalActions; virtual;
    function Scale96ToFont(const ASize: Integer): Integer;
    function  PreferedWidth: Integer; virtual; // at PPI 96
    function  PreferedWidthAtCurrentPPI: Integer; virtual;
    procedure SetBounds(ALeft, ATop, AHeight: Integer);
    procedure DoAutoSize;
    procedure SetAutoSize(const AValue : boolean); virtual;
    procedure SetVisible(const AValue : boolean); virtual;
    procedure GutterVisibilityChanged; virtual;
    procedure SetWidth(const AValue : integer); virtual;
    procedure Init; override;
    procedure VisibilityOrSize(aCallDoChange: Boolean = False);
    procedure DoResize(Sender: TObject); virtual;
    procedure DoChange(Sender: TObject); virtual;
    property GutterParts: TSynGutterPartListBase read GetGutterParts;
    property Gutter: TSynGutterBase read FGutter;
    property SynEdit:TSynEditBase read FSynEdit;
    property GutterArea: TLazSynSurfaceWithText read GetGutterArea;
  public
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy; override;
    procedure Assign(Source: TPersistent); override;
    property Left: Integer read FLeft;
    property Top: Integer read FTop;
    property Height:Integer read FHeight;
    procedure PaintAll(Canvas: TCanvas; AClip: TRect; FirstLine, LastLine: integer); virtual;
    procedure Paint(Canvas: TCanvas; AClip: TRect; FirstLine, LastLine: integer); virtual; abstract;
    procedure ScalePPI(const AScaleFactor: Double); virtual;
  public
    // X/Y are relative to the gutter, not the gutter part
    function HasCustomPopupMenu(out PopMenu: TPopupMenu): Boolean; virtual;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); virtual;
    procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
    function MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo;
                 HandleActionProc: TSynEditMouseActionHandler): Boolean; virtual;
    function DoHandleMouseAction(AnAction: TSynEditMouseAction;
                                 var AnInfo: TSynEditMouseActionInfo): Boolean; virtual;
    procedure ResetMouseActions; virtual; // set mouse-actions according to current Options / may clear them
    procedure DoOnGutterClick(X, Y: integer);  virtual;
    property OnGutterClick: TGutterClickEvent
      read FOnGutterClick write FOnGutterClick;
    property OnChange: TNotifyEvent read FOnChange write FOnChange;
    property Cursor: TCursor read FCursor write FCursor default crDefault;
    property MarkupInfo: TSynSelectedColor read FMarkupInfo write SetMarkupInfo;
  published
    property AutoSize: boolean read FAutoSize write SetAutoSize default True;
    property Width: integer read FWidth write SetWidth default 10;
    property FullWidth: integer read GetFullWidth; // includes Offsets
    property LeftOffset: integer read FLeftOffset write SetLeftOffset default 0;
    property RightOffset: integer read FRightOffset write SetRightOffset default 0;
    property Visible: boolean read FVisible write SetVisible default True;
    property MouseActions: TSynEditMouseActions
      read GetMouseActions write SetMouseActions;
  end;


const
  ScrollBarWidth=0;

implementation

{ TSynGutterBase }

constructor TSynGutterBase.Create(AOwner: TSynEditBase; ASide: TSynGutterSide;
  ATextDrawer: TheTextDrawer);
begin
  FOnResizeHandler := TMethodList.Create;
  FOnChangeHandler := TMethodList.Create;

  inherited Create;
  FSide := ASide;
  FSynEdit := AOwner;
  CreatePartList;
  FMouseActions := CreateMouseActions;


  FInDoChange := False;
  FChangeLock := 0;
  FTextDrawer := ATextDrawer;
  FWidth := -1;
  FLeftOffset := 0;
  FRightOffset := 0;
  FColor := clBtnFace;
  FVisible := True;
  AutoSize := True;
end;

destructor TSynGutterBase.Destroy;
begin
  FOnChange := nil;
  FOnResize := nil;
  FreeAndNil(FGutterPartList);
  FreeAndNil(FMouseActions);
  FreeAndNil(FOnChangeHandler);
  FreeAndNil(FOnResizeHandler);
  inherited Destroy;
end;

procedure TSynGutterBase.Assign(Source: TPersistent);
begin
  if Source is TSynGutterBase then
  begin
    IncChangeLock;
    try
      FGutterPartList.Assign(TSynGutterBase(Source).FGutterPartList);
      Color    := TSynGutterBase(Source).FColor;
      Visible  := TSynGutterBase(Source).FVisible;
      AutoSize := TSynGutterBase(Source).FAutoSize;
      Width     := TSynGutterBase(Source).FWidth;
      LeftOffset  := TSynGutterBase(Source).FLeftOffset;
      RightOffset := TSynGutterBase(Source).FRightOffset;
    finally
      DecChangeLock;
    end;
  end else
    inherited;
end;

procedure TSynGutterBase.RecalcBounds;
var
  NewTop, NewLeft, NewHeight: Integer;
begin
  // gutters act as alLeft or alRight, so Width is not computed here
  NewTop := 0;
  case FSide of
    gsLeft:
      begin
        NewLeft   := 0;
        NewHeight := SynEdit.ClientHeight;
      end;
    gsRight:
      begin
        NewLeft   := SynEdit.ClientWidth - Width - ScrollBarWidth;
        NewHeight := SynEdit.ClientHeight;
      end;
  end;
  if (NewLeft = FLeft) and (NewTop = FTop) and (NewHeight = FHeight) then
    exit;
  FLeft   := NewLeft;
  FTop    := NewTop;
  FHeight := NewHeight;

  //Resize parts
  IncChangeLock;
  try
    SetChildBounds;
    DoResize(Self);
  finally
    DecChangeLock;
  end;
end;

function TSynGutterBase.MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo;
  HandleActionProc: TSynEditMouseActionHandler): Boolean;
begin
  Result := HandleActionProc(FMouseActions.GetActionsForOptions(SynEdit.MouseOptions), AnInfo);
end;

procedure TSynGutterBase.ResetMouseActions;
begin
  FMouseActions.Options := SynEdit.MouseOptions;
  FMouseActions.ResetUserActions;
end;

procedure TSynGutterBase.ScalePPI(const AScaleFactor: Double);
var
  I: Integer;
begin
  for I := 0 to PartCount-1 do
    Parts[I].ScalePPI(AScaleFactor);
end;

procedure TSynGutterBase.RegisterResizeHandler(AHandler: TNotifyEvent);
begin
  FOnResizeHandler.Add(TMethod(AHandler));
end;

procedure TSynGutterBase.UnregisterResizeHandler(AHandler: TNotifyEvent);
begin
  FOnResizeHandler.Remove(TMethod(AHandler));
end;

procedure TSynGutterBase.RegisterChangeHandler(AHandler: TNotifyEvent);
begin
  FOnChangeHandler.Add(TMethod(AHandler));
end;

procedure TSynGutterBase.UnregisterChangeHandler(AHandler: TNotifyEvent);
begin
  FOnChangeHandler.Remove(TMethod(AHandler));
end;

procedure TSynGutterBase.SetColor(const Value: TColor);
begin
  if FColor <> Value then
  begin
    FColor := Value;
    DoChange(Self);
  end;
end;

procedure TSynGutterBase.SetAutoSize(const AValue: boolean);
begin
  if FAutoSize = AValue then exit;
  FAutoSize := AValue;
  if FAutoSize then
    DoAutoSize;
end;

function TSynGutterBase.GetMouseActions: TSynEditMouseActions;
begin
  Result := FMouseActions.UserActions;
end;

procedure TSynGutterBase.SetGutterParts(const AValue: TSynGutterPartListBase);
begin
  FGutterPartList.Assign(AValue);
end;

procedure TSynGutterBase.SetLeftOffset(const AValue: integer);
begin
  if FLeftOffset <> AValue then
  begin
    FLeftOffset := Max(0, AValue);
    DoChange(Self);
  end;
end;

procedure TSynGutterBase.SetMouseActions(const AValue: TSynEditMouseActions);
begin
  FMouseActions.UserActions := AValue;
end;

procedure TSynGutterBase.SetRightOffset(const AValue: integer);
begin
  if FRightOffset <> AValue then
  begin
    FRightOffset := Max(0, AValue);
    DoChange(Self);
  end;
end;

procedure TSynGutterBase.SetVisible(const AValue: boolean);
var
  i: Integer;
begin
  if FVisible <> AValue then
  begin
    FVisible := AValue;
    DoResize(Self);
    DoChange(Self);
    for i := 0 to PartCount - 1 do
      Parts[i].GutterVisibilityChanged;
  end;
end;

procedure TSynGutterBase.SetWidth(Value: integer);
begin
  Value := Max(0, Value);
  if (FWidth=Value) or (FAutoSize) then exit;
  FWidth := Value;
  DoResize(Self);
end;

procedure TSynGutterBase.DoAutoSize;
var
  NewWidth, i: Integer;
begin
  NewWidth := FLeftOffset + FRightOffset;
  for i := PartCount-1 downto 0 do
    if Parts[i].Visible then
    begin
      if Parts[i].AutoSize then
        Parts[i].DoAutoSize;
      NewWidth := NewWidth + Parts[i].FullWidth;
    end;

  if FWidth = NewWidth then begin
    SetChildBounds;
    exit;
  end;

  IncChangeLock;
  try
    FWidth := NewWidth;
    Include(FFlags, gfNeedChildBounds);

    RecalcBounds;

    if gfNeedChildBounds in FFlags then
      SetChildBounds;

    DoResize(Self);
  finally
    DecChangeLock;
  end;
end;

procedure TSynGutterBase.SetChildBounds;
var
  i, NewLeft: Integer;
begin
  NewLeft := Left + LeftOffset;
  for i := 0 to PartCount - 1 do
    if Parts[i].Visible then begin
      Parts[i].SetBounds(NewLeft, Top, Height);
      NewLeft := NewLeft + Parts[i].FullWidth;
    end;
  Exclude(FFlags, gfNeedChildBounds);
end;

function TSynGutterBase.PartCount: integer;
begin
  if FGutterPartList <> nil then
    result := FGutterPartList.Count
  else
    Result := 0;
end;

function TSynGutterBase.CreateMouseActions: TSynEditMouseInternalActions;
begin
  Result := TSynEditMouseInternalActions.Create(Self);
end;

procedure TSynGutterBase.DoChange(Sender: TObject);
begin
  if FInDoChange then exit;
  if FChangeLock > 0 then begin;
    FNeedOnChange := True;
    exit;
  end;
  FNeedOnChange := False;
  If FAutoSize then begin
    FInDoChange := True;
    try
      DoAutoSize;
    finally
      FInDoChange := False;
    end;
  end;
  FOnChangeHandler.CallNotifyEvents(Self);
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

procedure TSynGutterBase.DoResize(Sender: TObject);
begin
  if FChangeLock > 0 then begin;
    FNeedOnResize := True;
    exit;
  end;
  FNeedOnResize := False;
  FOnResizeHandler.CallNotifyEvents(Self);
  if Assigned(FOnResize) then
    FOnResize(Self)
  else
    DoChange(Self);
end;

procedure TSynGutterBase.IncChangeLock;
begin
  inc(FChangeLock);
end;

procedure TSynGutterBase.DecChangeLock;
begin
  dec(FChangeLock);
  if FChangeLock = 0 then begin
    if FNeedOnResize then
      DoResize(Self);
    if FNeedOnChange then
      DoChange(Self);
  end;
end;

procedure TSynGutterBase.DoDefaultGutterClick(Sender: TObject; X, Y, Line: integer; mark: TSynEditMark);
begin
end;

procedure TSynGutterBase.RegisterNewGutterPartList(APartList: TSynGutterPartListBase);
begin
  if (APartList = nil) or (APartList = FGutterPartList) then
    exit;
  if FGutterPartList <> nil then
    FreeAndNil(FGutterPartList);
  FGutterPartList := APartList;
  FGutterPartList.OnChange := @DoChange;
end;

procedure TSynGutterBase.Clear;
var
  i: Integer;
begin
  if FGutterPartList = nil then exit;
  for i := FGutterPartList.Count - 1 downto 0 do
    FGutterPartList[i].Free;
  FGutterPartList.Clear;
end;

function TSynGutterBase.GetOwner: TPersistent;
begin
  Result := FSynEdit;
end;

{ TSynGutterPartBase }

function TSynGutterPartBase.GetGutterParts: TSynGutterPartListBase;
begin
  Result := TSynGutterPartListBase(Owner);
end;

function TSynGutterPartBase.GetMouseActions: TSynEditMouseActions;
begin
  Result := FMouseActions.UserActions;
end;

procedure TSynGutterPartBase.SetLeftOffset(AValue: integer);
begin
  if FLeftOffset = AValue then Exit;
  FLeftOffset := AValue;
  VisibilityOrSize;
end;

function TSynGutterPartBase.GetGutterArea: TLazSynSurfaceWithText;
begin
  Result := Gutter.GutterArea;
end;

function TSynGutterPartBase.GetFullWidth: integer;
begin
  Result := FWidth + FLeftOffset + FRightOffset;
end;

procedure TSynGutterPartBase.SetMarkupInfo(const AValue: TSynSelectedColor);
begin
  FMarkupInfo.Assign(AValue);
end;

procedure TSynGutterPartBase.SetMouseActions(const AValue: TSynEditMouseActions);
begin
  FMouseActions.UserActions := AValue;
end;

procedure TSynGutterPartBase.SetRightOffset(AValue: integer);
begin
  if FRightOffset = AValue then Exit;
  FRightOffset := AValue;
  VisibilityOrSize;
end;

function TSynGutterPartBase.PreferedWidth: Integer;
begin
  Result := 12;
end;

function TSynGutterPartBase.PreferedWidthAtCurrentPPI: Integer;
begin
  Result := Scale96ToFont(PreferedWidth);
end;

procedure TSynGutterPartBase.SetBounds(ALeft, ATop, AHeight: Integer);
begin
  if (ALeft = FLeft) and (ATop = FTop) and (AHeight = FHeight) then
    exit;
  FLeft   := ALeft;
  FTop    := ATop;
  FHeight := AHeight;
  DoResize(Self);
end;

procedure TSynGutterPartBase.DoAutoSize;
var
  NewWidth: Integer;
begin
  NewWidth := PreferedWidthAtCurrentPPI;
  if FWidth = NewWidth then exit;
  FWidth := NewWidth;
  VisibilityOrSize;
end;

procedure TSynGutterPartBase.SetAutoSize(const AValue : boolean);
var
  OldSize: Integer;
begin
  if FAutoSize=AValue then exit;
  FAutoSize:=AValue;
  OldSize := FWidth;
  if FAutoSize then
    DoAutoSize;
  if FWidth = OldSize then
    DoChange(Self); // Size Did not Change
end;

procedure TSynGutterPartBase.SetVisible(const AValue : boolean);
begin
  if FVisible=AValue then exit;
  FVisible:=AValue;
  VisibilityOrSize(True);
end;

procedure TSynGutterPartBase.GutterVisibilityChanged;
begin
  //
end;

procedure TSynGutterPartBase.SetWidth(const AValue : integer);
begin
  if (FWidth=AValue) or ((FAutoSize) and not(csLoading in ComponentState)) then exit;
  FWidth:=AValue;
  VisibilityOrSize;
end;

procedure TSynGutterPartBase.DoChange(Sender : TObject);
begin
  if Assigned(FOnChange) then
    FOnChange(Self);
end;

function TSynGutterPartBase.CreateMouseActions: TSynEditMouseInternalActions;
begin
  Result := TSynEditMouseInternalActions.Create(Self);
end;

function TSynGutterPartBase.Scale96ToFont(const ASize: Integer): Integer;
begin
  Result := ASize;
  if SynEdit<>nil then
    Result := SynEdit.Scale96ToFont(Result);
end;

constructor TSynGutterPartBase.Create(AOwner: TComponent);
begin
  FMarkupInfo := TSynSelectedColor.Create;
  FMarkupInfo.Background := clBtnFace;
  FMarkupInfo.Foreground := clNone;
  FMarkupInfo.FrameColor := clNone;
  FMarkupInfo.OnChange := @DoChange;

  FMouseActions := CreateMouseActions;

  FVisible := True;
  FAutoSize := True;
  FLeftOffset := 0;
  FRightOffset := 0;
  Inherited Create(AOwner); // Todo: Lock the DoChange from RegisterItem, and call DoChange at the end (after/in autosize)
end;

procedure TSynGutterPartBase.Init;
begin
  inherited Init;
  FGutter := GutterParts.Gutter;
  FSynEdit := GutterParts.SynEdit;
  FriendEdit := FSynEdit;
end;

procedure TSynGutterPartBase.VisibilityOrSize(aCallDoChange: Boolean);
begin
  if (csLoading in ComponentState) then exit;
  Gutter.IncChangeLock;
  try
    if Gutter.AutoSize then
      Gutter.DoAutoSize;  // Calculate new total width of gutter
    DoResize(Self);
    if aCallDoChange then
      DoChange(Self);
  finally
    Gutter.DecChangeLock;
  end;
end;

procedure TSynGutterPartBase.DoResize(Sender: TObject);
begin
  DoChange(Sender);
end;

destructor TSynGutterPartBase.Destroy;
begin
  inherited Destroy;
  FreeAndNil(FMouseActions);
  FreeAndNil(FMarkupInfo);
end;

procedure TSynGutterPartBase.Assign(Source : TPersistent);
var
  Src: TSynGutterPartBase;
begin
  if Source is TSynGutterPartBase then
  begin
    Src := TSynGutterPartBase(Source);
    FVisible := Src.FVisible;
    FWidth := Src.FWidth;
    FAutoSize := Src.FAutoSize;
    MarkupInfo.Assign(Src.MarkupInfo);
    DoChange(Self);
    // Todo, maybe on Resize?
  end else
    inherited;
end;

procedure TSynGutterPartBase.PaintAll(Canvas: TCanvas; AClip: TRect; FirstLine,
  LastLine: integer);
var
  OffsRect: TRect;
begin
  if not Visible then exit;

    if MarkupInfo.Background = clNone then
  begin
    Paint(Canvas, AClip, FirstLine, LastLine);
    exit;
  end;

  if FLeftOffset > 0 then begin
    OffsRect := AClip;
    OffsRect.Left := FLeft;
    OffsRect.Right := FLeft + FLeftOffset;

    Canvas.Brush.Color := MarkupInfo.Background;
    LCLIntf.SetBkColor(Canvas.Handle, TColorRef(Canvas.Brush.Color));
    Canvas.FillRect(OffsRect);
  end;

  if FRightOffset > 0 then begin
    OffsRect := AClip;
    OffsRect.Right := FLeft + FullWidth;
    OffsRect.Left := OffsRect.Right - FRightOffset;

    Canvas.Brush.Color := MarkupInfo.Background;
    LCLIntf.SetBkColor(Canvas.Handle, TColorRef(Canvas.Brush.Color));
    Canvas.FillRect(OffsRect);
  end;

  AClip.Left := AClip.Left + FLeftOffset;
  AClip.Right := AClip.Right - FRightOffset;
  Paint(Canvas, AClip, FirstLine, LastLine);
end;

function TSynGutterPartBase.HasCustomPopupMenu(out PopMenu: TPopupMenu): Boolean;
begin
  Result := False;
end;

procedure TSynGutterPartBase.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  Y: Integer);
begin
end;

procedure TSynGutterPartBase.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
end;

procedure TSynGutterPartBase.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
end;

function TSynGutterPartBase.MaybeHandleMouseAction(var AnInfo: TSynEditMouseActionInfo;
  HandleActionProc: TSynEditMouseActionHandler): Boolean;
begin
  Result := HandleActionProc(FMouseActions.GetActionsForOptions(SynEdit.MouseOptions), AnInfo);
end;

function TSynGutterPartBase.DoHandleMouseAction(AnAction: TSynEditMouseAction;
  var AnInfo: TSynEditMouseActionInfo): Boolean;
begin
  Result := False;
end;

procedure TSynGutterPartBase.ResetMouseActions;
begin
  FMouseActions.Options := SynEdit.MouseOptions;
  FMouseActions.ResetUserActions;
end;

procedure TSynGutterPartBase.ScalePPI(const AScaleFactor: Double);
begin
  if not FAutoSize then
    Width := Round(Width*AScaleFactor)
  else
    DoAutoSize;
end;

procedure TSynGutterPartBase.DoOnGutterClick(X, Y : integer);
begin
  if Assigned(FOnGutterClick) then
    FOnGutterClick(Self, X, Y, 0, nil);
end;

{ TSynGutterPartListBase }

constructor TSynGutterPartListBase.Create(AOwner: TComponent);
begin
  Inherited Create(AOwner);
  include(FComponentStyle, csTransient);
  if (FGutter = nil) and (SynEdit.FindGutterFromGutterPartList(Self) <> nil) then
    FGutter := SynEdit.FindGutterFromGutterPartList(Self) as TSynGutterBase;
  Gutter.RegisterNewGutterPartList(self);
end;

constructor TSynGutterPartListBase.Create(AOwner: TComponent; AGutter: TSynGutterBase);
begin
  FGutter := AGutter;
  Create(AOwner);
end;

destructor TSynGutterPartListBase.Destroy;
begin
  FGutter.FGutterPartList := nil;
  OnChange := nil;
  inherited Destroy;
end;

procedure TSynGutterPartListBase.RegisterItem(AnItem: TSynObjectListItem);
begin
  TSynGutterPartBase(AnItem).OnChange := @DoChange;
  TSynGutterPartBase(AnItem).OnGutterClick := @Gutter.DoDefaultGutterClick;
  inherited RegisterItem(AnItem);
end;

function TSynGutterPartListBase.GetSynEdit: TSynEditBase;
begin
  Result := TSynEditBase(Owner);
end;

function TSynGutterPartListBase.GetPart(Index: Integer): TSynGutterPartBase;
begin
  Result := TSynGutterPartBase(BaseItems[Index]);
end;

procedure TSynGutterPartListBase.PutPart(Index: Integer; const AValue: TSynGutterPartBase);
begin
  BaseItems[Index] := TSynObjectListItem(AValue);
end;

function TSynGutterPartListBase.GetByClass(AClass: TSynGutterPartBaseClass; Index: Integer): TSynGutterPartBase;
var
  i: Integer;
begin
  for i := 0 to Count -1 do
    if Part[i] is AClass then begin
      if Index = 0 then
        exit(Part[i]);
      dec(Index);
    end;
  Result := nil;
end;

function TSynGutterPartListBase.GetByClassCount(AClass: TSynGutterPartBaseClass): Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := 0 to Count -1 do
    if Part[i] is AClass then
      inc(Result);
end;

end.

