{
 *****************************************************************************
  See the file COPYING.modifiedLGPL.txt, included in this distribution,
  for details about the license.
 *****************************************************************************

  Abstract:
    Mini-Map panel control
}
unit PnlMiniMap;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Controls, ExtCtrls, SynEdit, SrcEditorIntf, Graphics, lclType,
  SynEditMarkupSpecialLine, SynEditTypes, SynEditMiscClasses, SynEditMarkupBracket, LazLogger,
  LazLoggerBase;

Const
  DefaultViewFontSize        = 3;
  DefaultViewWindowColor     = TColor($00E3F33F);
  DefaultViewWindowTextColor = clNone;
  DefaultMapWidth            = 200;

Type

  { TMiniMapControl }

  TMiniMapControl = Class(TPanel)
  Private
    FMiniSynEdit: TSynEdit;
    FSourceEditor: TSourceEditorInterface;
    FSourceSynEdit: TCustomSynEdit;
    FViewWindowColor:TColor;
    FViewWindowTextColor:TColor;
    FViewFontSize: Integer;
    procedure ConfigMiniEdit;
    procedure SetSourceEditor(AValue: TSourceEditorInterface);
    procedure SetViewFontSize(AValue: Integer);
    procedure SetViewWindowColor(AValue: TColor);
    procedure SetViewWindowTextColor(AValue: TColor);
    procedure SyncMiniMapProps;
  Protected
    // Event handlers
    procedure HandleLineMarkup(Sender: TObject; Line: integer; var Special: boolean; Markup: TSynSelectedColor); virtual;
    procedure HandleStatusChange(Sender: TObject; {%H-}Changes: TSynStatusChanges); virtual;
    Procedure HandleClick({%H-}aSender : TObject); virtual;
    Procedure SyncViewWindow;
    Property MiniSynEdit : TSynEdit Read FMiniSynEdit;
    Property SourceSynEdit : TCustomSynEdit Read FSourceSynEdit;
  Public
    constructor Create(aOwner : TComponent); override;
    destructor Destroy; override;
    Procedure Reconfigure;
    procedure UnHook;
    Property SourceEditor: TSourceEditorInterface Read FSourceEditor Write SetSourceEditor;
    Property ViewWindowColor : TColor Read FViewWindowColor Write SetViewWindowColor;
    Property ViewWindowTextColor:TColor Read FViewWindowTextColor Write SetViewWindowTextColor;
    Property ViewFontSize : Integer Read FViewFontSize Write SetViewFontSize;
  end;

implementation

uses SynEditKeyCmds;

{ TMiniMapControl }

procedure TMiniMapControl.HandleLineMarkup(Sender: TObject; Line: integer;
  var Special: boolean; Markup: TSynSelectedColor);

var
  TopLine,BottomLine: Integer;

begin
  TopLine:=FSourceSynEdit.TopLine;
  BottomLine:=TopLine+FSourceSynEdit.LinesInWindow;
  if (Line>=TopLine) and (Line<=BottomLine) then
    begin
    Markup.Background:=FViewWindowColor;
    Markup.Foreground:=FViewWindowTextColor;
    Special:=True;
    end;
end;

procedure TMiniMapControl.HandleStatusChange(Sender: TObject;
  Changes: TSynStatusChanges);
begin
  SyncViewWindow;
end;

procedure TMiniMapControl.SetSourceEditor(AValue: TSourceEditorInterface);
begin
  if FSourceEditor=AValue then Exit;
  FSourceEditor:=AValue;
  FSourceSynEdit:=TCustomSynEdit(FSourceEditor.EditorControl);
  if Assigned(FSourceSynEdit) then
    begin
    SyncMiniMapProps;
    SyncViewWindow;
    end;
end;

procedure TMiniMapControl.SetViewFontSize(AValue: Integer);
begin
  if ViewFontSize=AValue then Exit;
  FViewFontSize:=AValue;
  FMiniSynEdit.Font.Size:=FViewFontSize;
end;

procedure TMiniMapControl.SetViewWindowColor(AValue: TColor);
begin
  if FViewWindowColor=AValue then Exit;
  FViewWindowColor:=AValue;
  FMiniSynEdit.Invalidate;
end;

procedure TMiniMapControl.SetViewWindowTextColor(AValue: TColor);
begin
  if FViewWindowTextColor=AValue then Exit;
  FViewWindowTextColor:=AValue;
  FMiniSynEdit.Invalidate;
end;

procedure TMiniMapControl.HandleClick(aSender: TObject);
begin
  SourceSynEdit.TopLine:=FMiniSynEdit.CaretY-(FSourceSynEdit.LinesInWindow div 2); //centered.
  SyncViewWindow;
end;

procedure TMiniMapControl.SyncViewWindow;

var
  CurrTop,CurrBottom : Integer;

begin
  if (FSourceSynEdit=nil) then
    Exit;
  CurrTop:=FSourceSynEdit.TopLine;
  CurrBottom:=CurrTop+FSourceSynEdit.LinesInWindow;
  if (CurrTop<FMiniSynEdit.TopLine) then
    FMiniSynEdit.TopLine:=CurrTop
  else if (CurrBottom>(FMiniSynEdit.TopLine+FMiniSynEdit.LinesInWindow)) then
    FMiniSynEdit.TopLine:=CurrBottom-FMiniSynEdit.LinesInWindow;
  FMiniSynEdit.Invalidate;
end;


