{v0.47 Based on:}
{
  -------------------------------------------------------------------
  Author:          Gawie Kellerman
  Compiler:        Inprise Delphi 4.0 SP3
  Date:            17 May 1999
  Purpose:         A Hex Editor component with great user interface
                   features
  Copyright (C) 1999 - KELSYS
  *******************************************************************
  Revisions:       None yet
  -------------------------------------------------------------------
}


unit HexEditor;
{v0.47 was GKHexEditor; renamed to be more content descriptive}

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  Grids;

const
  LL = 16;
  CHexOffset = 0;
  CDecOffset = 1;
  CHexData   = 2;
  CAscData   = 2+(LL*3);
  CMaximum   = CAscData+LL;
  CHexStr    = '0123456789ABCDEF';
  CMarkers   = 10;
  CMarkerStr : array[1..12] of char = (#0, #140, #141, #142,
                                       #143, #144, #145, #146, #147,
                                       #148, #149, #150 );

type
  THexEditor = class(TCustomGrid)
  private
    { Private declarations }
    FDataSize : integer;
    FData : Pointer;
    FCaretBmp : TBitmap;
    FCustomAnchors : TPoint;
    FMarkedAscColor: TColor;
    FHexColor: TColor;
    FAscColor: TColor;
    FMarkedHexColor: TColor;
    FDecOffsetColor: TColor;
    FHexBackColor: TColor;
    FHexOffsetColor: TColor;
    FAscHeadingColor: TColor;
    FHexHeadingColor: TColor;
    FWingdings: TFont;
    FHexDataFont: TFont;
    FAscDataFont: TFont;
    FHexHeaderFont: TFont;
    FAscOffsetFont: TFont;
    FAscHeaderFont: TFont;
    FHexOffsetFont: TFont;
    FInterlineSpacing: integer;
    FFixedVert: boolean;
    FFixedHorz: boolean;
    FMarkers : array[0..12] of integer;
    {v0.47}
    FCaretCreated: boolean;
    {/v0.47}
    procedure SetAscColor(const Value: TColor);
    procedure SetDecOffsetColor(const Value: TColor);
    procedure SetHexColor(const Value: TColor);
    procedure SetHexOffsetColor(const Value: TColor);
    procedure SetMarkedAscColor(const Value: TColor);
    procedure SetMarkedHexColor(const Value: TColor);
    procedure SetAscHeadingColor(const Value: TColor);
    procedure SetHexHeadingColor(const Value: TColor);
    procedure SetWingdings(const Value: TFont);
    procedure SetAscDataFont(const Value: TFont);
    procedure SetAscHeaderFont(const Value: TFont);
    procedure SetAscOffsetFont(const Value: TFont);
    procedure SetHexDataFont(const Value: TFont);
    procedure SetHexHeaderFont(const Value: TFont);
    procedure SetHexOffsetFont(const Value: TFont);
    procedure SetInterlineSpacing(const Value: integer);
    procedure SetFixedHorz(const Value: boolean);
    procedure SetFixedVert(const Value: boolean);
  protected
    { Protected declarations }

    { Custom }
    function CalcRowCount(ASize : Longint) : Longint;
    function FirstHexColumn(ACol : Longint) : boolean;
    function ValidHexColumn(ACol, ARow : Longint) : boolean;
    function ValidAscColumn(ACol, ARow : Longint) : boolean;
    function HexData(ACol, ARow : Longint) : char;
    function AscData(ACol, ARow : Longint) : char;
    {v0.47}
    procedure SetHexData(ACol, ARow: Longint; AChar: char);
    procedure SetAscData(ACol, ARow: Longint; AChar: char);
    {/v0.47}

    function HexColumn(ACol : Longint) : integer;
    function AscColumn(ACol : Longint) : integer;
    function FindTabColumn(ACol, ARow : Longint) : integer;
    function FindOffset(ACol, ARow : Longint) : integer;

    procedure DrawCenter(ARect : TRect; S : String);
    procedure DrawActiveCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
    procedure DrawInactiveCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
    procedure DrawHexOffset(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
    procedure DrawDecOffset(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
    procedure DrawHexData(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
    procedure DrawAscData(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState);
    procedure CreateCaret(ARect : TRect);
    {v0.47}
    procedure DestroyCaret;
    {/v0.47}
    procedure InvalidateBlock( ASob, AEob : integer);
    procedure ResizeAllColumns;

    { Overrides }
    function SelectCell(ACol, ARow: Longint): Boolean; override;
    procedure DrawCell(ACol, ARow: Longint; ARect: TRect; AState: TGridDrawState); override;
    procedure Loaded; override;
    procedure KeyDown(var Key: Word; Shift: TShiftState); override;
    procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); override;
    procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;

    {v0.47}
    procedure DoEnter; override;
    procedure DoExit; override;
    {/v0.47}
  public
    { Public declarations }
    constructor Create(AOwner : TComponent); override;
    destructor Destroy; override;
    procedure GoNextColumn;
    procedure GoPrevColumn;
    procedure GoNextRow;
    procedure GoPrevRow;
    procedure GoNextPage;
    procedure GoPrevPage;
    procedure GoLastColumn;
    procedure GoFirstColumn;
    procedure GoTab;
    procedure MarkSOB;
    procedure MarkEOB;
    procedure SetMarker(MarkerNo : Integer);
    procedure GotoMarker(MarkerNo : integer);
    function InAnchor(ACol, ARow : integer) : boolean;
    function FindMarker(AOffset : integer) : integer;
  published
    { Published declarations }
    property HexBackColor : TColor read FHexBackColor;
    property HexColor : TColor read FHexColor write SetHexColor;
    property AscColor : TColor read FAscColor write SetAscColor;
    property MarkedHexColor : TColor read FMarkedHexColor write SetMarkedHexColor;
    property MarkedAscColor : TColor read FMarkedAscColor write SetMarkedAscColor;
    property HexOffsetColor : TColor read FHexOffsetColor write SetHexOffsetColor;
    property DecOffsetColor : TColor read FDecOffsetColor write SetDecOffsetColor;
    property HexHeadingColor : TColor read FHexHeadingColor write SetHexHeadingColor;
    property AscHeadingColor : TColor read FAscHeadingColor write SetAscHeadingColor;
    property FixedHorz : boolean read FFixedHorz write SetFixedHorz;
    property FixedVert : boolean read FFixedVert write SetFixedVert;

    property Wingdings : TFont read FWingdings write SetWingdings;
    property HexOffsetFont : TFont read FHexOffsetFont write SetHexOffsetFont;
    property AscOffsetFont : TFont read FAscOffsetFont write SetAscOffsetFont;
    property HexDataFont : TFont read FHexDataFont write SetHexDataFont;
    property AscDataFont : TFont read FAscDataFont write SetAscDataFont;
    property HexHeaderFont : TFont read FHexHeaderFont write SetHexHeaderFont;
    property AscHeaderFont : TFont read FAscHeaderFont write SetAscHeaderFont;
    property InterlineSpacing : integer read FInterlineSpacing write SetInterlineSpacing;

    property Align;
    property Anchors;
    property BiDiMode;
    property BorderStyle;
    property Color;
    property ColCount;
    property Constraints;
    property Ctl3D;
    property DefaultColWidth;
    property DefaultRowHeight;
    property DefaultDrawing;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property FixedColor;
    property FixedCols;
    property RowCount;
    property FixedRows;
    property Font;
    property GridLineWidth;
    property Options;
    property ParentBiDiMode;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ScrollBars;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property Visible;
    property VisibleColCount;
    property VisibleRowCount;
    property OnClick;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseDown;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnStartDock;
    property OnStartDrag;
  end;

procedure Register;

implementation


  //-----------------------------------------------------------------
  //Register
procedure Register;
begin
  RegisterComponents('Chromulan', [THexEditor]);
end;



{ THexEditor }


  //-----------------------------------------------------------------
  //Create
constructor THexEditor.Create(AOwner: TComponent);
  var
    n : integer;
    p : pchar;
  begin
    inherited Create(AOwner);

    FixedCols := 2;
    FixedRows := 1;

    DefaultColWidth := abs(Font.Size);

    ColCount := CMaximum;
    RowCount := CalcRowCount(1028);

    ColWidths[0] := abs(Font.Size)*9;
    ColWidths[1] := abs(Font.Size)*9;

    // Custom Initialization
    FDataSize := 1028;
    GetMem( FData, FDataSize);
    FCaretBmp := nil;
    FCustomAnchors := Point(-1,-1);

    FMarkedAscColor := clGray;
    FMarkedHexColor := clYellow;
    FHexColor       := clSilver;
    FAscColor       := clSilver;
    FDecOffsetColor := clSilver;
    FHexBackColor   := clSilver;
    FHexOffsetColor := clSilver;
    FAscHeadingColor:= clRed;
    FHexHeadingColor:= clBlue;
    FHexDataFont := TFont.Create;
    FAscDataFont := TFont.Create;
    FHexHeaderFont := TFont.Create;
    FAscOffsetFont := TFont.Create;
    FAscHeaderFont := TFont.Create;
    FHexOffsetFont := TFont.Create;
    FWingdings := TFont.Create;

    FWingdings.Name := 'Webdings';
    FWingdings.Size := 8;

    FInterlineSpacing := 2;

    Options := [goTabs, goHorzLine, goThumbTracking];

    //{$IFDEF DEBUG}
      for n:=0 to 10 do
        FMarkers[n] := -1;

      p := FData;
      for n := 1 to 255 do
      begin
        p^ := char(n);
        p := p + 1;
      end;
    //{$ENDIF}
  end;



  //-----------------------------------------------------------------
  //Destroy
destructor THexEditor.Destroy;
begin
  // Custom Destruction
  FreeMem( FData );
  FData := nil;
  FDataSize := 0;
  FCaretBmp.Free;

  FWingdings.Free;
  FHexDataFont.Free;
  FAscDataFont.Free;
  FHexHeaderFont.Free;
  FAscOffsetFont.Free;
  FAscHeaderFont.Free;
  FHexOffsetFont.Free;

  DestroyCaret;

  inherited Destroy;
end;



  //-----------------------------------------------------------------
  //DrawCell
procedure THexEditor.DrawCell(
        ACol, ARow: Integer;
        ARect: TRect;
        AState: TGridDrawState);
var
  OtherCol : integer;
begin
  case ACol of
    CHexOffset : DrawHexOffset(ACol, ARow, ARect, AState);
    CDecOffset : DrawDecOffset(ACol, ARow, ARect, AState);
    CHexData..CAscData-1 : DrawHexData(ACol, ARow, ARect, AState);
    CAscData..CMaximum : DrawAscData(ACol, ARow, ARect, AState);
  end;  //end-case

  if (Col=ACol) and (Row=ARow) then
  begin
    DrawActiveCell(ACol, ARow, ARect, AState);
  end;

  OtherCol := FindTabColumn(Col, Row);
  if (OtherCol = ACol) and (Row = ARow) then
  begin
    DrawInactiveCell(ACol, ARow, ARect, AState);
  end;
end;



  //-----------------------------------------------------------------
  //DrawAscData
procedure THexEditor.DrawAscData(
        ACol, ARow: Integer;
        ARect: TRect;
        AState: TGridDrawState);
  var
    C : char;
    S : string;
  begin
    if ARow>=FixedRows then
    begin
      if ValidAscColumn(ACol, ARow) then
      begin
        if InAnchor(ACol, ARow) then
          Canvas.Brush.Color := MarkedAscColor
        else
          Canvas.Brush.Color := AscColor;

        Canvas.Font.Assign( AscDataFont );

        C := AscData(ACol, ARow);
        S := '' + C;
        DrawCenter(ARect, S);
      end;
    end else
    begin
      S := CHexStr[AscColumn(ACol)+1];

      Canvas.Brush.Color := AscHeadingColor;
      Canvas.Font.Assign( AscHeaderFont );
      DrawCenter(ARect, S);

      Canvas.Pen.Color := clWhite;
      Canvas.MoveTo( ARect.Left, ARect.Bottom);
      Canvas.LineTo( ARect.Right, ARect.Bottom );
    end;
  end;



  //-----------------------------------------------------------------
  //DrawDecOffset
procedure THexEditor.DrawDecOffset(
        ACol, ARow: Integer;
        ARect: TRect;
        AState: TGridDrawState);
  var
    S : string;
    I : integer;
  begin
    if ARow>=FixedRows then
    begin
      I := ARow * LL;
      S := Format('%0.8dd', [I] );
      Canvas.Brush.Color := DecOffsetColor;
      Canvas.Font.Assign( AscOffsetFont );
      DrawCenter(ARect, S);

      Canvas.Pen.Color := clWhite;
      Canvas.MoveTo( ARect.Right-1, ARect.Top );
      Canvas.LineTo( ARect.Right-1, ARect.Bottom );
    end;
  end;



  //-----------------------------------------------------------------
  //DrawHexData
procedure THexEditor.DrawHexData(
        ACol, ARow: Integer;
        ARect: TRect;
        AState: TGridDrawState);
  var
    C : char;
    S : string;
  begin
    Canvas.Brush.Color := HexBackColor;
    if ARow>=FixedRows then
    begin
      if ValidHexColumn(ACol, ARow) then
      begin
        { Set up the color for marked/non marked }
        if InAnchor(ACol, ARow) then
          Canvas.Brush.Color := MarkedHexColor
        else
          Canvas.Brush.Color := HexColor;
        Canvas.Font.Assign(HexDataFont);

        C := HexData(ACol, ARow);
        S := '' + C;
        DrawCenter(ARect, S);
      end else
      begin
        { In the gutter }

        { Draw special for start/end markers }
        Canvas.Font.Assign( Wingdings );
        if FindOffset(ACol, ARow) = FCustomAnchors.x then
        begin
          Canvas.TextRect(
              ARect,
              ARect.Left,
              ARect.Top,
              #218) //'3');
        end else
        if FindOffset(ACol, ARow) = FCustomAnchors.y then
        begin
          Canvas.TextRect(
              ARect,
              ARect.Left,
              ARect.Top,
              #217) //'4');
        end else
        begin
          S := CMarkerStr[FindMarker(FindOffset(ACol, ARow))+1];
          Canvas.Brush.Color := Color;

          if S[1]<>#0 then
          begin
            Canvas.TextOut(
                ARect.Left,
                ARect.Top,
                S);
          end else
          begin
            Canvas.TextRect(
                ARect,
                ARect.Left,
                ARect.Top,
                ' ');
          end;
        end;
      end;
    end else
    begin
      S := ' ';
      if ValidHexColumn(ACol, Row) then
      begin
        if (not FirstHexColumn(ACol)) then
        begin
          S := CHexStr[ HexColumn(ACol) + 1 ];
        end;
      end;
      Canvas.Brush.Color := Self.HexHeadingColor;
      Canvas.Font.Assign( HexHeaderFont );
      DrawCenter(ARect, S);

      Canvas.Pen.Color := clWhite;
      Canvas.MoveTo( ARect.Left, ARect.Bottom);
      Canvas.LineTo( ARect.Right, ARect.Bottom );
    end;
  end;



  //-----------------------------------------------------------------
  //DrawHexOffset
procedure THexEditor.DrawHexOffset(
        ACol, ARow: Integer;
        ARect: TRect;
        AState: TGridDrawState);
  var
    S : string;
    I : integer;
  begin
    if ARow>=FixedRows then
    begin
      I := ARow * LL;
      S := Format('%0.8xh', [I] );
      Canvas.Brush.Color := HexOffsetColor;
      Canvas.Font.Assign( HexOffsetFont );
      DrawCenter(ARect, S);
    end;
  end;



  //-----------------------------------------------------------------
  //Loaded
procedure THexEditor.Loaded;
  begin
    inherited Loaded;

    ColWidths[0] := abs(Font.Size)*9;
    ColWidths[1] := abs(Font.Size)*9;

    DefaultDrawing := false;


    ResizeAllColumns;
  end;



  //-----------------------------------------------------------------
  //HexData
function THexEditor.HexData(ACol, ARow: Integer): char;
var
  P : pchar;
  B : byte;
begin
  if ValidHexColumn(ACol, ARow) then
  begin
    P := FData;
    P := P + (ARow-FixedRows)*LL + HexColumn(ACol);
    B := Byte(P^);
    if FirstHexColumn(ACol) then
      B := (B shr $04)
    else
      B := (B and $0F);
    Result := CHexStr[B+1];
  end else
  begin
    Result := ' ';
  end;
end;

{v0.47}
procedure THexEditor.SetHexData(ACol, ARow: Longint; AChar: char);
var
  P: pchar;
  B: byte;
  i: integer;
begin
  i := pos(UpCase(AChar), CHexStr);
  if i = 0 then
    exit;
  if ValidHexColumn(ACol, ARow) then
  begin
    P := FData;
    P := P + (ARow - FixedRows) * LL + HexColumn(ACol);
    B := Byte(P^);
    if FirstHexColumn(ACol) then begin
      B := (B and $0F) or ((i - 1) shl 4)
    end else begin
      B := (B and $F0) or (i - 1);
    end;
    P^ := char(B);
    {Result := CHexStr[B+1];}
  end;
end;
{/v0.47}


  //-----------------------------------------------------------------
  //AscData
function THexEditor.AscData(ACol, ARow: Integer): char;
var
  P : pchar;
begin
  if ValidAscColumn(ACol, ARow) then
  begin
    P := FData;
    P := P + (ARow-FixedRows)*LL + AscColumn(ACol);
    Result := P^;
  end else
  begin
    Result := ' ';
  end;
end;

{v0.47}
procedure THexEditor.SetAscData(ACol, ARow: Longint; AChar: char);
var
  P : pchar;
begin
  if ValidAscColumn(ACol, ARow) then begin
    P := FData;
    P := P + (ARow - FixedRows) * LL + AscColumn(ACol);
    P^ := AChar;
  end;
end;
{/v0.47}


  //-----------------------------------------------------------------
  //ValidHexColumn
function THexEditor.ValidHexColumn(ACol, ARow: Integer): boolean;
  var
    ioffset : longint;
  begin
    Result := false;

    if (ACol>=CHexData) and (ACol<CAscData) then
    begin
      if (ARow>=FixedRows) and (ARow<=RowCount) then
      begin
        if (ACol-CDecOffset) mod 3 <> 0 then
        begin
          ioffset := (ARow-FixedRows)*LL + HexColumn(ACol);
          if ioffset<FDataSize then
            Result := true;
        end;
      end;
    end;
  end;



  //-----------------------------------------------------------------
  //ValidAscColumn
function THexEditor.ValidAscColumn(ACol, ARow: Integer): boolean;
  var
    ioffset : longint;
  begin
    Result := false;

    if (ACol>=CAscData) and (ACol<CMaximum) then
    begin
      if (ARow>=FixedRows) and (ARow<=RowCount) then
      begin
        ioffset := (ARow-FixedRows)*LL + AscColumn(ACol);
          if ioffset<FDataSize then
            Result := true;
      end;
    end;
  end;



  //-----------------------------------------------------------------
  //FirstHexColumn
function THexEditor.FirstHexColumn(ACol: Integer): boolean;
  var
    i : integer;
  begin
    i := ACol - (CHexData-1);
    i := i mod 3;
    if i=1 then
      Result := true
    else
      Result := false;
  end;



  //-----------------------------------------------------------------
  //DrawCenter
procedure THexEditor.DrawCenter(ARect: TRect; S: String);
  var
    p : TPoint;
    z : TSize;
  begin
    z := Canvas.TextExtent(S);

    p.x := ((ARect.Left + ARect.Right) shr 1) - (z.cx shr 1);
    p.y := ((ARect.Top + ARect.Bottom) shr 1) - (z.cy shr 1);

    Canvas.TextRect(
        ARect,
        p.x,
        p.y,
        S);
  end;



  //-----------------------------------------------------------------
  //HexColumn
function THexEditor.HexColumn(ACol: Integer): integer;
  begin
    Result := (ACol-FixedCols) div 3;
  end;



  //-----------------------------------------------------------------
  //AscColumn
function THexEditor.AscColumn(ACol: Integer): integer;
  begin
    Result := (ACol-CAscData);
  end;



  //-----------------------------------------------------------------
  //DrawActiveCell
procedure THexEditor.DrawActiveCell(
        ACol, ARow: Integer;
        ARect: TRect;
        AState: TGridDrawState);
begin
  CreateCaret(ARect);
  SetCaretPos({v0.47}Left + {/v0.47}ARect.Left, {v0.47}Top +{/v0.47}ARect.Top);
end;



  //-----------------------------------------------------------------
  //CreateCaret
procedure THexEditor.CreateCaret(ARect: TRect);
var
  b : boolean;
begin
  {v0.47}
  if not FCaretCreated then begin
    if (FCaretBmp=nil) then
    begin
      FCaretBmp := TBitmap.Create;
      FCaretBmp.Width := ARect.Right - ARect.Left;
      FCaretBmp.Height := ARect.Bottom - ARect.Top;
      FCaretBmp.Canvas.Brush.Color := clWhite;
      FCaretBmp.Canvas.FillRect( Rect(0,0,FCaretBmp.Width,FCaretBmp.Height));
    end;
    b := Windows.CreateCaret(Handle, FCaretBmp.Handle, 0, 0);
    Assert(b, 'CARET NOT CREATED!');
    if b then begin
      FCaretCreated := true;
      ShowCaret(Handle);
    end;
  end else begin
    ShowCaret(Handle);
  end;
  {/v0.47
  if (FCaretBmp=nil) then
  begin
    FCaretBmp := TBitmap.Create;
    FCaretBmp.Width := ARect.Right - ARect.Left;
    FCaretBmp.Height := ARect.Bottom - ARect.Top;
    FCaretBmp.Canvas.Brush.Color := clWhite;
    FCaretBmp.Canvas.FillRect( Rect(0,0,FCaretBmp.Width,FCaretBmp.Height));
    b := Windows.CreateCaret(Handle, FCaretBmp.Handle, 0, 0);
    Assert(b, 'CARET NOT CREATED!');
    ShowCaret(Handle);
  end;
  }
end;



  //-----------------------------------------------------------------
  //DrawInactiveCell
procedure THexEditor.DrawInactiveCell(
        ACol, ARow: Integer;
        ARect: TRect;
        AState: TGridDrawState);
begin
  Windows.DrawFocusRect(Canvas.Handle, ARect);
end;



  //-----------------------------------------------------------------
  //FindTabColumn
function THexEditor.FindTabColumn(ACol, ARow : Longint): integer;
  begin
    Result := 2;
    if ValidHexColumn(ACol, Row) then
    begin
      Result := HexColumn(ACol);
      Result := CAscData+Result;
    end else
    if ValidAscColumn(ACol, Row) then
    begin
      Result := AscColumn(ACol);
      Result := Result * 3 + CHexData;
    end;
  end;



  //-----------------------------------------------------------------
  //SelectCell
function THexEditor.SelectCell(ACol, ARow: Integer): Boolean;
  begin
    Result := false;

    if (ARow>=FixedRows) and (ARow<RowCount) and
       (ACol>=FixedCols) and (ACol<ColCount) then
    begin
      if (ValidHexColumn(ACol, ARow)) or (ValidAscColumn(ACol, ARow)) then
        Result := true;
    end;
  end;



  //-----------------------------------------------------------------
  //MouseDown
procedure THexEditor.MouseDown(
        Button: TMouseButton;
        Shift: TShiftState;
        X, Y: Integer);
  var
    ACol : integer;
  begin
    ACol := FindTabColumn(Col, Row);
    InvalidateCell(ACol, Row);

    inherited MouseDown(Button, Shift, X, Y);

    ACol := FindTabColumn(Col, Row);
    InvalidateCell(ACol, Row);
  end;



  //-----------------------------------------------------------------
  //MouseMove
procedure THexEditor.MouseMove(Shift: TShiftState; X, Y: Integer);
  var
    ACol : integer;
  begin
    ACol := FindTabColumn(Col, Row);
    InvalidateCell(ACol, Row);

    inherited MouseMove(Shift, X, Y);

    ACol := FindTabColumn(Col, Row);
    InvalidateCell(ACol, Row);
  end;



  //-----------------------------------------------------------------
  //KeyDown
procedure THexEditor.KeyDown(var Key: Word; Shift: TShiftState);
  var
    ACol : integer;
  begin
    //
    ACol := FindTabColumn(Col, Row);
    InvalidateCell(ACol, Row);

    if Shift = [ssShift, ssCtrl] then
    begin
      case Key of
        VK_F1 .. VK_F10 :
          begin
            SetMarker(Key-VK_F1);
            Key := 0;
          end;
      end;
    end;

    if Shift = [ssCtrl] then
    begin
      case Key of
        VK_F1 .. VK_F10 :
          begin
            GotoMarker(Key-VK_F1);
            Key := 0;
          end;
      end;
    end;

    if Shift = [] then
    begin
      case Key of
        VK_F2 :
          begin
            MarkSOB;
            Key := 0;
          end;

        VK_F3 :
          begin
            MarkEOB;
            Key := 0;
          end;

        VK_RIGHT :
          begin
            GoNextColumn;
            Key := 0;
          end;

        VK_LEFT :
          begin
            GoPrevColumn;
            Key := 0;
          end;

        VK_DOWN :
          begin
            GoNextRow;
            Key := 0;
          end;

        VK_UP :
          begin
            GoPrevRow;
            Key := 0;
          end;

        VK_NEXT :
          begin
            GoNextPage;
            Key := 0;
          end;

        VK_PRIOR :
          begin
            GoPrevPage;
            Key := 0;
          end;

        VK_HOME :
          begin
            GoFirstColumn;
            Key := 0;
          end;

        VK_END :
          begin
            GoLastColumn;
            Key := 0;
          end;

        VK_TAB :
          begin
            GoTab;
            Key := 0;
          end;
      end;
    end;
    inherited KeyDown(Key, Shift);
    ACol := FindTabColumn(Col, Row);
    {v0.47}
    if Enabled and (lo(Key) <> 0) then begin
      if ValidHexColumn(ACol, Row) then begin
        SetHexData(ACol, Row, chr(lo(Key)));
      end else if ValidAscColumn(ACol, Row) then
        SetAscData(ACol, Row, chr(lo(Key)));
    end;
    {/v0.47}
    InvalidateCell(ACol, Row);
  end;



  //-----------------------------------------------------------------
  //GoNextColumn
procedure THexEditor.GoNextColumn;
  var
    ACol, ARow : integer;
  begin
    ACol := Col;
    ARow := Row;

    if (ACol<CAscData) then
    begin
      { Working with a Hex Data Cursor }

      { Check whether cursor going out of range }
      if FindOffset(ACol, ARow) < FDataSize then
      begin
        { Check whether cursor at end-of-line}
        if ACol = CAscData-2 then
        begin
          ACol := CHexData;
          Inc(ARow);
        end else
        begin
          Inc(ACol);
          if not SelectCell(ACol, ARow) then Inc(ACol);
        end;

        Col := ACol;
        Row := ARow;
      end;
    end else
    begin
      { Working with an Ascii Data Cursor }

      if FindOffset(ACol, ARow) < FDataSize then
      begin
        if ACol = CMaximum-1 then
        begin
          Col := CAscData;
          Row := Row + 1;
        end else
        begin
          Col := Col + 1;
        end;
      end;
    end;
  end;



  //-----------------------------------------------------------------
  //GoPrevColumn
procedure THexEditor.GoPrevColumn;
  var
    ACol, ARow : integer;
  begin
    ACol := Col;
    ARow := Row;

    if (ACol<CAscData) then
    begin
      { Working with a Hex Data Cursor }

      { Check whether cursor going out of range }
      if (FindOffset(ACol, ARow) > 0) or (not FirstHexColumn(ACol)) then
      begin
        { Check whether cursor at beginning-of-line}
        if ACol = CHexData then
        begin
          ACol := CAscData-3;
          Dec(ARow);
          Row := ARow;
        end else
        begin
          Dec(ACol);
          if not SelectCell(ACol, ARow) then Dec(ACol);
        end;

        Col := ACol;
        Row := ARow;
      end;
    end else
    begin
      { Working with an Ascii Data Cursor }
      if FindOffset(ACol, ARow) > 0 then
      begin
        if ACol = CAscData then
        begin
          Row := Row - 1;
          Col := CMaximum - 1;
        end else
        begin
          Col := Col - 1;
        end;
      end;
    end;
  end;



  //-----------------------------------------------------------------
  //GoNextRow
procedure THexEditor.GoNextRow;
  begin
    if SelectCell(Col, Row+1) then Row := Row + 1;
  end;



  //-----------------------------------------------------------------
  //GoPrevRow
procedure THexEditor.GoPrevRow;
  begin
    if SelectCell(Col, Row-1) then Row := Row - 1;
  end;



  //-----------------------------------------------------------------
  //FindOffset
function THexEditor.FindOffset(ACol, ARow: Integer): integer;
  begin
    if ACol<CAscData then
      ACol := HexColumn(ACol)
    else
      ACol := AscColumn(ACol);

    Result := (ARow-FixedRows)*LL + ACol;
  end;



  //-----------------------------------------------------------------
  //CalcRowCount
function THexEditor.CalcRowCount(ASize: Integer): Longint;
  begin
    Result := ASize div LL;
    Inc(Result);
    if ASize mod LL > 0 then Inc(Result);
  end;



  //-----------------------------------------------------------------
  //GoNextPage
procedure THexEditor.GoNextPage;
  var
    ARow : integer;
  begin
    ARow := Row;
    ARow := ARow + VisibleRowCount;
    while not SelectCell(Col, ARow) do Dec(ARow);
    Row := ARow;
  end;



  //-----------------------------------------------------------------
  //GoPrevPage
procedure THexEditor.GoPrevPage;
  var
    ARow : integer;
  begin
    ARow := Row;
    ARow := ARow - VisibleRowCount;
    while not SelectCell(Col, ARow) do Inc(ARow);
    Row := ARow;
  end;



  //-----------------------------------------------------------------
  //GoFirstColumn
procedure THexEditor.GoFirstColumn;
  var
    ACol : integer;
  begin
    ACol := Col;

    if ACol<CAscData then
      ACol := CHexData
    else
      ACol := CAscData;

    Col := ACol;
  end;



  //-----------------------------------------------------------------
  //GoLastColumn
procedure THexEditor.GoLastColumn;
  var
    ACol : integer;
  begin
    ACol := Col;

    if ACol<CAscData then
      ACol := CAscData-2
    else
      ACol := CAscData+15;

    while not SelectCell(ACol, Row) do Dec(ACol);
    Col := ACol;
  end;



  //-----------------------------------------------------------------
  //GoTab
procedure THexEditor.GoTab;
  var
    ACol : integer;
  begin
    ACol := Col;

    if ACol<CAscData then
    begin
      ACol := HexColumn(ACol);
      ACol := ACol + CAscData;
    end else
    begin
      ACol := AscColumn(ACol);
      ACol := ACol*3 + CHexData;
    end;
    Col := ACol;
  end;



  //-----------------------------------------------------------------
  //Min
function Min(a, b : integer) : integer;
  begin
    if a<b then
      Result := a
    else
      Result := b;
  end;



  //-----------------------------------------------------------------
  //Max
function Max(a, b : integer) : integer;
  begin
    if a<b then
      Result := b
    else
      Result := a;
  end;



  //-----------------------------------------------------------------
  //MarkEOB (end-of-block)
procedure THexEditor.MarkEOB;
  var
    OldE : integer;
  begin
    OldE := FCustomAnchors.y;
    InvalidateRow( OldE div LL);
    Invalidate;

    FCustomAnchors.y := FindOffset(Col, Row);
    if OldE=-1 then OldE := FCustomAnchors.y;

    InvalidateRow( FCustomAnchors.y div LL);
    Invalidate;

    if FCustomAnchors.y>FCustomAnchors.x then Invalidate;

    if FCustomAnchors.x<>-1 then
      InvalidateBlock(
         FCustomAnchors.x,
         Max(OldE, FCustomAnchors.y));
  end;



  //-----------------------------------------------------------------
  //MarkSOB (Start-of-Black)
procedure THexEditor.MarkSOB;
  var
    OldX : integer;
  begin
    OldX := FCustomAnchors.x;

    FCustomAnchors.x := FindOffset(Col, Row);

    InvalidateRow( OldX div LL);
    Invalidate;


    InvalidateRow( FCustomAnchors.x div LL);
    Invalidate;

    if OldX=-1 then OldX := FCustomAnchors.x;

    if FCustomAnchors.y>FCustomAnchors.x then Invalidate;

    if FCustomAnchors.y<>-1 then
      InvalidateBlock(
          Min(OldX,FCustomAnchors.x),
          FCustomAnchors.y);
  end;



  //-----------------------------------------------------------------
  //InvalidateBlock
procedure THexEditor.InvalidateBlock(ASob, AEob : integer);
  var
    n : integer;
  begin
    ASOB := ASob div LL - 1;
    AEOB := AEob div LL + 1;
    for n:=ASOB to AEOB do
      InvalidateRow(n);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //InAnchor
function THexEditor.InAnchor(ACol, ARow: integer): boolean;
  var
    offset : integer;
  begin
    Result := false;

    offset := FindOffset(ACol, ARow);
    if (FCustomAnchors.x<>-1) and (FCustomAnchors.y<>-1) then
      if (offset>=FCustomAnchors.x) and
         (offset<=FCustomAnchors.y) then
        Result := true;
  end;



  //-----------------------------------------------------------------
  //SetAscColor
procedure THexEditor.SetAscColor(const Value: TColor);
  begin
    FAscColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetDecOffsetColor
procedure THexEditor.SetDecOffsetColor(const Value: TColor);
  begin
    FDecOffsetColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetHexColor
procedure THexEditor.SetHexColor(const Value: TColor);
  begin
    FHexColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetHexOffsetColor
procedure THexEditor.SetHexOffsetColor(const Value: TColor);
  begin
    FHexOffsetColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetMarkedAscColor
procedure THexEditor.SetMarkedAscColor(const Value: TColor);
  begin
    FMarkedAscColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetMarkedHexColor
procedure THexEditor.SetMarkedHexColor(const Value: TColor);
  begin
    FMarkedHexColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetAscHeadingColor
procedure THexEditor.SetAscHeadingColor(const Value: TColor);
  begin
    FAscHeadingColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetHexHeadingColor
procedure THexEditor.SetHexHeadingColor(const Value: TColor);
  begin
    FHexHeadingColor := Value;
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetWingdings
procedure THexEditor.SetWingdings(const Value: TFont);
  begin
    FWingdings.Assign(Value);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetAscDataFont
procedure THexEditor.SetAscDataFont(const Value: TFont);
  begin
    FAscDataFont.Assign(Value);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetAscHeaderFont
procedure THexEditor.SetAscHeaderFont(const Value: TFont);
  begin
    FAscHeaderFont.Assign(Value);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetAscOffsetFont
procedure THexEditor.SetAscOffsetFont(const Value: TFont);
  begin
    FAscOffsetFont.Assign(Value);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetHexDataFont
procedure THexEditor.SetHexDataFont(const Value: TFont);
  begin
    FHexDataFont.Assign(Value);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetHexHeaderFont
procedure THexEditor.SetHexHeaderFont(const Value: TFont);
  begin
    FHexHeaderFont.Assign(Value);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //SetHexOffsetFont
procedure THexEditor.SetHexOffsetFont(const Value: TFont);
  begin
    FHexOffsetFont.Assign(Value);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //MaxOfArray
function MaxOfArray(const i : array of integer) : integer;
  var
    n : integer;
    m : integer;
  begin
    Result := i[0];
    m := sizeof(i) div sizeof(integer);
    for n:=1 to m-1 do
    begin
      if i[n]>Result then Result := i[n];
    end;
  end;



  //-----------------------------------------------------------------
  //ResizeAllColumns
procedure THexEditor.ResizeAllColumns;
  var
    MaxHeight : integer;
    n : integer;
  begin
    ColWidths[CHexOffset] := HexOffsetFont.Size * 9;
    ColWidths[CDecOffset] := AscOffsetFont.Size * 9;

    for n:=CHexData to CAscData-1 do
    begin
      ColWidths[n] := HexDataFont.Size+1;
    end;

    for n:=CAscData to CMaximum-1 do
    begin
      ColWidths[n] := AscDataFont.Size+2;
    end;

    MaxHeight := MaxOfArray(
        [abs(HexOffsetFont.Height), abs(HexDataFont.Height),
         abs(AscOffsetFont.Height), abs(AscDataFont.Height) ] );

    DefaultRowHeight := MaxHeight;
    for n:=FixedRows to RowCount-1 do
    begin
      RowHeights[n] := MaxHeight + InterlineSpacing;
    end;

    RowHeights[0] := MaxOfArray(
        [abs(HexHeaderFont.Height), abs(AscHeaderFont.Height)]) + InterlineSpacing;
  end;



  //-----------------------------------------------------------------
  //SetInterlineSpacing
procedure THexEditor.SetInterlineSpacing(const Value: integer);
  begin
    FInterlineSpacing := Value;
    ResizeAllColumns;
  end;



  //-----------------------------------------------------------------
  //SetFixedHorz
procedure THexEditor.SetFixedHorz(const Value: boolean);
  begin
    FFixedHorz := Value;
    Refresh;
  end;



  //-----------------------------------------------------------------
  //SetFixedVert
procedure THexEditor.SetFixedVert(const Value: boolean);
  begin
    FFixedVert := Value;
    Refresh;
  end;



  //-----------------------------------------------------------------
  //SetMarker
procedure THexEditor.SetMarker(MarkerNo: Integer);
  var
    R : integer;
  begin
    R := FMarkers[MarkerNo] div LL;
    FMarkers[MarkerNo] := FindOffset(Col, Row);
    InvalidateRow(Row);
    InvalidateRow(R);
    Invalidate;
  end;



  //-----------------------------------------------------------------
  //FindMarker
function THexEditor.FindMarker(AOffset: integer): integer;
  var
    n : integer;
    f : boolean;
  begin
    f := false;
    n := 0;
    while (n<CMarkers) and (not f) do
    begin
      Inc(n);
      if AOffset = FMarkers[n-1] then
        f := true;
    end;

    if f = false then
    begin
      n:=0;
    end;
    Result := n;
  end;



  //-----------------------------------------------------------------
  //GotoMarker
procedure THexEditor.GotoMarker(MarkerNo: integer);
  var
    lLine, lColumn : integer;
  begin
    if FMarkers[MarkerNo]>=0 then
    begin
      lLine := FMarkers[MarkerNo] div LL;
      lColumn := FMarkers[MarkerNo] mod LL;

      // Update the Row
      if
        (TopRow*LL<=FMarkers[MarkerNo]) and
        ( (TopRow+VisibleRowCount)*LL>=FMarkers[MarkerNo]) then
      begin
        // The marker is within the Visible space
        Row := lLine+FixedRows;
      end else
      begin
        // The marker is not within the Visible space, so make it Visible
        TopRow := lLine+FixedRows;
        Row := lLine+FixedRows;;
      end;

      // Update the Column
      if Col >= CAscData then
      begin
        Col := CAscData + lColumn;
      end else
      begin
        Col := CHexData + lColumn*3;
      end;
    end;
  end;

{v0.47}
procedure THexEditor.DoEnter;
begin
  inherited;
  {CreateCaret;}
end;

procedure THexEditor.DoExit;
begin
  inherited;
  DestroyCaret;
end;

procedure THexEditor.DestroyCaret;
begin
  if FCaretCreated  then begin
    FCaretCreated := false;
    Windows.DestroyCaret;
  end;
end;
{/v0.47}

end.
