Guest User

MiniMap

a guest
Feb 13th, 2013
100
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (*******************************************************************************
  2.   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  3. /    Contributors(alphabetical order):    /
  4. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  5.  
  6.   Dorin Duminica - http://www.delphigeist.com
  7.  
  8. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  9.  
  10.   Change Log:
  11.  
  12.  ~~~~~~~~~~~~~~~~
  13. /    v 1.1     /
  14. ~~~~~~~~~~~~~~~
  15.  
  16.     new:
  17.     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  18.     -   editor scroll from MiniMap
  19.     -   options class(to be extended)
  20.     -   published a few more properties
  21.  
  22.     fixes:
  23.     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  24.     -   fixed flickering on scroll
  25.     -   fixed line number calculation on click
  26.  
  27.     other:
  28.     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  29.     -   cleanups
  30.  
  31.     known issues:
  32.     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  33.     -   translation of "char" when clicking the map is
  34.       not always correct needs rewrite
  35.  
  36.  ~~~~~~~~~~~~~~~
  37. /    v 1.0    /
  38. ~~~~~~~~~~~~~~
  39.  
  40.     -   initial release
  41.  
  42. *******************************************************************************)
  43. unit SynMiniMap;
  44.  
  45. interface
  46.  
  47. uses
  48.    SysUtils, Classes, Controls, Graphics,
  49.    {$IFDEF FPC}
  50.     LCLIntf, LCLType, SynEditPlugins, SynEditKeyCmds
  51.    {$ELSE}
  52.    Windows
  53.    {$ENDIF}, SynEdit;
  54.  
  55. const
  56.   SYNMINIMAP_DEFAULT_HEIGHT = 400;
  57.   SYNMINIMAP_DEFAULT_WIDTH = 200;
  58.   SYNMINIMAP_DEFAULT_FONTFACTOR = 3;
  59.   SYNMINIMAP_FONTFACTOR_MIN = 2;
  60.   SYNMINIMAP_FONTFACTOR_MAX = 4;
  61.   SYNMINIMAP_DEFAULT_OPTIONS_TABWIDTH = 4;
  62.  
  63. type
  64.   ///
  65.   ///  don't modify this, it will be extended to pass other information
  66.   ///  in the future without breaking backwards compatibility
  67.   ///
  68. {$IFDEF FPC}
  69.  TBufferCoord = record
  70.     Char: integer;
  71.     Line: integer;
  72.  end;
  73.  PPoint = ^TPoint;
  74. {$ENDIF}
  75.  
  76.   PSynMiniMapEventData = ^TSynMiniMapEventData;
  77.   TSynMiniMapEventData = record
  78.     Coord: TBufferCoord;
  79.     Redraw: Boolean;
  80.   end;
  81.  
  82. const
  83.   szSynMiniMapEventData = SizeOf(TSynMiniMapEventData);
  84.  
  85. type
  86.   ///
  87.   ///  colors for mini map
  88.   ///
  89.   TSynMiniMapColors = class(TPersistent)
  90.   private
  91.     FBackground: TColor;
  92.     FHighlight: TColor;
  93.     FPreviousLine: TColor;
  94.     FPreviousLineText: TColor;
  95.     FText: TColor;
  96.     FTextHighlight: TColor;
  97.   public
  98.     constructor Create(AOwner: TComponent);
  99.   published
  100.     property Background: TColor read FBackground write FBackground;
  101.     property Highlight: TColor read FHighlight write FHighlight;
  102.     property PreviousLine: TColor read FPreviousLine write FPreviousLine;
  103.     property PreviousLineText: TColor read FPreviousLineText write FPreviousLineText;
  104.     property Text: TColor read FText write FText;
  105.     property TextHighlight: TColor read FTextHighlight write FTextHighlight;
  106.   end;
  107.  
  108.   ///
  109.   ///  various behavioral options
  110.   ///
  111.   TSynMinimapOptions = class(TPersistent)
  112.   private
  113.     FAllowScroll: Boolean;
  114.     FReverseScroll: Boolean;
  115.     FTabWidthOverride: Boolean;
  116.     FTabWidth: Integer;
  117.   public
  118.     constructor Create(AOwner: TComponent);
  119.   published
  120.     ///
  121.     ///  scrolling editor using the MiniMap is possible only if AllowScroll
  122.     ///
  123.     property AllowScroll: Boolean read FAllowScroll write FAllowScroll;
  124.     property ReverseScroll: Boolean read FReverseScroll write FReverseScroll;
  125.     property TabWidthOverride: Boolean read FTabWidthOverride write FTabWidthOverride;
  126.     property TabWidth: Integer read FTabWidth write FTabWidth;
  127.   end;
  128.  
  129.   ///
  130.   ///  event fired under various conditions
  131.   ///
  132.   TSynMiniMapEvent = procedure (Sender: TObject; Data: PSynMiniMapEventData) of Object;
  133.  
  134.  
  135.   ///
  136.   ///  forward declaration
  137.   ///
  138.   TSynMiniMap = class;
  139.  
  140.   ///
  141.   ///  this plugin helps hook a few important events
  142.   ///
  143.   TSynMiniMapEditorPlugin = class({$IFDEF FPC}TAbstractSynSingleHookPlugin{$ELSE}TSynEditPlugin{$ENDIF})
  144.   private
  145.     FSynMiniMap: TSynMiniMap;
  146.  
  147.     procedure RePaint;
  148.     procedure UpdateLines(const FirstLine, Count: integer);
  149.   protected
  150.   {$IFDEF FPC}
  151.    procedure OnCommand(Sender: TObject; AfterProcessing: boolean;
  152.       var Handled: boolean; var Command: TSynEditorCommand;
  153.       var aChar: TUTF8Char;
  154.       Data: pointer; HandlerData: pointer); override;
  155.   {$ELSE}
  156.     procedure AfterPaint(ACanvas: TCanvas; const AClip: TRect;
  157.       FirstLine: Integer; LastLine: Integer); override;
  158.     procedure LinesDeleted(FirstLine: Integer; Count: Integer); override;
  159.     procedure LinesInserted(FirstLine: Integer; Count: Integer); override;
  160.    {$ENDIF}
  161.   public
  162.     constructor Create(ASynMiniMap: TSynMiniMap);
  163.   end;
  164.  
  165.   ///
  166.   ///  the minimap itself
  167.   ///
  168.   TSynMiniMap = class(TCustomControl)
  169.   protected
  170.     procedure Resize; override;
  171.     procedure Paint; override;
  172.     procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X: Integer;
  173.       Y: Integer); override;
  174.     procedure MouseMove(Shift: TShiftState; X: Integer; Y: Integer); override;
  175.     procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X: Integer;
  176.       Y: Integer); override;
  177.     procedure DoClick(const AX, AY: Integer); virtual;
  178.   private
  179.     FFullSizeBitmap: TBitmap;
  180.     FOffsetBitmap: TBitmap;
  181.     FEditor: TSynEdit;
  182.   private
  183.     FEditorHeight: Integer;
  184.     FEditorWidth: Integer;
  185.     FEditorRealWidth: Integer;
  186.     FFirstLine: Integer;
  187.     FLastLine: Integer;
  188.     FMaxCharsPerLine: Word;
  189.     FLineHeightInPixels: Integer;
  190.     FPreviousLineIndex: Integer;
  191.     FFontFactor: Single;
  192.     FCharWidth: Integer;
  193.     FTabWidth: Integer;
  194.     FOptions: TSynMinimapOptions;
  195.     ///
  196.     ///  mouse down & move => scroll
  197.     ///  mouse down + up => click
  198.     ///
  199.     FMouseDownPoint: TPoint;
  200.     FMouseUpPoint: TPoint;
  201.     FScrolling: Boolean;
  202.   private
  203.     FOnClick: TSynMiniMapEvent;
  204.   private
  205.     FColors: TSynMiniMapColors;
  206.     FMiniMapPlugin: TSynMiniMapEditorPlugin;
  207.   private
  208.     function GetClickCoord: TBufferCoord;
  209.     procedure ResetInternals;
  210.     procedure ClearEventData(var AEventData: TSynMiniMapEventData);
  211.     procedure Render; virtual;
  212.   private
  213.     function GetPixelFormat: TPixelFormat;
  214.     procedure SetPixelFormat(const Value: TPixelFormat);
  215.     procedure SetEditor(const Value: TSynEdit);
  216.     procedure SetFontFactor(const Value: Single);
  217.   public
  218.     constructor Create(AOwner: TComponent); override;
  219.     destructor Destroy; override;
  220.   public
  221.     function TranslatePoint(const APoint: PPoint): TBufferCoord;
  222.   public
  223.     property PreviousLineIndex: Integer read FPreviousLineIndex;
  224.     property Scrolling: Boolean read FScrolling;
  225.   published
  226.     property Colors: TSynMiniMapColors read FColors write FColors;
  227.     property Editor: TSynEdit read FEditor write SetEditor;
  228.     property FontFactor: Single read FFontFactor write SetFontFactor;
  229.     property Options: TSynMinimapOptions read FOptions write FOptions;
  230.     property PixelFormat: TPixelFormat read GetPixelFormat write SetPixelFormat;
  231.   published
  232.     property OnClick: TSynMiniMapEvent read FOnClick write FOnClick;
  233.   published
  234.     property Align;
  235.     property Constraints;
  236.     property Height;
  237.     property ShowHint;
  238.     property Width;
  239.     property OnDblClick;
  240.     property OnResize;
  241.   end;
  242.  
  243. procedure Register;
  244.  
  245. implementation
  246.  
  247. uses
  248.   Math
  249.   ,Dialogs
  250.   ;
  251.  
  252. resourcestring
  253.   SFontFactorMinMax = 'Font factor cannot be smaller than %.d or greater than %.d.';
  254.  
  255. procedure Register;
  256. begin
  257.   RegisterComponents('SynEdit', [TSynMiniMap]);
  258. end;
  259.  
  260. {$IFNDEF FPC}
  261. procedure StretchDrawHalftone(Handle: THandle; const AX, AY, AWidth,
  262.   AHeight: Integer; const ASource: TCanvas;
  263.   const ASrcX, ASrcY, ASrcWidth, ASrcHeight: Integer);
  264. begin
  265.   SetStretchBltMode(Handle, HALFTONE);
  266.   StretchBlt(
  267.     Handle,
  268.     AX, AY,
  269.     AWidth, AHeight,
  270.     ASource.Handle,
  271.     ASrcX, ASrcY,
  272.     ASrcWidth, ASrcHeight,
  273.     SRCCOPY
  274.   );
  275. end;
  276. {$ENDIF}
  277.  
  278. { TSynMiniMapColors }
  279.  
  280. constructor TSynMiniMapColors.Create(AOwner: TComponent);
  281. begin
  282.   inherited Create;
  283.   ///
  284.   ///  set default values
  285.   ///
  286.   Background := clWhite;
  287.   Highlight := $f4f4f4;
  288.   PreviousLine := clNone;
  289.   PreviousLineText := clNone;
  290.   Text := clGray;
  291.   TextHighlight := Text;
  292. end;
  293.  
  294. { TSynMinimapOptions }
  295.  
  296. constructor TSynMinimapOptions.Create(AOwner: TComponent);
  297. begin
  298.   inherited Create;
  299.   AllowScroll := False;
  300.   ReverseScroll := False;
  301.   TabWidthOverride := False;
  302.   TabWidth := SYNMINIMAP_DEFAULT_OPTIONS_TABWIDTH;
  303. end;
  304.  
  305. { TSynMiniMapEditorPlugin }
  306. constructor TSynMiniMapEditorPlugin.Create(ASynMiniMap: TSynMiniMap);
  307. begin
  308. {$IFDEF FPC}
  309.  ShortCut := 1;
  310. {$ENDIF}
  311.  
  312.   FSynMiniMap := ASynMiniMap;
  313.   inherited Create(ASynMiniMap.Editor);
  314. end;
  315.  
  316. procedure TSynMiniMapEditorPlugin.Repaint;
  317. begin
  318.   if assigned(FSynMiniMap) then FSynMiniMap.Render;
  319. end;
  320.  
  321. procedure TSynMiniMapEditorPlugin.UpdateLines(const FirstLine, Count: integer);
  322. var
  323.   LLineIndex: Integer;
  324. begin
  325.   ///
  326.   ///  check if we need to decrement/increment the previous line index
  327.   ///  if current line index is 10 and the user deleted a few lines
  328.   ///  before that, we need to adjust FPreviousLineIndex
  329.   ///
  330.   LLineIndex := FirstLine -1;
  331.   if assigned(FSynMiniMap) and (FSynMiniMap.PreviousLineIndex >= LLineIndex) then
  332.   begin
  333.     Inc(FSynMiniMap.FPreviousLineIndex, Count);
  334.     Repaint;
  335.   end;
  336. end;
  337.  
  338. {$IFDEF FPC}
  339. procedure TSynMiniMapEditorPlugin.OnCommand(Sender: TObject; AfterProcessing: boolean;
  340.    var Handled: boolean; var Command: TSynEditorCommand;
  341.    var aChar: TUTF8Char;
  342.    Data: pointer; HandlerData: pointer);
  343. begin
  344.   if AfterProcessing then
  345.   begin
  346.      case Command of
  347.        ecDeleteLine: Repaint;
  348.        ecLineBreak, ecInsertLine: Repaint;
  349.      end;
  350.   end;
  351. end;
  352. {$ELSE}
  353. procedure TSynMiniMapEditorPlugin.AfterPaint(ACanvas: TCanvas;
  354.   const AClip: TRect; FirstLine, LastLine: Integer);
  355. begin
  356.   inherited;
  357.   Repaint;
  358. end;
  359.  
  360. procedure TSynMiniMapEditorPlugin.LinesDeleted(FirstLine, Count: Integer);
  361. begin
  362.   inherited;
  363.   UpdateLines(FirstLine, -Count);
  364. end;
  365.  
  366. procedure TSynMiniMapEditorPlugin.LinesInserted(FirstLine, Count: Integer);
  367. begin
  368.   inherited;
  369.   UpdateLines(FirstLine, Count);
  370. end;
  371. {$ENDIF}
  372.  
  373. { TSynMiniMap }
  374.  
  375. procedure TSynMiniMap.ClearEventData(var AEventData: TSynMiniMapEventData);
  376. begin
  377.   FillChar(AEventData, 0, szSynMiniMapEventData);
  378. end;
  379.  
  380. constructor TSynMiniMap.Create(AOwner: TComponent);
  381. begin
  382.   inherited;
  383.   FFullSizeBitmap := TBitmap.Create;
  384.   FOffsetBitmap := TBitmap.Create;
  385.   FColors := TSynMiniMapColors.Create(Self);
  386.   PixelFormat := pf32bit;
  387.   Self.Height := SYNMINIMAP_DEFAULT_HEIGHT;
  388.   Self.Width := SYNMINIMAP_DEFAULT_WIDTH;
  389.   FMaxCharsPerLine := 100;
  390.   FOptions := TSynMinimapOptions.Create(Self);
  391.   FPreviousLineIndex := -1;
  392.   FFontFactor := SYNMINIMAP_DEFAULT_FONTFACTOR;
  393.   FScrolling := False;
  394. end;
  395.  
  396. destructor TSynMiniMap.Destroy;
  397. begin
  398.   FColors.Free;
  399.   FFullSizeBitmap.Free;
  400.   FOffsetBitmap.Free;
  401.   FOptions.Free;
  402.   inherited;
  403. end;
  404.  
  405. procedure TSynMiniMap.DoClick(const AX, AY: Integer);
  406. var
  407.   LEventData: TSynMiniMapEventData;
  408.   LPoint: TPoint;
  409. begin
  410.   ///
  411.   ///  OnClick has no value unless we have an editor assign
  412.   ///
  413.   if Assigned(FOnClick) and Assigned(FEditor) then begin
  414.     ///
  415.     ///  save previous line index for drawing in Render
  416.     ///
  417.     FPreviousLineIndex := FEditor.CaretY -1;
  418.     ///
  419.     ///  reset event data record
  420.     ///
  421.     ClearEventData(LEventData);
  422.     ///
  423.     ///  set the Line and Char coordonates
  424.     ///
  425.     LPoint.X := AX;
  426.     LPoint.Y := AY;
  427.     LEventData.Coord := TranslatePoint(@LPoint);
  428.     ///
  429.     ///  invoke assigned event
  430.     ///
  431.     FOnClick(Self, @LEventData);
  432.     ///
  433.     ///  check if we were asked to redraw
  434.     ///
  435.     if LEventData.Redraw then
  436.       Render;
  437.   end;
  438. end;
  439.  
  440. function TSynMiniMap.GetClickCoord: TBufferCoord;
  441. var
  442.   LPoint: TPoint;
  443. begin
  444.   ///
  445.   ///  grab the cursor coordonates
  446.   ///
  447.   LPoint := Point(0,0);
  448.   GetCursorPos(LPoint);
  449.   LPoint := Self.ScreenToClient(LPoint);
  450.   Result := TranslatePoint(@LPoint);
  451. end;
  452.  
  453. function TSynMiniMap.GetPixelFormat: TPixelFormat;
  454. begin
  455.   ///
  456.   ///  return the current pixel format
  457.   ///
  458.   Result := FFullSizeBitmap.PixelFormat;
  459. end;
  460.  
  461. procedure TSynMiniMap.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
  462.   Y: Integer);
  463. begin
  464.   inherited;
  465.   FScrolling := ( mbLeft = Button ) and Options.AllowScroll;
  466.   FMouseDownPoint.X := X;
  467.   FMouseDownPoint.Y := Y;
  468. end;
  469.  
  470. procedure TSynMiniMap.MouseMove(Shift: TShiftState; X, Y: Integer);
  471. var
  472.   LDelta: Integer;
  473.   LScrollDown: Boolean;
  474. begin
  475.   inherited;
  476.   if NOT Options.AllowScroll then
  477.     Exit;
  478.   if Scrolling and Assigned(Editor) then begin
  479.     LDelta := FMouseDownPoint.Y - Y;
  480.     LDelta := Trunc(LDelta /  FFontFactor);
  481.     LDelta := Abs(LDelta);
  482.  
  483.     LScrollDown := (Y > FMouseDownPoint.Y);
  484.     if Options.ReverseScroll then
  485.       LScrollDown := NOT LScrollDown;
  486.  
  487.     if LScrollDown then
  488.     //if Y > FMouseDownPoint.Y then
  489.       Editor.CaretY := Editor.CaretY + LDelta
  490.     else
  491.       Editor.CaretY := Editor.CaretY - LDelta;
  492.   end;
  493. end;
  494.  
  495. procedure TSynMiniMap.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
  496.   Y: Integer);
  497. var
  498.   LIsClick: Boolean;
  499. begin
  500.   inherited;
  501.   FScrolling := False;
  502.   FMouseUpPoint.X := X;
  503.   FMouseUpPoint.Y := Y;
  504.   LIsClick := ( NOT Options.AllowScroll )
  505.     or (( FMouseDownPoint.X = FMouseUpPoint.X)
  506.     and (FMouseDownPoint.Y = FMouseUpPoint.Y ));
  507.   if LIsClick then
  508.     DoClick(X, Y);
  509. end;
  510.  
  511. procedure TSynMiniMap.Paint;
  512. begin
  513.   ///
  514.   ///  draw the buffered bitmap
  515.   ///
  516.   Canvas.Draw(0, 0, FOffsetBitmap);
  517. end;
  518.  
  519. procedure TSynMiniMap.Render;
  520. var
  521.   LLineHeight: Integer;
  522.   LLineCount: Integer;
  523.   LLineTop: Integer;
  524.   LLineText: string;
  525.   LMaxLineCount: Integer;
  526.   LDrawHeight: Integer;
  527.   LDrawWidth: Integer;
  528.   LTopLineIndex: Integer;
  529.   LFocusRect: TRect;
  530.   LFocusTopIndex: Integer;
  531.   LPreviousLine: Boolean;
  532.   Index: Integer;
  533.  
  534.     function __get_line_xpos: Integer;
  535.     ///
  536.     ///  this function is counting leading spaces and tabs
  537.     ///  could use improvements
  538.     ///
  539.     var
  540.       cIndex: Integer;
  541.     begin
  542.       Result := 0;
  543.       for cIndex := 1 to Length(LLineText) do
  544.         case LLineText[cIndex] of
  545.           #$9: Inc(Result, FTabWidth);
  546.           ' ': Inc(Result, FCharWidth);
  547.           else
  548.             Break;
  549.         end;
  550.     end;
  551.  
  552. begin
  553.   if ( NOT Assigned(Editor) ) or ( csDestroying in ComponentState ) then
  554.     Exit;
  555.  
  556.   ///
  557.   ///  this is where the magic happens
  558.   ///  what it does:
  559.   ///  -  the MiniMap control has a limited height, we need to translate
  560.   ///   that height into a maximum number of lines we can display from
  561.   ///   the synedit control
  562.   ///  -  create a "full size bitmap"
  563.   ///  -  paint various misc stuff(highlight, previous line, etc.)
  564.   ///  -  paint each line starting from X lines before current visible lines
  565.   ///   and Y lines after
  566.   ///  -  scale down the "full size bitmap" and paint it on to the
  567.   ///   "offset bitmap"
  568.   ///   each time the "pain event" occures, the "offset bitmap" is used to
  569.   ///   draw on the MiniMap control
  570.   ///
  571.  
  572.   ///
  573.   ///  grab the lines
  574.   ///
  575.   LLineCount := Editor.Lines.Count;
  576.   ///
  577.   /// store the first visible line index
  578.   ///  IMPORTANT:
  579.   ///  -  synedit refers to lines as "real index" + 1
  580.   ///
  581.   LTopLineIndex := Editor.TopLine -1;
  582.   ///
  583.   ///  grab the font size of full size bitmap
  584.   ///
  585.   LLineHeight := FFullSizeBitmap.Canvas.Font.Size;
  586.   ///
  587.   ///  add a 2 pixel line spacing
  588.   ///
  589.   Inc(LLineHeight, 2);
  590.   ///
  591.   ///  "shrink" the font to a much small dimension, each character will be
  592.   ///  FontFactor times smaller than the "real thing"
  593.   ///
  594.   FLineHeightInPixels := Trunc( LLineHeight / FFontFactor);
  595.   ///
  596.   ///  calculate the maximum number of lines we can display
  597.   ///  IMPORTANT:
  598.   ///  -  FLineHeightInPixels includes the "line spacing"
  599.   ///
  600.   LMaxLineCount := Self.Height div FLineHeightInPixels;
  601.   ///
  602.   ///  calculate the smalles value of lines we will display
  603.   ///  if the lines in editor are more than we can display
  604.   ///  then we display the maximum possible, otherwise display as many
  605.   ///  as there are in the editor
  606.   ///
  607.   LLineCount := max(LLineCount, LMaxLineCount);
  608.   ///
  609.   ///  setup the full size bitmap dimensions
  610.   ///
  611.   FFullSizeBitmap.Height := LLineCount * LLineHeight;
  612.   ///
  613.   ///  setup the background color and fill it
  614.   ///
  615.   FFullSizeBitmap.Canvas.Brush.Color := Colors.Background;
  616.   FFullSizeBitmap.Canvas.FillRect(Rect(0, 0, FFullSizeBitmap.Width, FFullSizeBitmap.Height));
  617.   ///
  618.   ///  calculate the first and last lines that we will grab from editor
  619.   ///  and display in the MiniMap
  620.   ///
  621.   FFirstLine := LTopLineIndex - ( LMaxLineCount div 2 ) + (Editor.LinesInWindow div 2);
  622.   FFirstLine := Max(FFirstLine, 0);
  623.   FLastLine := Min(FFirstLine + LLineCount, Editor.Lines.Count -1);
  624.   ///
  625.   ///  setup brush and pen
  626.   ///
  627.   FFullSizeBitmap.Canvas.Brush.Style := bsSolid;
  628.   FFullSizeBitmap.Canvas.Brush.Color := Colors.Highlight;
  629.   FFullSizeBitmap.Canvas.Pen.Style := psClear;
  630.   ///
  631.   ///  highlight visible lines with provided color
  632.   ///
  633.   LFocusTopIndex := LTopLineIndex - FFirstLine;
  634.   LFocusRect := Rect(
  635.     0,
  636.     LFocusTopIndex * LLineHeight,
  637.     FFullSizeBitmap.Width,
  638.     ( LFocusTopIndex + Editor.LinesInWindow ) * LLineHeight
  639.   );
  640.   FFullSizeBitmap.Canvas.Rectangle(LFocusRect);
  641.   ///
  642.   ///  check if we need to hightlight previous line
  643.   ///  previous line is saved when the user clicks on the MiniMap
  644.   ///  on MiniMap's OnClick event you can jump to the clicked line
  645.   ///
  646.   LPreviousLine := ( Colors.PreviousLine <> clNone ) and
  647.     ( PreviousLineIndex >= FFirstLine ) and ( PreviousLineIndex <= FLastLine );
  648.   if LPreviousLine then begin
  649.     FFullSizeBitmap.Canvas.Brush.Color := Colors.PreviousLine;
  650.     LFocusRect := Rect(
  651.       0,
  652.       ( PreviousLineIndex - FFirstLine ) * LLineHeight,
  653.       FFullSizeBitmap.Width,
  654.       ( PreviousLineIndex - FFirstLine  + 1) * LLineHeight);
  655.     FFullSizeBitmap.Canvas.Rectangle(LFocusRect);
  656.   end;
  657.   ///
  658.   ///  set the brush style to clear, otherwise we get uggly background color
  659.   ///  for each line
  660.   ///
  661.   FFullSizeBitmap.Canvas.Brush.Style := bsClear;
  662.   ///
  663.   ///  LLineTop holds the Y pixel value of the line
  664.   ///
  665.   LLineTop := 0;
  666.   ///
  667.   ///  start drawing lines
  668.   ///
  669.   Index := FFirstLine;
  670.   while Index <= FLastLine do begin
  671.     ///
  672.     ///  grab current line text
  673.     ///
  674.     LLineText := Editor.Lines[Index];
  675.     if ( Index = PreviousLineIndex ) and (Colors.PreviousLineText <> clNone) then
  676.       ///
  677.       ///  color of the previous line if applies
  678.       ///
  679.       FFullSizeBitmap.Canvas.Font.Color := Colors.PreviousLineText
  680.     else
  681.     if ( Index >= LTopLineIndex ) and ( Index <= LTopLineIndex + Editor.LinesInWindow ) then
  682.       ///
  683.       ///  font color of lines visible in the editor
  684.       ///
  685.       FFullSizeBitmap.Canvas.Font.Color := Colors.TextHighlight
  686.     else
  687.       ///
  688.       ///  normal text font color
  689.       ///
  690.       FFullSizeBitmap.Canvas.Font.Color := Colors.Text;
  691.     ///
  692.     ///  draw the text
  693.     ///  at this point, the font size is the same as in the editor
  694.     ///  just the line spacing is smaller
  695.     ///
  696.     FFullSizeBitmap.Canvas.TextOut(__get_line_xpos, LLineTop, LLineText);
  697.     ///
  698.     ///  increment the top pixel
  699.     ///
  700.     Inc(LLineTop, LLineHeight);
  701.     ///
  702.     ///  increment the line
  703.     ///
  704.     Inc(Index);
  705.   end;
  706.   ///
  707.   ///  if the current number of lines in the editor is smaller than
  708.   ///  the maximum we can display, we need to fill the canvas with
  709.   ///  the provided background color
  710.   ///
  711.   FOffsetBitmap.Canvas.Brush.Color := Colors.Background;
  712.   FOffsetBitmap.Canvas.FillRect(Rect(0, 0, Width, Height));
  713.   ///
  714.   ///  and adjust the size of the "scaled down" version of full size bitmap
  715.   ///
  716.   LDrawHeight := Height;
  717.   if FOffsetBitmap.Height > LLineCount * FLineHeightInPixels then
  718.     LDrawheight := LLineCount * FLineHeightInPixels;
  719.  
  720.   LDrawWidth := Trunc(FFullSizeBitmap.Width / FFontFactor);
  721. {$IFDEF FPC}
  722.  FOffsetBitmap.canvas.StretchDraw(Rect(0,0,LDrawWidth, LDrawHeight), FFullSizeBitmap);
  723. {$ELSE}
  724.   StretchDrawHalftone(FOffsetBitmap.canvas.handle, 0, 0, LDrawWidth, LDrawheight,
  725.     FFullSizeBitmap.Canvas, 0, 0, FFullSizeBitmap.Width, FFullSizeBitmap.Height);
  726. {$ENDIF}
  727.   ///
  728.   /// call paint to update the canvas
  729.   ///
  730.   Paint;
  731. end;
  732.  
  733. procedure TSynMiniMap.ResetInternals;
  734. begin
  735.   if NOT Assigned(Editor) then
  736.     Exit;
  737.   FEditorHeight := Editor.Height;
  738.   FEditorWidth := Editor.Width;
  739.   FEditorRealWidth := FEditorWidth - Editor.Gutter.Width;
  740.   FFullSizeBitmap.Canvas.Brush.Style := bsSolid;
  741.   FFullSizeBitmap.Canvas.Brush.Color := Colors.Background;
  742.   FFullSizeBitmap.Canvas.Font.Height := Editor.Font.Height;
  743.   FFullSizeBitmap.Canvas.Font.Size := Editor.Font.Size;
  744.   FFullSizeBitmap.Canvas.Font.Name := Editor.Font.Name;
  745.   FCharWidth := FFullSizeBitmap.Canvas.TextWidth('X');
  746.   if Options.TabWidthOverride then
  747.     FTabWidth := Options.TabWidth
  748.   else
  749.     FTabWidth := FCharWidth * Editor.TabWidth;
  750.   FMaxCharsPerLine := Trunc(Self.Width / (FCharWidth / FFontFactor));
  751.   FFullSizeBitmap.Width := FMaxCharsPerLine * FCharWidth;
  752.   Self.Color := Editor.Color;
  753.  
  754.   Render;
  755. end;
  756.  
  757. procedure TSynMiniMap.Resize;
  758. const
  759.   CNO_EDITOR = '(no editor assigned)';
  760. var
  761.   LTextHeight: Integer;
  762.   LTextWidth: Integer;
  763.   LTextX: Integer;
  764.   LTextY: Integer;
  765. begin
  766.   FOffsetBitmap.Height := Self.Height;
  767.   FOffsetBitmap.Width := Self.Width;
  768.   FOffsetBitmap.Canvas.Brush.Color := Colors.Background;
  769.   FOffsetBitmap.Canvas.FillRect(Rect(0, 0, Width, Height));
  770.   FOffsetBitmap.Canvas.Font.Color := Colors.Text;
  771.   if csDesigning in ComponentState then begin
  772.     LTextHeight := FOffsetBitmap.Canvas.TextHeight(CNO_EDITOR);
  773.     LTextWidth := FOffsetBitmap.Canvas.TextWidth(CNO_EDITOR);
  774.     LTextX := Width div 2 - LTextWidth div 2;
  775.     LTextY := Height div 2 - LTextHeight div 2;
  776.     FOffsetBitmap.Canvas.TextOut(LTextX, LTextY, CNO_EDITOR);
  777.   end;
  778.   ResetInternals;
  779.   inherited Resize;
  780. end;
  781.  
  782. procedure TSynMiniMap.SetFontFactor(const Value: Single);
  783. begin
  784.   if ( Value < SYNMINIMAP_FONTFACTOR_MIN ) or
  785.       ( Value > SYNMINIMAP_FONTFACTOR_MAX ) then begin
  786.     MessageDlg(
  787.       Format(SFontFactorMinMax, [SYNMINIMAP_FONTFACTOR_MIN,
  788.         SYNMINIMAP_FONTFACTOR_MAX]),
  789.       mtError, [mbOK], 0);
  790.     Exit;
  791.   end;
  792.   FFontFactor := Value;
  793.   Render;
  794. end;
  795.  
  796. procedure TSynMiniMap.SetPixelFormat(const Value: TPixelFormat);
  797. begin
  798.   ///
  799.   ///  set the pixel format on both bitmaps
  800.   ///
  801.   FFullSizeBitmap.PixelFormat := Value;
  802.   FOffsetBitmap.PixelFormat := Value;
  803. end;
  804.  
  805. function TSynMiniMap.TranslatePoint(const APoint: PPoint): TBufferCoord;
  806. var
  807.   LChar: Integer;
  808. begin
  809.   ///
  810.   ///  this method translates X and Y from control's surface into
  811.   ///  editor's Line and Char, mainly used in OnClick event
  812.   ///
  813.   if APoint^.X < 1 then
  814.     LChar := 1
  815.   else
  816.     LChar := Trunc(APoint^.X / (FCharWidth / FFontFactor));
  817.   if LChar > FMaxCharsPerLine then
  818.     LChar := FMaxCharsPerLine;
  819.   Result.Char := LChar;
  820.   Result.Line := FFirstLine + APoint^.Y div FLineHeightInPixels +1;
  821. end;
  822.  
  823. procedure TSynMiniMap.SetEditor(const Value: TSynEdit);
  824. begin
  825.   FEditor := Value;
  826.   ///
  827.   ///  create a plugin if we don't have one
  828.   ///
  829.   if Value <> NIL then
  830.     FMiniMapPlugin := TSynMiniMapEditorPlugin.Create(Self);
  831.   ResetInternals;
  832. end;
  833.  
  834. initialization
  835.  
  836. finalization
  837.  
  838. end.
RAW Paste Data