procedure TMiniMapControl.SyncMiniMapProps;
begin
  With FMiniSynEdit do
    begin
    Font:=FSourceSynEdit.Font;
    Font.Size:=FViewFontSize;
    ShareTextBufferFrom(FSourceSynEdit);
    Highlighter:=FSourceSynEdit.Highlighter;
    RightEdge:=FSourceSynEdit.RightEdge;
    RightEdgeColor:=FSourceSynEdit.RightEdgeColor;
    Color:=FSourceSynEdit.Color;
    FSourceSynEdit.RegisterStatusChangedHandler(@HandleStatusChange,[scTopLine, scLinesInWindow]);
    end;
end;

procedure TMiniMapControl.ConfigMiniEdit;

  Procedure AddKey(aCommand : TSynEditorCommand; aShortCut : TShortCut);

  begin
    With FMiniSynEdit.Keystrokes.Add do
      begin
      Command:=aCommand;
      ShortCut:=aShortCut;
      end;
  end;

var
  I : integer;

begin
  With FMiniSynEdit do
    begin
    Left:=0;
    Top:=0;
    Align:=alClient;
    ParentColor:=False;
    ParentFont:=False;

    Font.Name := SynDefaultFontName;
    Font.Pitch := fpFixed;
    Font.Quality := fqNonAntialiased;
    Gutter.Visible := False;
    Gutter.Width := 57;
    RightGutter.Width := 0;
    VisibleSpecialChars := [vscSpace, vscTabAtLast]  ;
    SelectedColor.BackPriority := 50;
    SelectedColor.ForePriority := 50;
    SelectedColor.FramePriority := 50;
    SelectedColor.BoldPriority := 50;
    SelectedColor.ItalicPriority := 50;
    SelectedColor.UnderlinePriority := 50;
    SelectedColor.StrikeOutPriority := 50;
    BracketHighlightStyle := sbhsBoth;
    BracketMatchColor.Background := clNone;
    BracketMatchColor.Foreground := clNone;
    BracketMatchColor.Style := [fsBold];
    FoldedCodeColor.Background := clNone;
    FoldedCodeColor.Foreground := clGray;
    FoldedCodeColor.FrameColor := clGray;
    MouseLinkColor.Background := clNone;
    MouseLinkColor.Foreground := clBlue;
    LineHighlightColor.Background := clNone;
    LineHighlightColor.Foreground := clNone;
    end;
  SourceEditorManagerIntf.GetEditorControlSettings(FMiniSynEdit);
//  FMiniSynEdit.SelectionMode:=;
  FMiniSynEdit.Keystrokes.Clear;
  AddKey(ecUp,KeyToShortCut(VK_UP,[]));
  AddKey(ecScrollUp,KeyToShortCut(VK_UP,[ssCtrl]));
  AddKey(ecDown,KeyToShortCut(VK_DOWN,[ssCtrl]));
  AddKey(ecPageDown,KeyToShortCut(VK_NEXT,[]));
  AddKey(ecPageBottom,KeyToShortCut(VK_NEXT,[ssCtrl]));
  AddKey(ecPageUp,KeyToShortCut(VK_PRIOR,[]));
  AddKey(ecPageTop,KeyToShortCut(VK_PRIOR,[ssCtrl]));
  AddKey(ecEditorTop,KeyToShortCut(VK_HOME,[]));
  AddKey(ecEditorBottom,KeyToShortCut(VK_END,[]));

  FMiniSynEdit.Font.Size:=FViewFontSize;
  FMiniSynEdit.ReadOnly := True;
  FMiniSynEdit.Gutter.Visible := False;
  FMiniSynEdit.OnClick := @HandleClick;
  FMiniSynEdit.OnSpecialLineMarkup := @HandleLineMarkup;
  FMiniSynEdit.Options:=[eoNoCaret,eoNoSelection];
  FMiniSynEdit.Options2:=[];
  FMiniSynEdit.BookMarkOptions.EnableKeys:=False;
  FMiniSynEdit.BookMarkOptions.GlyphsVisible:=False;
  FMiniSynEdit.BookMarkOptions.DrawBookmarksFirst:=False;
  For I:=0 to FMiniSynEdit.Gutter.Parts.Count-1 do
    FMiniSynEdit.Gutter.Parts[I].Visible:=True;
//  FMiniSynEdit.Gutter.Parts[4].Visible := False; // code folding disabled.
end;


constructor TMiniMapControl.Create(aOwner: TComponent);
begin
  Inherited;
  BevelInner:=bvNone;
  BevelOuter:=bvNone;
  FMiniSynEdit:=TSynEdit.Create(Self);
  FMiniSynEdit.Parent:=Self;
  FViewFontSize:=DefaultViewFontSize;
  FViewWindowColor:=DefaultViewWindowColor;
  FViewWindowTextColor:=DefaultViewWindowTextColor;
  ConfigMiniEdit;
end;


procedure TMiniMapControl.UnHook;

begin
  if Not Assigned(FSourceSynEdit) then
    exit;
  FMiniSynedit.UnShareTextBuffer;
  FSourceSynEdit.UnRegisterStatusChangedHandler(@HandleStatusChange);
  FSourceSynEdit:=nil;
  FSourceEditor:=nil;
end;

destructor TMiniMapControl.Destroy;
begin
  Unhook;
  inherited destroy;
end;

procedure TMiniMapControl.Reconfigure;
begin
  FMiniSynEdit.Highlighter:=FSourceSynEdit.Highlighter;
end;

end.

