unit beBIFFGrid;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Controls, Grids, fpspreadsheet;

type
  TBIFFBuffer = array of byte;

  TBIFFDetailsEvent = procedure(Sender: TObject; ADetails: TStrings) of object;

  TBIFFGrid = class(TStringGrid)
  private
    FRecType: Word;
    FBuffer: TBIFFBuffer;
    FBufferIndex: LongWord;
    FFormat: TsSpreadsheetFormat;
    FCurrRow: Integer;
    FDetails: TStrings;
    FOnDetails: TBIFFDetailsEvent;
    function  GetStringType: String;

    procedure ShowBackup;
    procedure ShowBlankCell;
    procedure ShowBOF;
    procedure ShowBookBool;
    procedure ShowBottomMargin;
    procedure ShowCalcCount;
    procedure ShowCalcMode;
    procedure ShowCellAddress;
    procedure ShowCellAddressRange;
    procedure ShowClrtClient;
    procedure ShowCodePage;
    procedure ShowColInfo;
    procedure ShowColWidth;
    procedure ShowCountry;
    procedure ShowDateMode;
    procedure ShowDBCell;
    procedure ShowDefColWidth;
    procedure ShowDefinedName;
    procedure ShowDefRowHeight;
    procedure ShowDelta;
    procedure ShowDimensions;
    procedure ShowDSF;
    procedure ShowEOF;
    procedure ShowExcel9File;
    procedure ShowExternalBook;
    procedure ShowExternCount;
    procedure ShowExternSheet;
    procedure ShowFileSharing;
    procedure ShowFnGroupCount;
    procedure ShowFont;
    procedure ShowFontColor;
    procedure ShowFooter;
    procedure ShowFormat;
    procedure ShowFormatCount;
    procedure ShowFormula;
    procedure ShowFormulaTokens(ATokenBytes: Integer);
    procedure ShowGCW;
    procedure ShowHeader;
    procedure ShowHideObj;
    procedure ShowInteger;
    procedure ShowInterfaceEnd;
    procedure ShowInterfaceHdr;
    procedure ShowIteration;
    procedure ShowIXFE;
    procedure ShowLabelCell;
    procedure ShowLabelSSTCell;
    procedure ShowLeftMargin;
    procedure ShowMergedCells;
    procedure ShowMMS;
    procedure ShowMulBlank;
    procedure ShowMulRK;
    procedure ShowNote;
    procedure ShowNumberCell;
    procedure ShowObj;
    procedure ShowPageSetup;
    procedure ShowPalette;
    procedure ShowPane;
    procedure ShowPassword;
    procedure ShowPrecision;
    procedure ShowPrintGridLines;
    procedure ShowPrintHeaders;
    procedure ShowProt4Rev;
    procedure ShowProt4RevPass;
    procedure ShowProtect;
    procedure ShowRecalc;
    procedure ShowRefMode;
    procedure ShowRefreshAll;
    procedure ShowRightMargin;
    procedure ShowRK;
    procedure ShowRow;
    procedure ShowSelection;
    procedure ShowSharedFormula;
    procedure ShowSheet;
    procedure ShowSheetPR;
    procedure ShowSST;
    procedure ShowStandardWidth;
    procedure ShowString;
    procedure ShowStyle;
    procedure ShowStyleExt;
    procedure ShowTabID;
    procedure ShowTopMargin;
    procedure ShowWindow1;
    procedure ShowWindow2;
    procedure ShowWindowProtect;
    procedure ShowWriteAccess;
    procedure ShowWriteProt;
    procedure ShowXF;
    procedure ShowXFCRC;
    procedure ShowXFEXT;

  protected
    procedure Click; override;
    procedure DoExtractDetails;
    function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    procedure ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
      out AString: String; out ANumBytes: Integer; IgnoreCompressedFlag: Boolean = false);
    procedure PopulateGrid;
    procedure ShowInRow(var ARow: Integer; var AOffs: LongWord; ASize: Word; AValue,ADescr: String);
    procedure ShowRowColData(var ABufIndex: LongWord);

  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    procedure SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer; AFormat: TsSpreadsheetFormat);

  published
    property OnDetails: TBIFFDetailsEvent read FOnDetails write FOnDetails;
  end;

implementation

uses
  StrUtils, Math,
  fpsutils,
  beBIFFUtils;

const
  ABS_REL: array[boolean] of string = ('abs', 'rel');

constructor TBIFFGrid.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  ColCount := 4;
  FixedCols := 0;
  RowCount := 2;
  Cells[0, 0] := 'Offset';
  Cells[1, 0] := 'Size';
  Cells[2, 0] := 'Value';
  Cells[3, 0] := 'Description';
  ColWidths[0] := 60;
  ColWidths[1] := 60;
  ColWidths[2] := 120;
  ColWidths[3] := 350;
  Options := Options + [goThumbTracking, goColSizing, goTruncCellHints, goCellHints] - [goVertLine];
  FDetails := TStringList.Create;
end;


destructor TBIFFGrid.Destroy;
begin
  FDetails.Free;
  inherited;
end;


procedure TBIFFGrid.Click;
begin
  inherited;
  if (FBuffer <> nil) then
    DoExtractDetails;
end;


procedure TBIFFGrid.DoExtractDetails;
begin
  if Assigned(FOnDetails) then begin
    PopulateGrid;
    FOnDetails(self, FDetails);
  end;
end;

function TBIFFGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint
  ): Boolean;
begin
  Result := inherited;
  Click;
end;

function TBIFFGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint
  ): Boolean;
begin
  Result := inherited;
  Click;
end;

procedure TBIFFGrid.ExtractString(ABufIndex: Integer; ALenBytes: Byte; AUnicode: Boolean;
  out AString: String; out ANumBytes: Integer; IgnoreCompressedFlag: Boolean = false);
var
  ls: Integer;
  sa: ansiString;
  sw: WideString;
  w: Word;
  optn: Byte;
begin
  if Length(FBuffer) = 0 then begin
    AString := '';
    ANumBytes := 0;
    exit;
  end;
  if ALenBytes = 1 then
    ls := FBuffer[ABufIndex]
  else begin
    Move(FBuffer[ABufIndex], w, 2);
    ls := WordLEToN(w);
  end;
  if AUnicode then begin
    optn := FBuffer[ABufIndex + ALenBytes];
    if (optn  and $01 = 0) and (not IgnoreCompressedFlag)
    then begin   // compressed --> 1 byte per character
      SetLength(sa, ls);
      ANumbytes := ls*SizeOf(AnsiChar) + ALenBytes + 1;
      Move(FBuffer[ABufIndex + ALenBytes + 1], sa[1], ls*SizeOf(AnsiChar));
      AString := sa;
    end else begin
      SetLength(sw, ls);
      ANumBytes := ls*SizeOf(WideChar) + ALenBytes + 1;
      Move(FBuffer[ABufIndex + ALenBytes + 1], sw[1], ls*SizeOf(WideChar));
      AString := UTF8Encode(WideStringLEToN(sw));
    end;
  end else begin
    SetLength(sa, ls);
    ANumBytes := ls*SizeOf(AnsiChar) + ALenBytes;
    Move(FBuffer[ABufIndex + ALenBytes], sa[1], ls*SizeOf(AnsiChar));
    AString := AnsiToUTF8(sa);
  end;
end;


function TBIFFGrid.GetStringType: String;
begin
  case FFormat of
    sfExcel2: Result := 'Byte';
    sfExcel5: Result := 'Byte';
    sfExcel8: Result := 'Unicode';
  end;
end;


procedure TBIFFGrid.PopulateGrid;
begin
  FBufferIndex := 0;
  FCurrRow := FixedRows;
  FDetails.Clear;
  case FRecType of
    $0000, $0200:
      ShowDimensions;
    $0001, $0201:
      ShowBlankCell;
    $0002:
      ShowInteger;
    $0003, $0203:
      ShowNumberCell;
    $0004, $0204:
      ShowLabelCell;
    $0006:
      ShowFormula;
    $0007, $0207:
      ShowString;
    $0008, $0208:
      ShowRow;
    $0009, $0209, $0409, $0809:
      ShowBOF;
    $000A:
      ShowEOF;
    $000C:
      ShowCalcCount;
    $000D:
      ShowCalcMode;
    $000E:
      ShowPrecision;
    $000F:
      ShowRefMode;
    $0010:
      ShowDelta;
    $0011:
      ShowIteration;
    $0012:
      ShowProtect;
    $0013:
      ShowPassword;
    $0014:
      ShowHeader;
    $0015:
      ShowFooter;
    $0016:
      ShowExternCount;
    $0017:
      ShowExternSheet;
    $0018, $0218:
      ShowDefinedName;
    $0019:
      ShowWindowProtect;
    $001C:
      ShowNote;
    $001D:
      ShowSelection;
    $001E, $041E:
      ShowFormat;
    $001F:
      ShowFormatCount;
    $0022:
      ShowDateMode;
    $0024:
      ShowColWidth;
    $0025, $0225:
      ShowDefRowHeight;
    $0026:
      ShowLeftMargin;
    $0027:
      ShowRightMargin;
    $0028:
      ShowTopMargin;
    $0029:
      ShowBottomMargin;
    $002A:
      ShowPrintHeaders;
    $002B:
      ShowPrintGridLines;
    $0031:
      ShowFont;
    $003D:
      ShowWindow1;
    $003E, $023E:
      ShowWindow2;
    $0040:
      ShowBackup;
    $0041:
      ShowPane;
    $0042:
      ShowCodePage;
    $0043:
      ShowXF;
    $0044:
      ShowIXFE;
    $0045:
      ShowFontColor;
    $0055:
      ShowDefColWidth;
    $005B:
      ShowFileSharing;
    $005C:
      ShowWriteAccess;
    $005D:
      ShowObj;
    $005F:
      ShowRecalc;
    $007D:
      ShowColInfo;
    $0081:
      ShowSheetPR;
    $0085:
      ShowSheet;
    $0086:
      ShowWriteProt;
    $008C:
      ShowCountry;
    $008D:
      ShowHideObj;
    $0092:
      ShowPalette;
    $099:
      ShowStandardWidth;
    $00A1:
      ShowPageSetup;
    $00AB:
      ShowGCW;
    $00C1:
      ShowMMS;
    $009C:
      ShowFnGroupCount;
    $00BE:
      ShowMulBlank;
    $00BD:
      ShowMulRK;
    $00D7:
      ShowDBCell;
    $00DA:
      ShowBookBool;
    $00E0:
      ShowXF;
    $00E1:
      ShowInterfaceHdr;
    $00E2:
      ShowInterfaceEnd;
    $00E5:
      ShowMergedCells;
    $00FC:
      ShowSST;
    $00FD:
      ShowLabelSSTCell;
    $013D:
      ShowTabID;
    $0161:
      ShowDSF;
    $01AE:
      ShowExternalBook;
    $01AF:
      ShowProt4Rev;
    $01B7:
      ShowRefreshAll;
    $01BC:
      ShowProt4RevPass;
    $01C0:
      ShowExcel9File;
    $027E:
      ShowRK;
    $0293:
      ShowStyle;
    $04BC:
      ShowSharedFormula;
    $087C:
      ShowXFCRC;
    $087D:
      ShowXFEXT;
    $0892:
      ShowStyleExt;
    $105C:
      ShowClrtClient;
    else
      RowCount := 2;
      Rows[1].Clear;
  end;
end;


procedure TBIFFGrid.SetRecordType(ARecType: Word; ABuffer: TBIFFBuffer;
  AFormat: TsSpreadsheetFormat);
begin
  FFormat := AFormat;
  FRecType := ARecType;
  SetLength(FBuffer, Length(ABuffer));
  if Length(FBuffer) > 0 then
    Move(ABuffer[0], FBuffer[0], Length(FBuffer));
  PopulateGrid;
  if Assigned(FOnDetails) then FOnDetails(self, FDetails);
end;


procedure TBIFFGrid.ShowBackup;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Save backup copy of workbook:'#13);
    if w = 0
      then FDetails.Add('0 = no backup')
      else FDetails.Add('1 = backup copy is saved when workbook is saved');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
    'Save backup copy of workbook');
end;


procedure TBIFFGrid.ShowBlankCell;
var
  numBytes: Integer;
  b: Byte = 0;
  w: Word = 0;
  dbl: Double;
begin
  RowCount := IfThen(FFormat = sfExcel2, FixedRows + 5, FixedRows + 3);
  // Offset 0: Row & Offset 2: Column
  ShowRowColData(FBufferIndex);

  // Offset 4: Cell attributes (BIFF2) or XF record index (> BIFF2)
  if FFormat = sfExcel2 then begin
    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell protection and XF index:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
      case b and $40 of
        0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
        1: FDetails.Add('Bit 6 = 1: Cell is locked.');
      end;
      case b and $80 of
        0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
        1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
      'Cell protection and XF index');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Indexes to format and font records:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
      FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
      'Indexes of format and font records');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell style:'#13);
      case b and $07 of
        0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
        1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
        2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
        3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
        4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
      end;
      if b and $08 = 0
        then FDetails.Add('Bit 3 = 0: Cell has NO left border')
        else FDetails.Add('Bit 3 = 1: Cell has left black border');
      if b and $10 = 0
        then FDetails.Add('Bit 4 = 0: Cell has NO right border')
        else FDetails.Add('Bit 4 = 1: Cell has right black border');
      if b and $20 = 0
        then FDetails.Add('Bit 5 = 0: Cell has NO top border')
        else FDetails.Add('Bit 5 = 1: Cell has top black border');
      if b and $40 = 0
        then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
        else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
      if b and $80 = 0
        then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
        else FDetails.Add('Bit 7 = 1: Cell has shaded background');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
      'Cell style');
  end else
  begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Index of XF record');
  end;
end;


procedure TBIFFGrid.ShowBOF;
var
  numBytes: Integer;
  w: Word;
  s: String;
begin
  case FFormat of
    sfExcel2: RowCount := FixedRows + 2;
    { //Excel3 & 4 not supported by fpspreadsheet
    sfExcel3, sfExcel4: RowCount := FixedRows + 3;
    }
    sfExcel5: RowCount := FixedRows + 4;
    sfExcel8: RowCount := FixedRows + 6;
  end;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('BIFF version:'#13);
    case FRecType of
      $0009,
      $0209,
      $0409: FDetails.Add('not used');
      $0809: case FFormat of
               sfExcel5: FDetails.Add('$0500 = BIFF5');
               sfExcel8: FDetails.Add('$0600 = BIFF8');
             end;
      else   case w of
               $0000: FDetails.Add('$0000 = BIFF5');
               $0200: FDetails.Add('$0200 = BIFF2');
               $0300: FDetails.Add('$0300 = BIFF3');
               $0400: FDetails.Add('$0400 = BIFF4');
               $0500: FDetails.Add('$0500 = BIFF5');
               $0600: FDetails.Add('$0600 = BIFF8');
             end;
    end;
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
    'BIFF version');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  s := '$0010=Sheet, $0020=Chart, $0040=Macro sheet';
  if FFormat > sfExcel2 then
    s := '$0005=WB globals, $0006=VB module, ' + s + ', $0100=Workspace';
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Type of data:'#13);
    FDetails.Add(Format('$%.4x = %s', [w, BofName(w)]));
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
    Format('Type of data (%s)', [s]));

  if FFormat > sfExcel2 then begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    { Excel3/4 not supported in fpSpreadsheet
    if FFormat in [sfExcel3, sfExcel4] then
      ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
        'not used')
    else}
    begin
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
        'Build identifier (must not be zero)');

      numBytes := 2;
      Move(FBuffer[FBufferIndex], w, numBytes);
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
        'Build year (must not be zero)');
    end;
  end;

  if FFormat = sfExcel8 then begin
    numBytes := 4;
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
      'File history flags');

    numBytes :=4;
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
      'Lowest Excel version that can read all records of this file');
  end;
end;


procedure TBIFFGrid.ShowBookBool;
var
  numbytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Some properties assosciated with notebook:'#13);
    if w and $0001 = 0
      then FDetails.Add('Bit 0 = 0: External link values are saved.')
      else FDetails.Add('Bit 0 = 1: External link values are NOT saved.');
    FDetails.Add('Bit 1: to be ignored');
    if w and $0004 = 0
      then FDetails.Add('Bit 2 = 0: Workbook does not have a mail envelope')
      else FDetails.Add('Bit 2 = 1: Workbook has a mail envelope');
    if w and $0008 = 0
      then FDetails.Add('Bit 3 = 0: Mail envelope is NOT visible.')
      else FDetails.Add('Bit 3 = 1: Mail envelope is visible.');
    if w and $0010 = 0
      then FDetails.Add('Bit 4 = 0: Mail envelope has NOT been initialized.')
      else FDetails.Add('Bit 4 = 1: Mail envelope has been initialized.');
    case (w and $0060) shr 5 of
      0: FDetails.Add('Bits 5-6 (Update external links) = 0: Prompt user to update');
      1: FDetails.Add('Bits 5-6 (Update external linls) = 1: Do not update, and do not prompt user.');
      2: FDetails.Add('Bits 5-6 (Update external links) = 2: Silently update external links.');
    end;
    FDetails.Add('Bit 7: undefined, must be ignored');
    if w and $0100 = 0
      then FDetails.Add('Bit 8 = 0: Do not hide borders of tables that do not contain the active cell')
      else FDetails.Add('Bit 8 = 1: Hide borders of tables that do not contain the active cell');
    FDetails.Add('Bits 9-15: MUST BE zero, MUST be ignored');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
    'Specifies some properties assosciated with a workbook');
end;

procedure TBIFFGrid.ShowBottomMargin;
var
  numBytes: Integer;
  dbl: Double;
begin
  RowCount := FixedRows + 1;
  numBytes := 8;
  Move(FBuffer[FBufferIndex], dbl, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
    'Bottom page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;


procedure TBIFFGrid.ShowCalcCount;
var
  numBytes: Word;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Maximum number of iterations allowed in circular references');
end;


procedure TBIFFGrid.ShowCalcMode;
var
  numBytes: Word;
  w: word;
  s: String;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if w = $FFFF then
    s := '–1 = automatically except for multiple table operations'
  else if w = 0 then
    s := '0 = manually'
  else if w = 1 then
    s := '1 = automatically (default)';
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), s);
end;


procedure TBIFFGrid.ShowCellAddress;
{ Note: The bitmask assignment to relative column/row is reversed in relation
  to OpenOffice documentation in order to match with Excel files. }
var
  numBytes: Word;
  b: Byte;
  w: Word;
  r,c: Integer;
  s: String;
begin
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);   // row --> w
  r := WordLEToN(w);
  if FFormat = sfExcel8 then begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex+2], w, numBytes);  // column --w1
    c := WordLEToN(w);
    if FCurrRow = Row then begin
      FDetails.Add('RowIndex information:'#13);
      FDetails.Add(Format('RowIndex = %d (%s)', [r, ABS_REL[c and $4000 <> 0]]));
    end;
    s := Format('%d ($%.4x)', [r, r]);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Row index');
    if FCurrRow = Row then begin
      FDetails.Add('ColIndex information:'#13);
      FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c and $3FFF, ABS_REL[c and $8000 <> 0]]));
      if c and $4000 = 0
        then FDetails.Add('Bit 14=0: absolute column index')
        else FDetails.Add('Bit 14=1: relative column index');
      if c and $8000 = 0
        then FDetails.Add('Bit 15=0: absolute row index')
        else FDetails.Add('Bit 15=1: relative row index');
    end;
    s := Format('%d ($%.4x)', [c, c]);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Column index');
  end else
  begin
    numbytes := 1;
    Move(FBuffer[FBufferIndex+2], b, numBytes);
    c := b;
    if FCurrRow = Row then begin
      FDetails.Add('RowIndex information:'#13);
      FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r and $3FFF, ABS_REL[r and $4000 <> 0]]));
      if r and $4000 = 0
        then FDetails.Add('Bit 14=0: absolute column index')
        else FDetails.Add('Bit 14=1: relative column index');
      if r and $8000 = 0
        then FDetails.Add('Bit 15=0: absolute row index')
        else FDetails.Add('Bit 15=1: relative row index');
    end;
    //s := Format('$%.4x (%d, %s)', [r, r and $3FFF, ABS_REL[r and $4000 <> 0]]);
    s := Format('%d ($%.4x)', [r, r]);
    ShowInRow(FCurrRow, FBufferIndex, 2, s, 'Row index');
    if FCurrRow = Row then begin
      FDetails.Add('ColIndex information:'#13);
      FDetails.Add(Format('ColIndex = %d (%s)', [c, ABS_REL[r and $8000 <> 0]]));
    end;
    //s := Format('$%.2x (%d, %s)', [c, c, ABS_REL[r and $8000 <> 0]]);
    s := Format('%d ($%.4x)', [c, c]);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Column index');
  end;
end;


procedure TBIFFGrid.ShowCellAddressRange;
{ Note: The bitmask assignment to relative column/row is reversed in relation
  to OpenOffice documentation in order to match with Excel files. }
var
  numbytes: Word;
  b: Byte;
  w: Word;
  r, c, r2, c2: Integer;
  s: String;
begin
  if FFormat = sfExcel8 then begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    r := WordLEToN(w);
    if FCurrRow = Row then begin
      FDetails.Add('RowIndex information:'#13);
      FDetails.Add(Format('RowIndex = %d (%s)', [r, ABS_REL[c and $4000 <> 0]]));
    end;
    s := Format('%d ($%.4x)', [r, r]);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'First row index');

    Move(FBuffer[FBufferIndex], w, numBytes);
    r2 := WordLEToN(w);
    if FCurrRow = Row then begin
      FDetails.Add('RowIndex information:'#13);
      FDetails.Add(Format('RowIndex = %d (%s)', [r2, ABS_REL[c and $4000 <> 0]]));
    end;
    s := Format('%d ($%.4x)', [r2, r2]);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Last row index');

    Move(FBuffer[FBufferIndex], w, numBytes);  // column
    c := WordLEToN(w);
    Move(FBuffer[FBufferIndex+2], w, numBytes);
    c2 := WordLEToN(w);

    if FCurrRow = Row then begin
      FDetails.Add('ColIndex information:'#13);
      FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c and $3FFF, ABS_REL[c and $8000 <> 0]]));
      if c and $4000 = 0
        then FDetails.Add('Bit 14=0: absolute column index')
        else FDetails.Add('Bit 14=1: relative column index');
      if c and $8000 = 0
        then FDetails.Add('Bit 15=0: absolute row index')
        else FDetails.Add('Bit 15=1: relative row index');
    end;
    s := Format('%d ($%.4x)', [c, c]);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'First column index');

    if FCurrRow = Row then
    begin
      FDetails.Add('ColIndex information:'#13);
      FDetails.Add(Format('Bits 0-13: ColIndex = %d (%s)', [c2 and $3FFF, ABS_REL[c2 and $8000 <> 0]]));
      if c2 and $4000 = 0
        then FDetails.Add('Bit 14=0: absolute column index')
        else FDetails.Add('Bit 14=1: relative column index');
      if c2 and $8000 = 0
        then FDetails.Add('Bit 15=0: absolute row index')
        else FDetails.Add('Bit 15=1: relative row index');
    end;
    s := Format('%d ($%.4x)', [c2, c2]);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Last column index');
  end
  else
  begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    r := WordLEToN(w);
    Move(FBuffer[FBufferIndex+2], w, numBytes);
    r2 := WordLEToN(w);

    numbytes := 1;
    c := FBuffer[FBufferIndex+4];
    c2 := FBuffer[FBufferIndex+5];

    if FCurrRow = Row then
    begin
      FDetails.Add('RowIndex information:'#13);
      FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r and $3FFF, ABS_REL[r and $4000 <> 0]]));
      if r and $4000 = 0
        then FDetails.Add('Bit 14=0: absolute column index')
        else FDetails.Add('Bit 14=1: relative column index');
      if r and $8000 = 0
        then FDetails.Add('Bit 15=0: absolute row index')
        else FDetails.Add('Bit 15=1: relative row index');
    end;
    s := Format('%d ($%.4x)', [r, r]);
    ShowInRow(FCurrRow, FBufferIndex, 2, s, 'First row index');

    if FCurrRow = Row then
    begin
      FDetails.Add('RowIndex information:'#13);
      FDetails.Add(Format('Bits 0-13: RowIndex = %d (%s)', [r2 and $3FFF, ABS_REL[r2 and $4000 <> 0]]));
      if r2 and $4000 = 0
        then FDetails.Add('Bit 14=0: absolute column index')
        else FDetails.Add('Bit 14=1: relative column index');
      if r2 and $8000 = 0
        then FDetails.Add('Bit 15=0: absolute row index')
        else FDetails.Add('Bit 15=1: relative row index');
    end;
    s := Format('%d ($%.4x)', [r2, r2]);
    ShowInRow(FCurrRow, FBufferIndex, 2, s, 'Last row index');

    if FCurrRow = Row then
    begin
      FDetails.Add('ColIndex information:'#13);
      FDetails.Add(Format('ColIndex = %d (%s)', [c, ABS_REL[r and $8000 <> 0]]));
    end;
    s := Format('%d ($%.4x)', [c, c]);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'First column index');

    if FCurrRow = Row then
    begin
      FDetails.Add('ColIndex information:'#13);
      FDetails.Add(Format('ColIndex = %d (%s)', [c2, ABS_REL[r2 and $8000 <> 0]]));
    end;
    s := Format('%d ($%.4x)', [c2, c2]);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Last column index');
  end;
end;


procedure TBIFFGrid.ShowClrtClient;
var
  w: Word;
  dw: DWord;
  numbytes: Word;
begin
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);

  RowCount := FixedRows + w + 1;

  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Number of colors (must be 3)');

  numBytes := 4;
  Move(FBuffer[FBufferIndex], dw, numbytes);
  dw := DWordLEToN(dw);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
    'Foreground color (system window text color)');
  Move(FBuffer[FBufferIndex], dw, numbytes);
  dw := DWordLEToN(dw);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
    'Background color (system window color)');
  Move(FBuffer[FBufferIndex], dw, numbytes);
  dw := DWordLEToN(dw);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]), '???');
end;


procedure TBIFFGrid.ShowCodePage;
var
  numBytes: Word;
  w: Word;
  s: String;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  s := CodePageName(w);
  if Row = FCurrRow then begin
    FDetails.Add('Code page:'#13);
    FDetails.Add(Format('$%.04x = %s', [w, s]));
  end;
  if s <> '' then s := 'Code page identifier (' + s + ')' else s := 'Code page identifier';
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]), s);
end;


procedure TBIFFGrid.ShowColInfo;
var
  numBytes: Integer;
  w: Word;
begin
  if FFormat = sfExcel2 then
    exit;

  RowCount := FixedRows + 5;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index of first column in range');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index of last column in range');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d (%f characters)', [w, w/256]),
    'Width of the columns in 1/256 of the width of the zero character, using default font (first FONT record in the file)');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Index to XF record for default column formatting');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Column options:'#13);
    if w and $0001 = 0
      then FDetails.Add('Bit  $0001 = 0: Columns are NOT hidden')
      else FDetails.Add('Bit  $0001 = 1: Columns are hidden');
    FDetails.Add(Format('Bits $0700 = %d: Outline level of the columns (0 = no outline)', [(w and $0700) shr 8]));
    if w and $1000 = 0
      then FDetails.Add('Bit  $1000 = 0: Columns are NOT collapsed')
      else FDetails.Add('Bit  $1000 = 1: Columns are collapsed');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Option flags');
end;


procedure TBIFFGrid.ShowColWidth;
var
  numBytes: Integer;
  w: Word;
  b: Byte;
begin
  if FFormat <> sfExcel2 then
    exit;

  RowCount := FixedRows + 3;

  numBytes := 1;
  b := FBuffer[FBufferIndex];
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
    'Index of first column');

  numBytes := 1;
  b := FBuffer[FBufferIndex];
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
    'Index of last column');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Width of the columns in 1/256 of the width of the zero character, using default font (first FONT record in the file)');
end;


procedure TBIFFGrid.ShowCountry;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 2;
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Windows country identifier for UI language of Excel');

  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Windows country identifier of system regional settings');
end;


procedure TBIFFGrid.ShowDateMode;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    '0 = Base date is 1899-Dec-31, 1 = Base date is 1904-Jan-01');
end;

procedure TBIFFGrid.ShowDBCell;
var
  i, n: Integer;
  dw: DWord;
  w: Word;
  numBytes: Integer;
begin
  if FFormat < sfExcel5 then exit;

  n := (Length(FBuffer) - 4) div 2;
  RowCount := FixedRows + 1 + n;

  numBytes := 4;
  Move(FBuffer[FBufferIndex], dw, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)),
    'Relative offset to first ROW record in the Row Block');

  numBytes := 2;
  for i:=1 to n do begin
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Relative offsets to calculate stream position of the first cell record in row');
  end;
end;

procedure TBIFFGrid.ShowDefColWidth;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Column width in characters, using the width of the zero character from default '+
    'font (first FONT record in the file) + some extra space.');
end;


procedure TBIFFGrid.ShowDefinedName;
var
  numBytes: Integer;
  b: Byte;
  w: Word;
  isFuncMacro: Boolean;
  lenName: Word;
  ansistr: AnsiString;
  widestr: WideString;
  s: String;
  macro: Boolean;
  formulaSize: Word;
  firstTokenBufIdx: Integer;
  token: Byte;
  r,c, r2,c2: Integer;
begin
  BeginUpdate;
  RowCount := FixedRows + 1000;
  // Brute force simplification because of unknown row count at this point
  // Will be reduced at the end.

  if FFormat = sfExcel2 then
  begin
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    isFuncMacro := b and $02 <> 0;
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:'#13);
      if b and $02 = 0 then
        FDetails.Add('Bit $02 = 0: NO function macro or command macro')
      else
        FDetails.Add('Bit $02 = 1: Function macro or command macro');
      if b and $04 = 0 then
        FDetails.Add('Bit $04 = 0: NO Complex function (array formula or user defined)')
      else
        FDetails.Add('Bit $04 = 1: Complex function (array formula or user defined)');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
      'Option flags');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    if isFuncMacro then
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
        '$01 = Function macro, $02 = Command macro')
    else
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
        'unknown');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Keyboard shortcut (only for command macro names)');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
      'Length of the name (character count)');
    lenName := b;

    numbytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b),
      'Size of the formula data');
    formulaSize := b;
  end
  else
  begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      macro := (w and $0008 <> 0);
      FDetails.Add('Option flags:'#13);
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 (flag name "hidden") = 0: Visible')
        else FDetails.Add('Bit $0001 (flag name "hidden")= 1: Hidden');
      if w and $0002 = 0
        then FDetails.Add('Bit §0002 (flag name "func") = 0: Command macro')
        else FDetails.Add('Bit $0002 (flag name "func") = 1: Function macro');
      if w and $0004 = 0
        then FDetails.Add('Bit $0004 (flag name "vbasic") = 0: Sheet macro')
        else FDetails.Add('Bit $0004 (flag name "vbasic") = 1: Visual basic macro');
      if w and $0008 = 0
        then FDetails.Add('Bit $0008 (flag name "macro") = 0: Standard name')
        else FDetails.Add('Bit $0008 (flag name "macro") = 1: Macro name');
      if w and $0010 = 0
        then FDetails.Add('Bit $0010 (flag name "complex") = 0: Simple formula')
        else FDetails.Add('Bit $0010 (flag name "complex") = 1: Complex formula (array formula or user defined)');
      if w and $0020 = 0
        then FDetails.Add('Bit $0020 (flag name "builtin") = 0: User-defined name')
        else FDetails.Add('Bit $0020 (flag name "builtin") = 1: Built-in name');
      case (w and $0FC0) shr 6 of
        0: if macro then
             FDetails.Add('Bit $0FC0 = 0: --- forbidden value, must be > 0 ---')
           else
             FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 0: not used (requires "macro" = 1)');
        1: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 1: financial');
        2: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 2: date & time');
        3: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 3: math & trig');
        4: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 4: statistical');
        5: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 5: lookup & reference');
        6: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 6: database');
        7: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 7: text');
        8: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 8: logical');
        9: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 9: information');
       10: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 10: commands');
       11: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 11: customizing');
       12: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 12: macro control');
       13: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 13: dde/external');
       14: FDetails.Add('Bit $0FC0 (flag name "funcgroup") = 14: user defined');
      end;
      if w and $1000 = 0
        then FDetails.Add('Bit $1000 (flag name "binary") = 0: formula definition')
        else FDetails.add('Bit $1000 (flag name "binary") = 1: binary data (BIFF5-BIFF8)');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Option flags');

    numbytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInrow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
      'Keyboard shortcurt (only for command macro names)');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      'Length of the name (character count)');
    lenName := b;

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
      'Size of the formula data');
    formulaSize := w;

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if FFormat = sfExcel5 then
      ShowInRow(FCurrRow, FBufferIndex, NumBytes, IntToStr(w),
        '0 = Global name, otherwise index to EXTERNSHEET record (one-based)')
    else
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
        'not used');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, NumBytes, IntToStr(w),
      '0 = Global name, otherwise index to sheet (one-based)');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
      'Length of the menu text (character count)');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
      'Length of the description text (character count)');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
      'Length of the help topic text (character count)');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, nuMbytes, IntToStr(b),
      'Length of the status bar text (character count)');

    if FFormat = sfExcel5 then begin
      numBytes := lenName * sizeOf(ansiChar);
      SetLength(ansiStr, lenName);
      Move(FBuffer[FBufferIndex], ansiStr[1], numbytes);
      ShowInRow(FCurrRow, FBufferIndex, numBytes, ansiStr,
        'Character array of the name');
    end else begin

      if (FBuffer[FBufferIndex] and $01 = 0) //and (not IgnoreCompressedFlag)
      then begin   // compressed --> 1 byte per character
        SetLength(ansiStr, lenName);
        numbytes := lenName*SizeOf(ansiChar) + 1;
        Move(FBuffer[FBufferIndex + 1], ansiStr[1], lenName*SizeOf(AnsiChar));
        s := AnsiToUTF8(ansiStr);
      end else begin
        SetLength(wideStr, lenName);
        numBytes := lenName*SizeOf(WideChar) + 1;
        Move(FBuffer[FBufferIndex + 1], wideStr[1], lenName*SizeOf(WideChar));
        s := UTF8Encode(WideStringLEToN(wideStr));
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
        'Name (Unicode string without length field)');
    end;
  end;

  firstTokenBufIdx := FBufferIndex;
  while FBufferIndex < firstTokenBufIdx + formulaSize do begin
    token := FBuffer[FBufferIndex];
    numBytes := 1;
    case token of
      $3A, $3B, $5A, $5B, $7A, $7B:
        begin
          case token of
            $3A: s := 'Token tRef3dR for "3D or external reference to a cell" (R = Reference)';
            $5A: s := 'Token tRef3dV for "3D or external reference to a cell" (V = Value)';
            $7A: s := 'Token tRef3dA for "3D or external reference to a cell" (A = Area)';
            $3B: s := 'Token tArea3dR for "3D or external reference to a cell range" (R = Reference)';
            $5B: s := 'Token tArea3dV for "3D or external reference to a cell range" (V = Value)';
            $7B: s := 'Token tArea3dA for "3D or external reference to a cell range" (A = Area)';
          end;
          ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]), s);

          numbytes := 2;
          Move(FBuffer[FBufferIndex], w, numBytes);
          w := WordLEToN(w);
          if FFormat = sfExcel5 then begin
            if w and $8000 <> 0 then begin  // negative value --> 3D reference
              ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(SmallInt(w)),
                '3D reference, 1-based index to EXTERNSHEET record');
              numBytes := 8;
              ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used');
              numBytes := 2;
              Move(FBuffer[FBufferIndex], w, numBytes);
              ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
                'Index to first referenced sheet ($FFFF = deleted sheet)');
              Move(FBuffer[FBufferIndex], w, numBytes);
              ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
                'Index to last referenced sheet ($FFFF = deleted sheet)');
            end else
            begin
              ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
                'External reference, 1-based index to EXTERNSHEET record');
              numBytes := 12;
              ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used');
            end;
          end else
            ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
              'Index to REF entry in EXTERNSHEET record');

          if token in [$3A, $5A, $7A] then
            ShowCellAddress        // Cell address
          else
            ShowCellAddressRange;  // Cell range
        end;

      else
        numBytes := 1;
        ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
          '(unknown token)');
    end;  // case
  end;  // while

  RowCount := FCurrRow;
  EndUpdate(true);
end;

procedure TBIFFGrid.ShowDefRowHeight;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + IfThen(FFormat = sfExcel2, 1, 2);

  if FFormat = sfExcel2 then begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Default height for unused rows:'#13);
      FDetails.Add(Format(
        'Bits $7FFF = %d: Default height for unused rows, in twips = 1/20 of a point',
        [w and $7FFF]));
      if w and $8000 = 0 then
        FDetails.Add('Bit $8000 = 0: Row height changed manually')
      else
        FDetails.Add('Bit $8000 = 1: Row height not changed manually');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Default height for unused rows, in twips = 1/20 of a point');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:'#13);
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 = 0: Row height and default font height do match')
        else FDetails.Add('Bit $0001 = 1: Row height and default font height do not match');
      if w and $0002 = 0
        then FDetails.Add('Bit $0002 = 0: Row is visible')
        else FDetails.Add('Bit $0002 = 1: Row is hidden');
      if w and $0004 = 0
        then FDetails.Add('Bit $0004 = 0: No additional space above the row')
        else FDetails.Add('Bit $0004 = 1: Additional space above the row');
      if w and $0008 = 0
        then FDetails.Add('Bit $0008 = 0: No additional space below the row')
        else FDetails.Add('Bit $0008 = 1: Additional space below the row');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [WordLEToN(w)]),
      'Option flags');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
      'Default height for unused rows, in twips = 1/20 of a point');
  end;
end;


procedure TBIFFGrid.ShowDelta;
var
  numBytes: Integer;
  dbl: Double;
begin
  RowCount := FixedRows + 1;
  numBytes := 8;
  Move(FBuffer[FBufferIndex], dbl, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
    'Maximum change in iteration (IEEE 754 floating-point value, 64-bit double precision)');
end;


procedure TBIFFGrid.ShowDimensions;
var
  numBytes: Integer;
  dw: DWord;
  w: Word;
begin
  RowCount := FixedRows + IfThen(FFormat = sfExcel2, 4, 5);

  if FFormat = sfExcel8 then begin
    numBytes := 4;
    Move(FBuffer[FBufferIndex], dw, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)),
      'Index to first used row');

    Move(FBuffer[FBufferIndex], dw, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(DWordLEToN(dw)),
      'Index to last used row, increased by 1');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
      'Index to first used row');

    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
      'Index to last used row, increased by 1');
  end;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to first used column');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to last used column, increased by 1');

  if FFormat <> sfExcel2 then begin
    numBytes := 2;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, '',
      '(not used)');
  end;
end;


procedure TBIFFGrid.ShowDSF;
var
  w: Word;
  numbytes: Integer;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Reserved, MUST be ignored');
end;


procedure TBIFFGrid.ShowEOF;
begin
  RowCount := FixedRows + 1;
  ShowInRow(FCurrRow, FBufferIndex, 0, '', '(no content)');
end;


procedure TBIFFGrid.ShowExcel9File;
begin
  RowCount := FixedRows + 1;
  ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Optional and unused');
end;


procedure TBIFFGrid.ShowExternalBook;
var
  numBytes: Integer;
  w: Word;
  wideStr: WideString;
  ansiStr: AnsiString;
  s: String;
  i, n: Integer;
begin
  BeginUpdate;
  RowCount := FixedRows + 1000;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  n := WordLEToN(w);

  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(n),
    'Number of sheet names / number of sheets');

  if Length(FBuffer) - FBufferIndex = 2 then begin
    SetLength(ansiStr, 1);
    Move(FBuffer[FBufferIndex], ansiStr[1], 1);

    ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
      '(relict of BIFF5)');
  end else begin
    ExtractString(FBufferIndex, 2, true, s, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Encoded URL without sheet name:'#13);
      case s[1] of
        #0: FDetails.Add('First character = #00: Reference relative to current sheet');
        #1: FDetails.Add('First character = #01: Encoded URL follows');
        #2: if FFormat = sfExcel8 then
              FDetails.Add('First character = #02: Reference to a sheet in own document; sheet name follows')
            else
              FDetails.Add('First character = #02: Reference to the corrent sheet (nothing will follow)');
        #3: if FFormat = sfExcel5 then
              FDetails.Add('First character = #03: Reference to a sheet in own document; sheet name follows')
            else
              FDetails.Add('First character = #03: not used');
        #4: if FFormat = sfExcel5 then
              FDetails.ADd('First character = #03: Reference to the own workbook, sheet is unspecified (nothing will follow)')
            else
              FDetails.Add('First character = #03: not used');
      end;
    end;
    if s[1] in [#0, #1, #2, #3, #4] then Delete(s, 1, 1);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
      'Encoded URL without sheet name (Unicode string, 16-bit string length)');

    for i:=0 to n-1 do begin
      ExtractString(FBufferIndex, 2, true, s, numBytes);
      ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
        'Sheet name (Unicode string with 16-bit string length)');
    end;
  end;

  RowCount := FCurrRow;
  EndUpdate(true);
end;


procedure TBIFFGrid.ShowExternCount;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Number of following EXTERNSHEET records');
end;


procedure TBIFFGrid.ShowExternSheet;
var
  numBytes: Integer;
  w: Word;
  s: String;
  nREF: Integer;
  i: Integer;
  len: Byte;
  ansiStr: AnsiString;
begin
  if FFormat <= sfExcel5 then begin
    RowCount := FixedRows + 1;
    len := FBuffer[0];
    if FBuffer[1] = $03 then inc(len);
    numBytes := len*SizeOf(AnsiChar) + 1;
    SetLength(ansiStr, len);
    Move(FBuffer[1], ansiStr[1], len*SizeOf(AnsiChar));
    s := AnsiToUTF8(ansiStr);
    if FCurrRow = Row then begin
      FDetails.Add('Encoded document and sheet name:'#13);
      if s[1] = #03 then begin
        FDetails.Add('First character = $03: EXTERNSHEET stores a reference to one of the own sheets');
        FDetails.Add('Document name: ' + Copy(s, 2, Length(s)));
      end else
      if (s[1] = ':') and (Length(s) = 1) then begin
        FDetails.Add('Special EXTERNSHEET record for an add-in function. EXTERNName record with the name of the function follows.');
      end else
        FDetails.Add('Document name: ' + s);
    end;
    if s[1] = #03 then
      Delete(s, 1, 1);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
      'Encoded document and sheet name (Byte string, 8-bit string length)');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    nREF := WordLEToN(w);

    RowCount := FixedRows + 1 + nREF*3;

    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(nREF),
      'Number of following REF structures');

    for i:=1 to nREF do begin
      Move(FBuffer[FBufferIndex], w, numBytes);
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
        Format('REF #%d: Index to EXTERNALBOOK record', [i]));

      Move(FBuffer[FBufferIndex], w, numBytes);
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
        Format('REF #%d: Index to first sheet in EXTERNALBOOK sheet list', [i]));

      Move(FBuffer[FBufferIndex], w, numBytes);
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
        Format('REF #%d: Index to last sheet in EXTERNALBOOK sheet list', [i]));
    end;
  end;
end;


procedure TBIFFGrid.ShowFileSharing;
var
  numbytes: Integer;
  w: Word;
  s: String;
begin
  RowCount := FixedRows + 3;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Recommend read-only state when loading the file:'#13);
    if w = 0 then FDetails.Add('0 = no') else FDetails.Add('1 = yes');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Recommend read-only state when loading the file');

  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
    'Hash value calculated from the read-only password');

  ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
    s, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
    'User name of the file creator' + IfThen(FFormat = sfExcel8,
    ' (Unicode string, 16-bit string length)',
    ' (byte string, 8-bit string length)'
  ));
end;


procedure TBIFFGrid.ShowFnGroupCount;
var
  numbytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Number of built-in function categories:'#13);
    case w of
      $000E:
        FDetails.Add(
          'There are 14 built-in function categories in the workbook.'#13+
          'This implies that the file was last saved by a specific version of the application.'#13+
          'The following 9 built-in function categories are visible to the end-user:'#13+
          '  Financial'#13+
          '  Date & Time'#13+
          '  Math & Trig'#13+
          '  Statistical'#13+
          '  Lookup & Reference'#13+
          '  Database'#13+
          '  Text'#13+
          '  Logical'#13+
          '  Information'#13+
          'The following 5 built-in function categories are not visible to the end-user:'#13+
          '  UserDefined'#13+
          '  Commands'#13+
          '  Customize'#13+
          '  MacroControl'#13+
          '  DDEExternal'
        );
      $0010:
        FDetails.Add(
          'There are 16 built-in function categories in the workbook.'#13+
          'This implies that the file was last saved by a specific version of the application'#13+
          'The following 11 built-in function categories are visible to the end-user:'#13+
          '  Financial'#13+
          '  Date & time'#13+
          '  Math & Trig'#13+
          '  Statistical'#13+
          '  Lookup & Reference'+
          '  Database'#13+
          '  Text'#13+
          '  Logical'#13+
          '  Information'#13+
          '  Engineering'#13+
          '  Cube'#13+
          'The following 5 built-in function categories are not visible to the end-user:'#13+
          '  UserDefined'#13+
          '  Commands'#13+
          '  Customize'#13+
          '  MacroControl'#13+
          '  DDEExternal'
        );
    end;
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
    'Number of built-in function categories');
end;


procedure TBIFFGrid.ShowFont;
var
  numbytes: Integer;
  w: Word;
  b: Byte;
  s: String;
begin
  RowCount := IfThen(FFormat = sfExcel2, 3, 10) + FixedRows;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
    'Font height in twips (=1/20 point)');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Option flags:'#13);
    if w and $0001 = 0
      then FDetails.Add('Bit $0001 = 0: not bold')
      else FDetails.Add('Bit $0001 = 1: bold (redundant in BIFF5-BIFF8)');
    if w and $0002 = 0
      then FDetails.Add('Bit $0002 = 0: not italic')
      else FDetails.Add('Bit $0002 = 1: italic');
    if w and $0004 = 0
      then FDetails.Add('Bit $0004 = 0: not underlined')
      else FDetails.Add('Bit $0004 = 1: underlined (redundant in BIFF5-BIFF8)');
    if w and $0008 = 0
      then FDetails.Add('Bit $0008 = 0: not struck out')
      else FDetails.Add('Bit $0008 = 1: struck out');
    if w and $0010 = 0
      then FDetails.Add('Bit $0010 = 0: not outlined')
      else FDetails.Add('Bit $0010 = 1: outlined');
    if w and $0020 = 0
      then FDetails.Add('Bit $0020 = 0: not shadowed')
      else FDetails.Add('Bit $0020 = 1: shadowed');
    if w and $0040 = 0
      then FDetails.Add('Bit $0040 = 0: not condensed')
      else FDetails.Add('Bit $0040 = 1: condensed');
    if w and $0080 = 0
      then FDetails.Add('Bit $0080 = 0: not extended')
      else FDetails.Add('Bit $0080 = 1: extended');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
    'Option flags');

  if FFormat <> sfExcel2 then begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(2), 'Color index');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w,w]),
      'Font weight (400=normal, 700=bold)');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Escapement:'#13);
      case w of
        0: FDetails.Add('0 = none');
        1: FDetails.Add('1 = superscript');
        2: FDetails.Add('2 = subscript');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
      'Escapement ($00=none, $01=superscript, $02=subscript)');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Underline type:'#13);
      case b of
        $00: FDetails.Add('$00 = no underline');
        $01: FDetails.Add('$01 = single underline');
        $02: FDetails.Add('$02 = double underline');
        $21: FDetails.Add('$21 = single accounting');
        $22: FDetails.Add('$22 = double accounting');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
      'Underline type ($00=none, $01=single, $02=double, ...)');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Font family:'#13);
      case b of
        $00: FDetails.Add('$00 = None (unknown or don''t care)');
        $01: FDetails.Add('$01 = Roman (variable width, serifed)');
        $02: FDetails.Add('$02 = Swiss (variable width, sans-serifed)');
        $03: FDetails.Add('$03 = Modern (fixed width, serifed or sans-serifed)');
        $04: FDetails.Add('$04 = Script (cursive)');
        $05: FDetails.Add('$05 = Decorative (specialised, for example Old English, Fraktur)');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%.2x', [b]),
      'Font family');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    case b of
      $00: s := 'ANSI Latin';
      $01: s := 'System default';
      $02: s := 'Symbol';
      $4D: s := 'Apple Roman';
      $80: s := 'ANSI Japanese Shift-JIS';
      $81: s := 'ANSI Korean (Hangul)';
      $82: s := 'ANSI Korean (Johab)';
      $86: s := 'ANSI Chinese Simplified GBK';
      $88: s := 'ANSI Chinese Traditional BIG5';
      $A1: s := 'ANSI Greek';
      $A2: s := 'ANSI Turkish';
      $A3: s := 'ANSI Vietnamese';
      $B1: s := 'ANSI Hebrew';
      $B2: s := 'ANSI Arabic';
      $BA: s := 'ANSI Baltic';
      $CC: s := 'ANSI Cyrillic';
      $DE: s := 'ANSI Thai';
      $EE: s := 'ANSI Latin II (Central European)';      // East Europe in MS docs!
      $FF: s := 'OEM Latin I';
      else s := '';
    end;
    if s <> '' then s := Format('$%.2x: %s', [b, s]) else s := Format('$%.2x', [b]);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
      'Character set');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, '', 'Not used');
  end;

  ExtractString(FBufferIndex, 1, FFormat=sfExcel8, s, numbytes);
  if FFormat = sfExcel8 then
    ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Font name (unicode string, 8-bit string length)')
  else
    ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Font name (byte string, 8-bit string length)');
end;


procedure TBIFFGrid.ShowFontColor;
var
  numBytes: Integer;
  w: Word;
  s: String;
begin
  RowCount := FixedRows + 1;
  NumBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  case w of
    $0000: s := 'EGA Black (rgb = $000000)';
    $0001: s := 'EGA White (rgb = $FFFFFF)';
    $0002: s := 'EGA Red (rgb = $0000FF)';
    $0003: s := 'EGA Green (rgb = $00FF00)';
    $0004: s := 'EGA Blue (rgb = $FF0000)';
    $0005: s := 'EGA Yellow (rgb = $00FFFF)';
    $0006: s := 'EGA Magenta (rgb = $FF00FF)';
    $0007: s := 'EGA Cyan (rgb = $FFFF00)';
    $7FFF: s := 'Automatic (system window text colour)';
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.04x)', [w, w]),
    Format('Font color index into preceding FONT record (%s)', [s]));
end;

procedure TBIFFGrid.ShowFooter;
var
  numbytes: Integer;
  s: String;
begin
  RowCount := FixedRows + 1;
  ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
    s, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
    'Page footer string' + IfThen(FFormat = sfExcel8,
    ' (Unicode string, 16-bit string length)',
    ' (byte string, 8-bit string length)'
  ));
end;


procedure TBIFFGrid.ShowFormat;
var
  numBytes: Integer;
  w: word;
  b: Byte;
  s: String;
begin
  RowCount := IfThen(FFormat = sfExcel2, FixedRows + 1, FixedRows + 2);
  if FFormat <> sfExcel2 then begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
      'FormatIndex used in other records');
  end;
  b := IfThen(FFormat=sfExcel8, 2, 1);
  ExtractString(FBufferIndex, b, (FFormat=sfExcel8), s, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
    Format('Number format string (%s string, %d-bit string length)', [GetStringType, b*8]));
end;


procedure TBIFFGrid.ShowFormatCount;
var
  numBytes: Integer;
  w: Word;
begin
  if FFormat = sfExcel2 then begin
    RowCount := 1 + FixedRows;
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Number of FORMAT records');
  end;
end;


procedure TBIFFGrid.ShowFormula;
var
  numBytes: Integer;
  b: Byte;
  w: Word;
  q: QWord;
  dbl: double absolute q;
  bytearr: array[0..7] of byte absolute q;
  wordarr: array[0..3] of word absolute q;
//  s: String;
  tokenBytes: Integer;
//  firstTokenBufIdx: Integer;
//  token: Byte;
  r,c, r2,c2: Integer;
begin
  BeginUpdate;
  RowCount := FixedRows + 1000;
  // Brute force simplification because of unknown row count at this point
  // Will be reduced at the end.

  // Offset 0 = Row, Offset 2 = Column
  ShowRowColData(FBufferIndex);
  // Offset 4 = Cell attributes (BIFF2) or XF ecord index (> BIFF2)
  if FFormat = sfExcel2 then begin
    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell protection and XF index:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
      case b and $40 of
        0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
        1: FDetails.Add('Bit 6 = 1: Cell is locked.');
      end;
      case b and $80 of
        0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
        1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
      'Cell protection and XF index');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Indexes to format and font records:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
      FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
      'Indexes of format and font records');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell style:'#13);
      case b and $07 of
        0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
        1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
        2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
        3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
        4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
      end;
      if b and $08 = 0
        then FDetails.Add('Bit 3 = 0: Cell has NO left border')
        else FDetails.Add('Bit 3 = 1: Cell has left black border');
      if b and $10 = 0
        then FDetails.Add('Bit 4 = 0: Cell has NO right border')
        else FDetails.Add('Bit 4 = 1: Cell has right black border');
      if b and $20 = 0
        then FDetails.Add('Bit 5 = 0: Cell has NO top border')
        else FDetails.Add('Bit 5 = 1: Cell has top black border');
      if b and $40 = 0
        then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
        else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
      if b and $80 = 0
        then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
        else FDetails.Add('Bit 7 = 1: Cell has shaded background');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
      'Cell style');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Index of XF record');
  end;

  // Offset 6: Result of formula
  numBytes := 8;
  Move(FBuffer[FBufferIndex], q, numBytes);
  if wordarr[3] <> $FFFF then begin
    if FCurrRow = Row then begin
      FDetails.Add('Formula result:'#13);
      FDetails.Add(Format('Bytes 0-7: $%.15x --> IEEE 764 floating-point value, 64-bit double precision'#13+
                          '           = %g', [q, dbl]));
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
      'Result of formula (IEEE 764 floating-point value, 64-bit double precision)');
  end else begin
    case bytearr[0] of
      0: begin     // String result
           if FCurrRow = Row then begin
             FDetails.Add('Formula result:'#13);
             FDetails.Add('Byte 0 = 0  --> Result is string, follows in STRING record');
             FDetails.Add('Byte 1-5: Not used');
             FDetails.Add('Byte 6&7: $FFFF --> no floating point number');
           end;
           ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
             'Result is a string, follows in STRING record');
         end;
      1: begin     // BOOL result
           if FCurrRow = Row then begin
             FDetails.Add('Formula result:'#13);
             FDetails.Add('Byte 0 = 1 --> Result is BOOLEAN');
             FDetails.Add('Byte 1: Not used');
             if bytearr[2] = 0
               then FDetails.Add('Byte 2 = 0 --> FALSE')
               else FDetails.Add('Byte 2 = 1 --> TRUE');
             FDetails.Add('Bytes 3-5: Not used');
             FDetails.Add('Bytes 6&7: $FFFF --> no floating point number');
           end;
           ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
             'Result is BOOLEAN');
         end;
      2: begin        // ERROR result
           if FCurrRow = Row then begin
             FDetails.Add('Formula result:'#13);
             FDetails.Add('Byte 0 = 2 --> Result is an ERROR value');
             FDetails.Add('Byte 1: Not used');
             case bytearr[2] of
               $00: FDetails.Add('Byte 2 = $00 --> #NULL! Intersection of two cell ranges is empty');
               $07: FDetails.Add('Byte 2 = $07 --> #DIV/0! Division by zero');
               $0F: FDetails.Add('Byte 2 = $0F --> #VALUE! Wrong type of operand');
               $17: FDetails.Add('Byte 2 = $17 --> #REF! Illegal or deleted cell reference');
               $1D: FDetails.Add('Byte 2 = $1D --> #NAME? Wrong function or range name');
               $24: FDetails.Add('Byte 2 = $24 --> #NUM! Value range overflow');
               $2A: FDetails.Add('Byte 2 = $2A --> #N/A Argument or function not available');
             end;
             FDetails.Add('Bytes 6&7: $FFFF --> no floating point number');
           end;
           ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
             'Result is an ERROR value');
         end;
      3: begin       // EMPTY cell
           if FCurrRow = Row then begin
             FDetails.Add('Formula result:'#13);
             FDetails.Add('Byte 0 = 3 --> Result is an empty cell, for example an empty string');
             FDetails.Add('Byte 1-5: Not used');
             FDetails.Add('Bytes 6&7: $FFFF --> no floating point number');
           end;
           ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.16x', [q]),
             'Result is an EMPTY cell (empty string)');
         end;
    end;
  end;

  // Option flags
  if FFormat = sfExcel2 then begin
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:'#13);
      case b of
        0: FDetails.Add('0 = Do not recalculate');
        1: FDetails.Add('1 = Recalculate always');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), 'Option flags');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:'#13);
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 = 0: Do not recalculate')
        else FDetails.Add('Bit $0001 = 1: Recalculate always');
      FDetails.Add('Bit $0002: Reserved - MUST be zero, MUST be ignored');
      if w and $0004 = 0
        then FDetails.Add('Bit $0004 = 0: Cell does NOT have a fill alignment or a center-across-selection alignment.')
        else FDetails.Add('Bit $0004 = 1: Cell has either a fill alignment or a center-across-selection alignment.');
      if w and $0008 = 0
        then FDetails.Add('Bit $0008 = 0: Formula is NOT part of a shared formula')
        else FDetails.Add('Bit $0008 = 1: Formula is part of a shared formula');
      FDetails.Add('Bit $0010: Reserved - MUST be zero, MUST be ignored');
      if w and $0020 = 0
        then FDetails.Add('Bit $0020 = 0: Formula is NOT excluded from formula error checking')
        else FDetails.Add('Bit $0020 = 1: Formula is excluded from formula error checking');
      FDetails.Add('Bits $FC00: Reserved - MUST be zero, MUST be ignored');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
      'Option flags');
  end;

  // Not used
  if (FFormat >= sfExcel5) then begin
    numBytes := 4;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used');
  end;

  // Size of Token array (in Bytes)
  if FFormat = sfExcel2 then begin
    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    tokenBytes := b;
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    tokenBytes := WordLEToN(w);
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(tokenBytes),
    'Size of formula data (in Bytes)');

  ShowFormulaTokens(tokenBytes);

  RowCount := FCurrRow;
  EndUpdate(true);
end;


procedure TBIFFGrid.ShowFormulaTokens(ATokenBytes: Integer);
var
  numBytes: Integer;
  b: Byte;
  w: Word;
  s: String;
  dbl: Double;
  firstTokenBufIndex: Integer;
  token: Byte;
  r, c: Word;
begin
  // Tokens and parameters
  firstTokenBufIndex := FBufferIndex;
  while FBufferIndex < firstTokenBufIndex + ATokenBytes do begin
    token := FBuffer[FBufferIndex];
    numBytes := 1;
    case token of
      $01: begin
             ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]),
               'Token for "Cell is part of shared formula"');
             numbytes := 2;
             Move(FBuffer[FBufferIndex], w, numBytes);
             ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
               'Index to row of first FORMULA record in the formula range');
             if FFormat = sfExcel2 then begin
               numbytes := 1;
               b := FBuffer[FBufferIndex];
               ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
                 'Index to column of first FORMULA record in the formula range');
             end else begin
               numbytes := 2;
               Move(FBuffer[FBufferIndex], w, numbytes);
               ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
                 'Index to column of first FORMULA record in the formula range');
             end;
           end;
      $02: begin
             ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]),
               'Token for "Cell is part of a multiple operations table"');
             numbytes := 2;
             Move(FBuffer[FBufferIndex], w, numBytes);
             ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
               'Index to first row of the table range');
             if FFormat = sfExcel2 then begin
               numbytes := 1;
               b := FBuffer[FBufferIndex];
               ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
                 'Index to first column of the table range');
             end else begin
               numbytes := 2;
               Move(FBuffer[FBufferIndex], w, numbytes);
               ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
                 'Index to first column of the table range');
             end;
           end;
      $03: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "+" (add)');
      $04: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "-" (subtract)');
      $05: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "*" (multiply)');
      $06: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "/" (divide)');
      $07: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "^" (power)');
      $08: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "&" (concat)');
      $09: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "<" (less than)');
      $0A: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "<=" (less equal)');
      $0B: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "=" (equal)');
      $0C: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token ">=" (greater equal)');
      $0D: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token ">" (greater than)');
      $0E: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "<>" (not equal)');
      $0F: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token " " (intersect)');
      $10: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "list character"');
      $11: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token ":" (range)');
      $12: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "+" (unary plus)');
      $13: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "-" (unary minus)');
      $14: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "%" (percent)');
      $15: ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [token]),
             'Token "()" (operator in parenthesis)');
      $16: ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
             'Token "missing argument"');
      $17: begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tSTR (Label)');
             ExtractString(FBufferIndex, 1, (FFormat = sfExcel8), s, numBytes);
             ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'String value');
           end;
      $1C: begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tERR (Error)');
             numBytes := 1;
             b := FBuffer[FBufferIndex];
             if FCurrRow = Row then begin
               FDetails.Add('Error code:'#13);
               FDetails.Add(Format('Code $%.2x --> "%s"', [b, GetErrorValueStr(TsErrorValue(b))]));
             end;
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Error code');
           end;
      $1D: begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tBOOL');
             numBytes := 1;
             b := FBuffer[FBufferIndex];
             ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
               '0=FALSE, 1=TRUE');
           end;
      $1E: begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tINT (Integer)');
             numBytes := 2;
             Move(FBuffer[FBufferIndex], w, numBytes);
             ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
               'Integer value');
           end;
      $1F: begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tNUM (Number)');
             numBytes := 8;
             Move(FBuffer[FBufferIndex], dbl, numBytes);
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%g', [dbl]), //FloatToStr(dbl),
               'IEEE 754 floating-point value');
           end;
      $20, $40, $60:
           begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tARRAY');
             if FFormat = sfExcel2 then numBytes := 6 else numBytes := 7;
             ShowInRow(FCurrRow, FBufferIndex, numbytes, '', '(not used)');
           end;
      $21, $41, $61:
           begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tFUNC (Function with fixed argument count)');
             if FFormat = sfExcel2 then begin
               numBytes := 1;
               b := FBuffer[FBufferIndex];
               s := Format('Index of function (%s)', [SheetFuncName(b)]);
               ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), s);
             end else begin
               numBytes := 2;
               Move(FBuffer[FBufferIndex], w, numBytes);
               w := WordLEToN(w);
               s := Format('Index of function (%s)', [SheetFuncName(w)]);
               ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), s);
             end;
           end;
      $22, $42, $62:
           begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tFUNCVAR (Function with variable argument count)');
             numBytes := 1;
             b := FBuffer[FBufferIndex];
             ShowInRow(FCurrRow, FBufferIndex, numBytes,  IntToStr(b),
               'Number of arguments');
             if FFormat = sfExcel2 then begin
               numBytes := 1;
               s := Format('Index of built-in function (%s)', [SheetFuncName(b)]);
               ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), s);
             end else begin
               numBytes := 2;
               Move(FBuffer[FBufferIndex], w, numbytes);
               w := WordLEToN(w);
               s := Format('Index of built-in function (%s)', [SheetFuncName(w)]);
               ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), s);
             end;
           end;
      $23, $43, $63:
           begin
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               'Token tNAME');
             numBytes := 2;
             Move(FBuffer[FBufferIndex], w, numBytes);
             case FFormat of
               sfExcel2: s := 'DEFINEDNAME or EXTERNALNAME record';
               sfExcel5: s := 'DEFINEDNAME record in Global Link Table';
               sfExcel8: s := 'DEFINEDNAME record in Link Table';
             end;
             ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
               '1-based index to '+s);
             case FFormat of
               sfExcel2: numBytes := 5;
               sfExcel5: numBytes := 12;
               sfExcel8: numBytes := 2;
             end;
             ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used)');
           end;
      $24, $44, $64:
           begin
             case token of
               $24: s := 'reference';
               $44: s := 'value';
               $64: s := 'array';
             end;
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               Format('Token tREF (Cell %s)', [s]));
             ShowCellAddress;
           end;
      $25, $45, $65:
           begin
             case token of
               $25: s := 'reference';
               $45: s := 'value';
               $65: s := 'array';
             end;
             ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
               Format('Token tAREA (Cell range %s)', [s]));
             ShowCellAddressRange;
           end;
      $2C, $4C, $6C:
          begin
            case token of
              $2C: s := 'reference';
              $4C: s := 'value';
              $6C: s := 'array';
            end;
            ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
              Format('Token tREFN (Relative reference to cell %s in same sheet)', [s]));

            // Encoded relative cell address
            if FFormat = sfExcel8 then
            begin
              numBytes := 2;
              Move(FBuffer[FBufferIndex], w, numBytes);    // row
              r := WordLEToN(w);
              Move(FBuffer[FBufferIndex+2], w, numBytes);  // column with flags
              c := WordLEToN(w);

              { Note: The bitmask assignment to relative column/row is reversed in relation
                to OpenOffice documentation in order to match with Excel files. }
              if Row = FCurrRow then begin
                FDetails.Add('Encoded cell address (row):'#13);
                if c and $8000 = 0 then
                begin
                  FDetails.Add('Row index is ABSOLUTE (see Encoded column index)');
                  FDetails.Add('Absolute row index: ' + IntToStr(r));
                end else
                begin
                  FDetails.Add('Row index is RELATIVE (see Encoded column index)');
                  FDetails.Add('Relative row index: ' + IntToStr(Smallint(r)));
                end;
              end;
              ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [r, r]),
                'Encoded row index');

              // Bit mask $4000 --> column
              // Bit mask $8000 --> row
              if Row = FCurrRow then
              begin
                FDetails.Add('Encoded cell address (column):'#13);
                if c and $4000 = 0
                  then FDetails.Add('Bit 14=0: Column index is ABSOLUTE')
                  else FDetails.Add('Bit 14=1: Column index is RELATIVE');
                if c and $8000 = 0
                  then FDetails.Add('Bit 15=0: Row index is ABSOLUTE')
                  else FDetails.Add('Bit 15=1: Row index is RELATIVE');
                FDetails.Add('');
                if c and $4000 = 0
                  then FDetails.Add('Absolute column index: ' + IntToStr(Lo(c)))
                  else FDetails.Add('Relative column index: ' + IntToStr(ShortInt(Lo(c))));
              end;

              ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [c, c]),
                'Encoded column index');
            end
            else
            // Excel5 (Excel2 does not support shared formulas)
            begin
              numBytes := 2;
              Move(FBuffer[FBufferIndex], w, numBytes);
              w := WordLEToN(w);
              r := w and $3FFF;
              b := FBuffer[FBufferIndex+2];
              c := b;

              // Bit mask $4000 --> column
              // Bit mask $8000 --> row
              if Row = FCurrRow then begin
                FDetails.Add('Encoded cell address (row):'#13);
                if w and $4000 = 0
                  then FDetails.Add('Bit 14=0: Column index is ABSOLUTE')
                  else FDetails.Add('Bit 14=0: Column index is RELATIVE');
                if w and $8000 = 0
                  then FDetails.Add('Bit 15=0: Row index is ABSOLUTE')
                  else FDetails.Add('Bit 15=1: Row index is RELATIVE');
                FDetails.Add('');
                if w and $8000 = 0
                  then FDetails.Add('Absolute row index: ' + IntToStr(r))
                  else FDetails.Add('Relative row index: ' + IntToStr(Smallint(r)));
              end;
              ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
                'Encoded row index');

              if Row = FCurrRow then
              begin
                FDetails.Add('Encoded cell address (column):'#13);
                if w and $4000 = 0 then begin
                  FDetails.Add('Column index is ABSOLUTE (see Encoded row index)');
                  FDetails.Add('Absolute column index: ' + IntToStr(c));
                end else begin
                  FDetails.Add('Column index is RELATIVE (see Encoded row index)');
                  FDetails.Add('Relative column index: ' + IntToStr(ShortInt(c)));
                end;
              end;
              numBytes := 1;
              ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
                'Encoded column index');
            end;
          end;

    else
          ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [token]),
            '(unknown token)');

    end;  // case
  end;  // while

end;

procedure TBIFFGrid.ShowGCW;
var
  numBytes: Integer;
  b: Byte;
  w: Word;
  i,j: Integer;
  bit: Byte;
begin
  RowCount := FixedRows + 33;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Size of Global Column Width bit field (1 bit per column), must be 32');

  numBytes := 1;
  for i:= 0 to w-1 do begin
    b := FBuffer[FBufferIndex];
    if FCurrRow = Row then begin
      FDetails.Add(Format('GCW (Global column width) record, byte #%d:'#13, [i]));
      bit := 1;
      for j:=0 to 7 do begin
        if b and bit = 0
          then FDetails.Add(Format('Bit $%.2x=0: Column %d uses width of COLWIDTH record.', [bit, j+i*8]))
          else FDetails.Add(Format('Bit $%.2x=1: Column %d uses width of STANDARDWIDTH record '+
                                   '(or, if not available, DEFCOLWIDTH record)', [bit, j+i*8]));
        bit := bit * 2;
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
      Format('Widths of columns %d-%d', [i*8, i*8+7]));
  end;
end;


procedure TBIFFGrid.ShowHeader;
var
  numbytes: Integer;
  s: String;
begin
  RowCount := FixedRows + 1;
  ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
    s, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, s,
    'Page header string' + IfThen(FFormat = sfExcel8,
    ' (Unicode string, 16-bit string length)',
    ' (byte string, 8-bit string length)'
  ));
end;


procedure TBIFFGrid.ShowHideObj;
var
  numBytes: word;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Viewing mode for objects:'#13);
    case w of
      0: FDetails.Add('0 = Show all objects');
      1: FDetails.Add('1 = Show placeholders');
      2: FDetails.Add('2 = Do not show objects');
    end;
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Viewing mode for objects');
end;


procedure TBIFFGrid.ShowInRow(var ARow: Integer; var AOffs: LongWord;
  ASize: Word; AValue,ADescr: String);
begin
  Cells[0, ARow] := IntToStr(AOffs);
  Cells[1, ARow] := IntToStr(ASize);
  Cells[2, ARow] := AValue;
  Cells[3, ARow] := ADescr;
  inc(ARow);
  inc(AOffs, ASize);
end;


procedure TBIFFGrid.ShowInteger;
var
  numBytes: Integer;
  w: Word;
  b: Byte;
begin
  // BIFF2 only
  if (FFormat <> sfExcel2) then
    exit;

  RowCount := FixedRows + 5;
  ShowRowColData(FBufferIndex);

  numBytes := 1;
  b := FBuffer[FBufferIndex];
  if Row = FCurrRow then begin
    FDetails.Add('Cell protection and XF index:'#13);
    FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
    case b and $40 of
      0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
      1: FDetails.Add('Bit 6 = 1: Cell is locked.');
    end;
    case b and $80 of
      0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
      1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
    end;
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
    'Cell protection and XF index');

  b := FBuffer[FBufferIndex];
  if Row = FCurrRow then begin
    FDetails.Add('Indexes to format and font records:'#13);
    FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
    FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
    'Indexes of format and font records');

  b := FBuffer[FBufferIndex];
  if Row = FCurrRow then begin
    FDetails.Add('Cell style:'#13);
    case b and $07 of
      0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
      1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
      2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
      3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
      4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
    end;
    if b and $08 = 0
      then FDetails.Add('Bit 3 = 0: Cell has NO left border')
      else FDetails.Add('Bit 3 = 1: Cell has left black border');
    if b and $10 = 0
      then FDetails.Add('Bit 4 = 0: Cell has NO right border')
      else FDetails.Add('Bit 4 = 1: Cell has right black border');
    if b and $20 = 0
      then FDetails.Add('Bit 5 = 0: Cell has NO top border')
      else FDetails.Add('Bit 5 = 1: Cell has top black border');
    if b and $40 = 0
      then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
      else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
    if b and $80 = 0
      then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
      else FDetails.Add('Bit 7 = 1: Cell has shaded background');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
    'Cell style');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Unsigned 16-bit integer cell value');
end;


procedure TBIFFGrid.ShowInterfaceEnd;
begin
  RowCount := FixedRows + 1;
  ShowInRow(FCurrRow, FBufferIndex, 0, '', 'End of Globals Substream');
end;


procedure TBIFFGrid.ShowInterfaceHdr;
var
  numbytes: Integer;
  w: Word;
begin
  if FFormat < sfExcel8 then begin
    RowCount := FixedRows;
    exit;
  end;

  RowCount := FixedRows + 1;
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Code page of user interface:'#13);
    FDetails.Add(Format('$%.4x = %s', [w, CodePageName(w)]));
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
    'Begin of Globals Substream, code page of user interface');
end;


procedure TBIFFGrid.ShowIteration;
var
  numBytes: Integer;
  w: word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Iterations:'#13);
    case w of
      0: FDetails.Add('0 = Iterations off');
      1: FDetails.Add('1 = Iterations on');
    end;
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Iterations on/off');
end;


procedure TBIFFGrid.ShowIXFE;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to XF record');
end;

procedure TBIFFGrid.ShowLabelCell;
var
  numBytes: Integer;
  b: Byte;
  w: Word;
  s: String;
begin
  RowCount := IfThen(FFormat = sfExcel2, FixedRows + 6, FixedRows + 4);
  ShowRowColData(FBufferIndex);
  if (FFormat = sfExcel2) then begin
    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell protection and XF index:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
      case b and $40 of
        0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
        1: FDetails.Add('Bit 6 = 1: Cell is locked.');
      end;
      case b and $80 of
        0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
        1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
      'Cell protection and XF index');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Indexes to format and font records:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
      FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
      'Indexes of format and font records');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell style:'#13);
      case b and $07 of
        0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
        1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
        2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
        3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
        4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
      end;
      if b and $08 = 0
        then FDetails.Add('Bit 3 = 0: Cell has NO left border')
        else FDetails.Add('Bit 3 = 1: Cell has left black border');
      if b and $10 = 0
        then FDetails.Add('Bit 4 = 0: Cell has NO right border')
        else FDetails.Add('Bit 4 = 1: Cell has right black border');
      if b and $20 = 0
        then FDetails.Add('Bit 5 = 0: Cell has NO top border')
        else FDetails.Add('Bit 5 = 1: Cell has top black border');
      if b and $40 = 0
        then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
        else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
      if b and $80 = 0
        then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
        else FDetails.Add('Bit 7 = 1: Cell has shaded background');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
      'Cell style');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Index of XF record');
  end;

  b := IfThen(FFormat=sfExcel2, 1, 2);
  ExtractString(FBufferIndex, b, (FFormat = sfExcel8), s, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, s,
    Format('%s string, %d-bit string length', [GetStringType, b*8]));
end;


procedure TBIFFGrid.ShowLabelSSTCell;
var
  numBytes: Integer;
  w: Word;
  dw: DWord;
begin
  RowCount := FixedRows + 4;
  ShowRowColData(FBufferIndex);

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
    'Index of XF record');

  numBytes := 4;
  Move(FBuffer[FBufferIndex], dw, numBytes);
  dw := DWordLEToN(dw);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(dw),
    'Index into SST record (shared string table)');
end;


procedure TBIFFGrid.ShowLeftMargin;
var
  numBytes: Integer;
  dbl: Double;
begin
  RowCount := FixedRows + 1;
  numBytes := 8;
  Move(FBuffer[FBufferIndex], dbl, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
    'Left page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;


procedure TBIFFGrid.ShowMergedCells;
var
  w: Word;
  numBytes: Integer;
  i, n: Integer;
begin
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  n := WordLEToN(w);  // count of merged ranges in this record

  RowCount := FixedRows + 1 + n*4;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(n),
    'Count of merged ranges in this record');

  for i:=1 to n do begin
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
      Format('Merged range #%d: First row = %d', [i, w]));
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
      Format('Merged range #%d: Last row = %d', [i, w]));
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
      Format('Merged range #%d: First column = %d', [i, w]));
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
      Format('Merged range #%d: Last column = %d', [i, w]));
  end;
end;


procedure TBIFFGrid.ShowMMS;
var
  w: Word;
  numbytes: Integer;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w), 'Reserved, MUST be ignored');
end;


procedure TBIFFGrid.ShowMulBlank;
var
  w: Word;
  numbytes: Integer;
  i, nc: Integer;
begin
  nc := (Length(FBuffer) - 6) div 2;
  RowCount := FixedRows + 3 + nc;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Index to row');

  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Index to first column');

  for i:=0 to nc-1 do begin
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      Format('Index to XF record #%d', [i]));
  end;

  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Index to last column');
end;


procedure TBIFFGrid.ShowMulRK;
var
  w: Word;
  numBytes: Integer;
  i, nc: Integer;
  dw: DWord;
  encint: DWord;
  encdbl: QWord;
  dbl: Double absolute encdbl;
  s: String;
begin
  nc := (Length(FBuffer) - 6) div 6;
  RowCount := FixedRows + 3 + nc*2;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Index to row');

  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Index to first column');

  for i:=0 to nc-1 do begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      Format('Index to XF record #%d', [i]));

    numBytes := 4;
    Move(FBuffer[FBufferIndex], dw, numbytes);
    dw := DWordLEToN(dw);

    if Row = FCurrRow then begin
      FDetails.Add('RK Value:'#13);
      if dw and $00000001 = 0
        then FDetails.Add('Bit 0 = 0: Value not changed')
        else FDetails.Add('Bit 0 = 1: Encoded value is multiplied by 100.');
      if dw and $00000002 = 0
        then FDetails.Add('Bit 1 = 0: Floating point value')
        else FDetails.Add('Bit 1 = 1: Signed integer value');
      if dw and $00000002 = 0 then begin
        encdbl := (QWord(dw) and QWord($FFFFFFFFFFFFFFFC)) shl 32;
        if dw and $00000001 = 1 then
          s := Format('%.2f', [dbl*0.01])
        else
          s := Format('%.0f', [dbl]);
      end
      else begin
        s := Format('$%.16x', [-59000000]);
        encint := ((dw and DWord($FFFFFFFC)) shr 2) or (dw and DWord($C0000000));
          // "arithmetic shift" = replace left-most bits by original bits
        if dw and $00000001 = 1 then
          s := FloatToStr(encint*0.01)
        else
          s := IntToStr(encint);
      end;
      FDetails.Add('Bits 31-2: Encoded value ' + s);
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes,
      Format('$%.8x', [dw]),
      Format('RK value #%d', [i])
    );
  end;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Index to last column');
end;

procedure TBIFFGrid.ShowNote;
var
  numBytes: Integer;
  w: Word = 0;
  s: String;
begin
  RowCount := IfThen(FFormat = sfExcel8, 6, 4);

  // Offset 0: Row and Col index
  ShowRowColData(FBufferIndex);

  if FFormat = sfExcel8 then begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Comment flags:'#13);
      if (w and $0002 <> 0)
        then FDetails.Add('Bit 1=1: Comment is shown at all times')
        else FDetails.Add('Bit 1=0: Comment is not shown at all tiems');
      if (w and $0080 <> 0)
        then FDetails.Add('Bit 7=1: Row with comment is hidden')
        else FDetails.Add('Bit 7=0: Row with comment is visible');
      if (w and $0100 <> 0)
        then FDetails.Add('Bit 8=1: Column with comment is hidden')
        else FDetails.Add('Bit 8=0: Column with comment is visible');
      FDetails.Add('All other bits are reserved and must be ignored.');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Flags');

    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Object ID');

    ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8,
      s, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Author');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
      'Total length of comment');

    numBytes := Min(Length(FBuffer) - FBufferIndex, 2048);
    SetLength(s, numBytes);
    Move(FBuffer[FBufferIndex], s[1], numBytes);
    SetLength(s, Length(s));
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Comment text');
  end;
end;


procedure TBIFFGrid.ShowNumberCell;
var
  numBytes: Integer;
  b: Byte = 0;
  w: Word = 0;
  dbl: Double;
begin
  RowCount := IfThen(FFormat = sfExcel2, FixedRows + 6, FixedRows + 4);
  // Offset 0: Row & Offsset 2: Column
  ShowRowColData(FBufferIndex);
  // Offset 4: Cell attributes (BIFF2) or XF ecord index (> BIFF2)
  if FFormat = sfExcel2 then begin
    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell protection and XF index:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: XF Index', [b and $3F]));
      case b and $40 of
        0: FDetails.Add('Bit 6 = 0: Cell is NOT locked.');
        1: FDetails.Add('Bit 6 = 1: Cell is locked.');
      end;
      case b and $80 of
        0: FDetails.Add('Bit 7 = 0: Formula is NOT hidden.');
        1: FDetails.Add('Bit 7 = 1: Formula is hidden.');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
      'Cell protection and XF index');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Indexes to format and font records:'#13);
      FDetails.Add(Format('Bits 5-0 = %d: Index to FORMAT record', [b and $3f]));
      FDetails.Add(Format('Bits 7-6 = %d: Index to FONT record', [(b and $C0) shr 6]));
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b,b]),
      'Indexes of format and font records');

    numBytes := 1;
    Move(FBuffer[FBufferIndex], b, numBytes);
    if Row = FCurrRow then begin
      FDetails.Add('Cell style:'#13);
      case b and $07 of
        0: FDetails.Add('Bits 2-0 = 0: Horizontal alignment is GENERAL');
        1: FDetails.Add('Bits 2-0 = 1: Horizontal alignment is LEFT');
        2: FDetails.Add('Bits 2-0 = 2: Horizontal alignment is CENTERED');
        3: FDetails.Add('Bits 2-0 = 3: Horizontal alignment is RIGHT');
        4: FDetails.Add('Bits 2-0 = 4: Horizontal alignment is FILLED');
      end;
      if b and $08 = 0
        then FDetails.Add('Bit 3 = 0: Cell has NO left border')
        else FDetails.Add('Bit 3 = 1: Cell has left black border');
      if b and $10 = 0
        then FDetails.Add('Bit 4 = 0: Cell has NO right border')
        else FDetails.Add('Bit 4 = 1: Cell has right black border');
      if b and $20 = 0
        then FDetails.Add('Bit 5 = 0: Cell has NO top border')
        else FDetails.Add('Bit 5 = 1: Cell has top black border');
      if b and $40 = 0
        then FDetails.Add('Bit 6 = 0: Cell has NO bottom border')
        else FDetails.Add('Bit 6 = 1: Cell has bottom black border');
      if b and $80 = 0
        then FDetails.Add('Bit 7 = 0: Cell has NO shaded background')
        else FDetails.Add('Bit 7 = 1: Cell has shaded background');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.2x)', [b,b]),
      'Cell style');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrROw, FBufferIndex, numBytes, Format('%d ($%.4x)', [w, w]),
      'Index of XF record');
  end;
  // Offset  6: Double value
  numBytes := 8;
  Move(FBuffer[FBufferIndex], dbl, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%g', [dbl]), //FloatToStr(dbl),
    'IEEE 764 floating-point value');
end;


procedure TBIFFGrid.ShowObj;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 5;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [WordLEToN(w)]),
    'ft (must be $15)');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [WordLEToN(w)]),
    'cb (must be $12)');

  numBytes := 2;
  w := WordLEToN(w);
  Move(FBuffer[FBufferIndex], w, numBytes);
  if Row = FCurrRow then begin
    FDetails.Add('Object type:'#13);
    case w of
      $00: FDetails.Add('$00 = Group');
      $01: FDetails.Add('$01 = Line');
      $02: FDetails.Add('$02 = Rectangle');
      $03: FDetails.Add('$03 = Oval');
      $04: FDetails.Add('$04 = Arc');
      $05: FDetails.Add('$05 = Chart');
      $06: FDetails.Add('$06 = Text');
      $07: FDetails.Add('$07 = Button');
      $08: FDetails.Add('$08 = Picture');
      $09: FDetails.Add('$09 = Polygon');
      $0B: FDetails.Add('$0B = Checkbox');
      $0C: FDetails.Add('$0C = Radio button');
      $0D: FDetails.Add('$0D = Edit box');
      $0E: FDetails.Add('$0E = Label');
      $0F: FDetails.Add('$0F = Dialog box');
      $10: FDetails.Add('$10 = Spin control');
      $11: FDetails.Add('$11 = Scrollbar');
      $12: FDetails.Add('$12 = List');
      $13: FDetails.Add('$13 = Group box');
      $14: FDetails.ADd('$14 = Dropdown list');
      $19: FDetails.Add('$19 = Note');
      $1E: FDetails.Add('$1E = OfficeArt object');
      else FDetails.Add(IntToStr(w) + ' = (unknown object)');
    end;
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]),
    'Object type (ot)');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [WordLEToN(w)]),
    'Object ID');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Object flags:'#13);
    if w and $0001 <> 0
      then FDetails.Add('Bit $0001 = 1: Object is locked')
      else FDetails.Add('Bit $0001 = 0: Object is NOT locked');
    if w and $0002 <> 0
      then FDetails.Add('Bit $0002 = 1: Reserved - must be zero!!!d')
      else FDetails.Add('Bit $0002 = 0: Reserved - must be zero');
    if w and $0004 <> 0
      then FDetails.Add('Bit $0004 = 1: Application is expected to choose object size')
      else FDetails.Add('Bit $0004 = 0: Application is NOT expected to choose object size');
    if w and $0008 <> 0
      then FDetails.Add('Bit $0008 = 1: Is a chart that is expected to be published when sheet is published')
      else FDetails.Add('Bit $0008 = 0: Is NOT a chart that is expected to be published when sheet is published');
    if w and $0010 <> 0
      then FDetails.Add('Bit $0010 = 1: Image of this object is intended to be included when printing')
      else FDetails.Add('Bit $0010 = 0: Image of this object is NOT intended to be included when printing');
    FDetails.Add('Bit $0020    : unused');
    FDetails.Add('Bit $0040    : unused');
    if w and $0080 <> 0
      then FDetails.Add('Bit $0080 = 1: Object is disabled')
      else FDetails.ADd('Bit $0080 = 0: Object is NOT disabled');
    if w and $0100 <> 0
      then FDetails.Add('Bit $0100 = 1: is an auxiliary object that can only be automatically inserted by the application')
      else FDetails.Add('Bit $0100 = 0: is NOT an auxiliary object that can only be automatically inserted by the application');
    if w and $0200 <> 0
      then FDetails.Add('Bit $0200 = 1: is expected to be updated on load to reflect the values in the range associated with the object')
      else FDetails.Add('Bit $0200 = 0: is NOT expected to be updated on load to reflect the values in the range associated with the object');
    FDetails.Add('Bit $0400    : unused');
    FDetails.Add('Bit $0800    : unused');
    if w and $1000 <> 0
      then FDetails.Add('Bit $1000 = 1: is expected to be updated whenever the value of a cell in the range associated with the object changes')
      else FDetails.Add('Bit $1000 = 0: is NOT expected to be updated whenever the value of a cell in the range associated with the object changes');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.04x', [w]),
    'Flags');
end;


procedure TBIFFGrid.ShowPageSetup;
var
  numBytes: Integer;
  w: Word;
  s: String;
  dbl: Double;
begin
  if FFormat in [sfExcel5, sfExcel8] then RowCount := FixedRows + 11
    else RowCount := FixedRows + 6;

  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Paper size:'#13);
    FDetails.Add(Format('%d - %s', [w, PaperSizeName(w)]));
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Paper size');

  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Scaling factor in percent');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLETON(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Start page number');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLETON(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Fit worksheet width to this number of pages (0 = use as many as needed)');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLETON(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Fit worksheet height to this number of pages (0 = use as many as needed)');

  { Excel4 not supported in fpspreadsheet }
  {
  if FFormat = sfExcel4 then begin
    Move(FBuffer[FBufferIndex], w, numbytes);
    w := WordLETON(w);
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:'#13);
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 = 0: Print pages in columns')
        else FDetails.Add('Bit $0001 = 1: Print pages in rows');
      if w and $0002 = 0
        then FDetails.add('Bit $0002 = 0: Landscape')
        else FDetails.Add('Bit $0002 = 1: Portrait');
      if w and $0004 = 0
        then FDetails.Add('Bit $0004 = 0: Paper size, scaling factor, and paper orientation ' +
                                          '(portrait/landscape) ARE initialised')
        else FDetails.Add('Bit $0004 = 1: Paper size, scaling factor, and paper orientation ' +
                                          '(portrait/landscape) are NOT initialised');
      if w and $0008 = 0
        then FDetails.Add('Bit $0008 = 0: Print colored')
        else FDetails.add('Bit $0008 = 1: Print black and white');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]),
      'Option flags');
  end else }
  if (FFormat in [sfExcel5, sfExcel8]) then begin
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:'#13);
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 = 0: Print pages in columns')
        else FDetails.Add('Bit $0001 = 1: Print pages in rows');
      if w and $0002 = 0
        then FDetails.add('Bit $0002 = 0: Landscape')
        else FDetails.Add('Bit $0002 = 1: Portrait');
      if w and $0004 = 0
        then FDetails.Add('Bit $0004 = 0: Paper size, scaling factor, and paper orientation ' +
                                          '(portrait/landscape) ARE initialised')
        else FDetails.Add('Bit $0004 = 1: Paper size, scaling factor, and paper orientation ' +
                                          '(portrait/landscape) are NOT initialised');
      if w and $0008 = 0
        then FDetails.Add('Bit $0008 = 0: Print colored')
        else FDetails.add('Bit $0008 = 1: Print black and white');
      if w and $0010 = 0
        then FDetails.Add('Bit $0010 = 0: Default print quality')
        else FDetails.Add('Bit $0010 = 1: Draft quality');
      if w and $0020 = 0
        then FDetails.Add('Bit $0020 = 0: Do NOT print cell notes')
        else FDetails.Add('Bit $0020 = 0: Print cell notes');
      if w and $0040 = 0
        then FDetails.Add('Bit $0040 = 0: Use paper orientation (portrait/landscape) flag abov')
        else FDetails.Add('Bit $0040 = 1: Use default paper orientation (landscape for chart sheets, portrait otherwise)');
      if w and $0080 = 0
        then FDetails.Add('Bit $0080 = 0: Automatic page numbers')
        else FDetails.Add('Bit $0080 = 1: Use start page number above');
      if w and $0200 = 0
        then FDetails.Add('Bit $0200 = 0: Print notes as displayed')
        else FDetails.Add('Bit $0200 = 1: Print notes at end of sheet');
      case (w and $0C00) shr 10 of
        0: FDetails.Add('Bit $0C00 = 0: Print errors as displayed');
        1: FDetails.add('Bit $0C00 = 1: Do not print errors');
        2: FDetails.Add('Bit $0C00 = 2: Print errors as "--"');
        3: FDetails.Add('Bit $0C00 = 4: Print errors as "#N/A"');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]),
      'Option flags');

    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Print resolution in dpi');

    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Vertical print resolution in dpi');

    numBytes := 8;
    Move(FBuffer[FBufferIndex], dbl, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), 'Header margin');

    Move(FBuffer[FBufferIndex], dbl, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl), 'Footer margin');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Number of copies to print');
  end;

end;

procedure TBIFFGrid.ShowPalette;
var
  numBytes: Integer;
  w: Word;
  dw: DWord;
  npal: Integer;
  i: Integer;
  s: String;
begin
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  npal := WordLEToN(w);

  RowCount := FixedRows + 1 + npal;

  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(npal),
    'Number of palette colors');

  for i := 0 to npal-1 do begin
    numbytes := 4;
    Move(FBuffer[FBufferIndex], dw, numBytes);
    dw := DWordLEToN(dw);
    s := Format('Palette color, index #%d ($%.2x)',[i, i]);
    case i of
      $00..$07: ;
      $08..$3F: s := s + ', user-defined palette';
      $40     : s := s + ', system window text color for border lines';
      $41     : s := s + ', system window background color for pattern background';
      $43     : s := s + ', system face color (dialogue background color)';
      $4D     : s := s + ', system window text colour for chart border lines';
      $4E     : s := s + ', system window background color for chart areas';
      $4F     : s := s + ', automatic color for chart border lines (seems to be always Black)';
      $50     : s := s + ', system tool tip background color (used in note objects)';
      $51     : s := s + ', system tool tip text color (used in note objects)';
      $7FFF   : s := s + ', system window text color for fonts';
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]), s);
  end;
end;


procedure TBIFFGrid.ShowPane;
var
  numBytes: Integer;
  w: Word;
  b: Byte;
begin
  RowCount := FixedRows + IfThen(FFormat < sfExcel5, 5, 6);

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Position of vertical split (twips or columns (if frozen))');

  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Position of horizontal split (twips or rows (if frozen))');

  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to first visible row in bottom pane(s)');

  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to first visible column in right pane(s)');

  numBytes := 1;
  b := FBuffer[FBufferIndex];
  ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b),
    'Identifier of pane with active cell cursor');

  if FFormat >= sfExcel5 then begin
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBUfferIndex, numBytes, IntToStr(b), 'not used');
  end;
end;

procedure TBIFFGrid.ShowPassword;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Password verifier for sheet or workbook:'#13);
    if w = 0
      then FDetails.Add('0 = No password')
      else FDetails.Add(Format('$%.4x = Password verifier', [w]));
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Password verifier for sheet or workbook');
end;


procedure TBIFFGrid.ShowPrecision;
var
  numbytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Precision-as-displayed mode:'#13);
    if w = 0
      then FDetails.Add('0 = Precision-as-displayed mode selected')
      else FDetails.Add('1 = Precision-as-displayed mode NOT selected');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Precision-as-displayed mode');
end;


procedure TBIFFGrid.ShowPrintGridLines;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Print sheet grid lines:'#13);
    if w = 0
      then FDetails.Add('0 = Do not print sheet grid lines')
      else FDetails.Add('1 = Print sheet grid lines');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Print sheet grid lines');
end;


procedure TBIFFGrid.ShowPrintHeaders;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Print row/column headers');
    FDetails.Add('(the area with row numbers and column letters):'#13);
    if w = 0
      then FDetails.Add('0 = Do not print row/column headers')
      else FDetails.Add('1 = Print row/column headers');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Print row/column headers');
end;


procedure TBIFFGrid.ShowProt4Rev;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Removal of the shared workbook''s revision logs:'#13);
    if w = 0
      then FDetails.Add('0 = Removal of the shared workbook''s revision logs is allowed.')
      else FDetails.Add('1 = Removal of the shared workbook''s revision logs is NOT allowed.');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Removal of the shared workbook''s revision logs');
end;


procedure TBIFFGrid.ShowProt4RevPass;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Password verifier needed to change the PROT4REV record:'#13);
    if w = 0
      then FDetails.Add('0 = No password.')
      else FDetails.Add(Format('$%.04x = Password verifier.', [w]));
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.4x)', [w,w]),
    'Password verifier needed to change the PROT4REV record');
end;


procedure TBIFFGrid.ShowProtect;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Protection state of the workbook:'#13);
    if w = 0
      then FDetails.Add('0 = Workbook is NOT protected.')
      else FDetails.Add('1 = Workbook is protected.');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
    'Protection state of the workbook');
end;


procedure TBIFFGrid.ShowRecalc;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('"Recalculate before save" option:'#13);
    if w = 0
      then FDetails.Add('0 = Do not recalculate')
      else FDetails.Add('1 = Recalculate before saving the document');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w),
    'Recalculate before saving');
end;


procedure TBIFFGrid.ShowRefMode;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FbufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Cell reference mode:'#13);
    if w = 0
      then FDetails.Add('0 = RC mode (i.e. cell address shown as "R(1)C(-1)"')
      else FDetails.Add('1 = A1 mode (i.e. cell address shown as "B1")');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
    'Cell reference mode');
end;


procedure TBIFFGrid.ShowRefreshAll;
var
  numbytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('RefreshAll record:'#13);
    if w = 0
      then FDetails.Add('0 = Do not force refresh of external data ranges, PivotTables and XML maps on workbook load.')
      else FDetails.Add('1 = Force refresh of external data ranges, PivotTables and XML maps on workbook load.');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
    'Force refresh of external data ranges, Pivot tables and XML maps on workbook load');
end;


procedure TBIFFGrid.ShowRightMargin;
var
  numBytes: Integer;
  dbl: Double;
begin
  RowCount := FixedRows + 1;
  numBytes := 8;
  Move(FBuffer[FBufferIndex], dbl, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
    'Right page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;


procedure TBIFFGrid.ShowRK;
var
  numBytes: Integer;
  w: Word;
  dw: DWord;
  encint: DWord;
  encdbl: QWord;
  dbl: Double absolute encdbl;
  s: String;
begin
  RowCount := FixedRows + 4;

  ShowRowColData(FBufferIndex);

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to XF record');

  numBytes := 4;
  Move(FBuffer[FBufferIndex], dw, numBytes);
  dw := DWordLEToN(dw);

  if dw and $00000002 = 0 then begin
    encdbl := (QWord(dw) and QWord($FFFFFFFFFFFFFFFC)) shl 32;
    if dw and $00000001 = 1 then
      s := Format('%.2f', [dbl*0.01])
    else
      s := Format('%.0f', [dbl]);
  end
  else begin
    s := Format('$%.16x', [-59000000]);
    encint := ((dw and DWord($FFFFFFFC)) shr 2) or (dw and DWord($C0000000));
      // "arithmetic shift" = replace left-most bits by original bits
    if dw and $00000001 = 1 then
      s := FloatToStr(encint*0.01)
    else
      s := IntToStr(encint);
  end;

  if Row = FCurrRow then begin
    FDetails.Add('RK Value:'#13);
    if dw and $00000001 = 0
      then FDetails.Add('Bit 0 = 0: Value not changed')
      else FDetails.Add('Bit 0 = 1: Encoded value is multiplied by 100.');
    if dw and $00000002 = 0
      then FDetails.Add('Bit 1 = 0: Floating point value')
      else FDetails.Add('Bit 1 = 1: Signed integer value');
    FDetails.Add('Bits 31-2: Encoded value ' + s);
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes,
    Format('$%.8x', [QWord(dw)]), 'RK value ['+s+']');
end;


procedure TBIFFGrid.ShowRow;
var
  numBytes: Integer;
  dw: DWord;
  w: Word;
  b: Byte;
begin
  RowCount := FixedRows + IfThen(FFormat = sfExcel2, 10, 7);

  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index of this row');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to column of the first cell which is described by a cell record');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to column of the last cell which is described by a cell record, increased by 1');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Row height:'#13);
    FDetails.Add(Format('Bits 14-0 = %d: Row height in twips (1/20 pt) --> %.1f-pt',
      [w and $7FFF, (w and $7FFF)/20.0])
    );
    if w and $8000 = 0
      then FDetails.Add('Bit 15 = 0: Row has custom height')
      else FDetails.Add('Bit 15 = 1: Row has default height');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
    'Bits 14-0: Height of row in twips (1/20 pt), Bit 15: Row has default height');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, '', '(not used)');

  if FFormat = sfExcel2 then begin
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
      '0=No defaults written, 1=Default row attribute field and XF index occur below');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
      'Relative offset to calculate stream position of the first cell record for this row');

    if b = 1 then begin
      numBytes := 1;
      b := FBuffer[FBufferIndex];
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
        'Cell protection and XF index');

      numBytes := 1;
      b := FBuffer[FBufferIndex];
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
        'Indexes to FORMAT and FONT records');

      numBytes := 1;
      b := FBuffer[FBufferIndex];
      ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
        'Cell style');
    end;
  end
  else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, '',
      'In BIFF5-BIFF8 this field is not used anymore, but the DBCELL record instead.');

    numBytes := 4;
    Move(FBuffer[FBufferIndex], dw, numbytes);
    dw := DWordLEToN(dw);
    if Row = FCurrRow then begin
      FDetails.Add('Option flags and default row formatting:'#13);
      FDetails.Add(Format('Bits 0-2 = %d: Outline level of the row', [dw and $00000007]));
      if dw and $00000010 = 0
        then FDetails.Add('Bit 4 = 0: Outline group does not start or end here and is not collapsed')
        else FDetails.Add('Bit 4 = 1: Outline group starts or ends here and is collapsed');
      if dw and $00000020 = 0
        then FDetails.Add('Bit 5 = 0: Row is NOT hidden')
        else FDetails.Add('Bit 5 = 1: Row is hidden');
      if dw and $00000040 = 0
        then FDetails.Add('Bit 6 = 0: Row height and default font height do match.')
        else FDetails.Add('Bit 6 = 1: Row height and default font height do NOT match.');
      if dw and $00000080 = 0
        then FDetails.Add('Bit 7 = 0: Row does NOT have explicit default format.')
        else FDetails.Add('Bit 7 = 1: Row has explicit default format.');
      FDetails.Add('Bit 8 = 1: Is always 1');
      FDetails.Add(Format('Bits 16-27 = %d: Index to default XF record', [(dw and $0FFF0000) shr 16]));
      if dw and $10000000 = 0
        then FDetails.Add('Bit 28 = 0: No additional space above the row.')
        else FDetails.Add('Bit 28 = 1: Additional space above the row.');
      if dw and $20000000 = 0
        then FDetails.Add('Bit 29 = 0: No additional space below the row.')
        else FDetails.Add('Bit 29 = 1: Additional space below the row.');
      if dw and $40000000 = 0
        then FDetails.Add('Bit 30 = 0: D0 NOT show phonetic text for all cells in the row.')
        else FDetails.Add('Bit 30 = 1: Show phonetic text for all cells in the row.');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]),
      'Option flags and default row formatting');
  end;
end;

procedure TBIFFGrid.ShowRowColData(var ABufIndex: LongWord);
var
  w: Word;
  numBytes: Integer;
begin
  // Row
  numBytes := 2;
  Move(FBuffer[ABufIndex], w, numBytes);
  ShowInRow(FCurrRow, ABufIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to row');
  // Column
  numBytes := 2;
  Move(FBuffer[ABufIndex], w, numBytes);
  ShowInRow(FCurrRow, ABufIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to column');
end;


procedure TBIFFGrid.ShowSelection;
var
  numBytes: Integer;
  w: word;
  b: Byte;
  i, n: Integer;
begin
  Move(FBuffer[FBufferIndex+7], w, 2);
  n := WordLEToN(w);

  RowCount := FixedRows + 5 + n*4;

  numBytes := 1;
  b := FBuffer[FBufferIndex];
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Pane identifier (see PANE record)');

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to row of the active cell');

  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index to column of the active cell');

  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
    'Index into the following cell range list to the entry that contains the active cell');

  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(n),
    'Number of following cell range addresses');

  numbytes := 2;
  for i:=1 to n do begin
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first row');
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last row');
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to first column');
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to last column');
  end;
end;


procedure TBIFFGrid.ShowSharedFormula;
var
  numBytes: Integer;
  b: Byte;
  w: Word;
  tokenBytes: Integer;
begin
  BeginUpdate;
  RowCount := FixedRows + 1000;
  // Brute force simplification because of unknown row count at this point
  // Will be reduced at the end.

  // Index to first row
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to first row');

  // Index to last row
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)), 'Index to last row');

  // Index to first column
  numBytes := 1;  // 8-bit also for BIFF8!
  Move(FBuffer[FBufferIndex], b, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to first column');

  // Index to last column
  numBytes := 1;  // 8-bit also for BIFF8!
  Move(FBuffer[FBufferIndex], b, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Index to last column');

  // Not used
  numBytes := 1;
  Move(FBuffer[FBufferIndex], b, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Not used');

  // Number of existing FORMULA records for this shared formula
  numBytes := 1;
  Move(FBuffer[FBufferIndex], b, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b), 'Number of FORMULA records in shared formula');

  // Size of Token array (in Bytes)
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  tokenBytes := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(tokenBytes),
    'Size of formula data (in Bytes)');

  // Formula tokens
  ShowFormulaTokens(tokenBytes);

  RowCount := FCurrRow;
  EndUpdate(true);
end;


procedure TBIFFGrid.ShowSheet;
var
  numBytes: Integer;
  dw: DWord;
  b: Byte;
  s: String;
begin
  RowCount := FixedRows + 4;

  numBytes := 4;
  Move(FBuffer[FBufferIndex], dw, numBytes);
  dw := DWordLEToN(dw);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.8x)', [dw, dw]),
    'Absolute stream position of BOF record of sheet represented by this record.');

  numBytes := 1;
  b := FBuffer[FBufferIndex];
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
    'Sheet state (0=visible, 1=hidden, 2="very" hidden)');

  numBytes := 1;
  b := FBuffer[FBufferIndex];
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
    'Sheet type ($00=worksheet, $02=Chart, $06=VB module)');

  ExtractString(FBufferIndex, 1, (FFormat = sfExcel8), s, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, s, IfThen(FFormat=sfExcel8,
    'Sheet name (unicode string, 8-bit string length)',
    'Sheet name (byte string, 8-bit string length)')
  );
end;


procedure TBIFFGrid.ShowSheetPR;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Option flags:'#13);
    if w and $0001 = 0
      then FDetails.Add('Bit $0001 = 0: Do not show automatic page breaks')
      else FDetails.Add('Bit $0001 = 1: Show automatic page breaks');
    if w and $0010 = 0
      then FDetails.Add('Bit $0010 = 0: Standard sheet')
      else FDetails.Add('Bit $0010 = 1: Dialog sheet (BIFF5-BIFF8)');
    if w and $0020 = 0
      then FDetails.Add('Bit $0020 = 0: No automatic styles in outlines')
      else FDetails.Add('Bit $0020 = 1: Apply automatic styles to outlines');
    if w and $0040 = 0
      then FDetails.Add('Bit $0040 = 0: Outline buttons above outline group')
      else FDetails.Add('Bit $0040 = 1: Outline buttons below outline group');
    if w and $0080 = 0
      then FDetails.Add('Bit $0080 = 0: Outline buttons left of outline group')
      else FDetails.Add('Bit $0080 = 1: Outline buttons right of outline group');
    if w and $0100 = 0
      then FDetails.Add('Bit $0100 = 0: Scale printout in percent')
      else FDetails.Add('Bit $0100 = 1: Fit printout to number of pages');
    if w and $0200 = 0
      then FDetails.Add('Bit $0200 = 0: Save external linked values (BIFF3-BIFF4 only)')
      else FDetails.Add('Bit $0200 = 1: Do NOT save external linked values (BIFF3-BIFF4 only)');
    if w and $0400 = 0
      then FDetails.Add('Bit $0400 = 0: Do not show row outline symbols')
      else FDetails.Add('Bit $0400 = 1: Show row outline symbols');
    if w and $0800 = 0
      then FDetails.Add('Bit $0800 = 0: Do not show column outline symbols')
      else FDetails.Add('Bit $0800 = 1: Show column outline symbols');
    case (w and $3000) shr 12 of
      0: FDetails.Add('Bits $3000 = $0000: Arrange windows tiled');
      1: FDetails.Add('Bits $3000 = $1000: Arrange windows horizontal');
      2: FDetails.Add('Bits $3000 = $2000: Arrange windows vertical');
      3: FDetails.Add('Bits $3000 = $3000: Arrange windows cascaded');
    end;
    if w and $4000 = 0
      then FDetails.Add('Bits $4000 = 0: Excel like expression evaluation (BIFF4-BIFF8 only)')
      else FDetails.Add('Bits $4000 = 1: Lotus like expression evaluation (BIFF4-BIFF8 only)');
    if w and $8000 = 0
      then FDetails.Add('Bits $8000 = 0: Excel like formula editing (BIFF4-BIFF8 only)')
      else FDetails.Add('Bits $8000 = 1: Lotus like formula editing (BIFF4-BIFF8 only)');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x (%d)', [w, w]),
    'Option flags');
end;

procedure TBIFFGrid.ShowSST;
var
  numBytes: Integer;
  s: String;
  total1, total2: DWord;
  i: Integer;
begin
  numBytes := 4;
  Move(FBuffer[FBufferIndex], total1, numBytes);
  Move(FBuffer[FBufferIndex+4], total2, numBytes);
  total1 := DWordLEToN(total1);
  total2 := DWordLEToN(total2);

  RowCount := FixedRows + 2 + total2;

  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total1),
    'Total number of shared strings in the workbook');
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(total2),
    'Number of following strings');

  for i:=1 to total2 do begin
    ExtractString(FBufferIndex, 2, true, s, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, s, Format('Shared string #%d', [i]));
  end;
end;


procedure TBIFFGrid.ShowStandardWidth;
var
  w: Word;
  numBytes: Integer;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d (%f characters)', [w, w/256]),
    'Default column width (overrides DFCOLWIDTH, in 1/256 of "0" width)');
end;


procedure TBIFFGrid.ShowString;
var
  numBytes: Integer;
  s: String;
begin
  RowCount := FixedRows + 1;
  case FFormat of
    sfExcel2:
      begin
        ExtractString(FBufferIndex, 1, false, s, numBytes);
        ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Byte string, 8-bit string length');
      end;
    sfExcel5:
      begin
        ExtractString(FBufferIndex, 2, false, s, numBytes);
        ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Byte string, 16-bit string length');
      end;
    sfExcel8:
      begin
        ExtractString(FBufferIndex, 2, true, s, numBytes);
        ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Unicode string, 16-bit string length');
      end;
  end;
end;


procedure TBIFFGrid.ShowStyle;
var
  numBytes: Integer;
  b: Byte;
  w: Word;
  s: String;
  isRowLevel: Boolean;
  isColLevel: Boolean;
begin
  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if w and $8000 = 0 then
    RowCount := FixedRows + 2
  else
    RowCount := FixedRows + 3;
  if Row = FCurrRow then begin
    FDetails.Add('Style:'#13);
    FDetails.Add(Format('Bits 0-11 = %d: Index to style XF record', [w and $0FFFF]));
    if w and $8000 = 0
      then FDetails.Add('Bit 15 = 0: user-defined style')
      else FDetails.Add('Bit 15 = 1: built-in style');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Style index and type');

  if w and $8000 = 0 then begin
    if FFormat = sfExcel8 then begin
      ExtractString(FBufferIndex, 2, true, s, numBytes);
      ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Style name (Unicode string, 16-bit string length)');
    end else begin
      ExtractString(FBufferIndex, 1, false, s, numBytes);
      ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'Style name (Byte string, 8-bit string length)');
    end;
  end else begin
    numbytes := 1;
    b := FBuffer[FBufferIndex];
    isRowLevel := (b = 1);
    isColLevel := (b = 2);
    if FCurrRow = Row then begin
      FDetails.Add('Identifier for built-in cell style:'#13);
      case b of
        0: FDetails.Add('0 = normal');
        1: FDetails.Add('1 = RowLevel (see next field)');
        2: FDetails.Add('2 = ColLevel (see next field)');
        3: FDetails.Add('3 = Comma');
        4: FDetails.Add('4 = Currency');
        5: FDetails.Add('5 = Percent');
        6: FDetails.Add('6 = Comma [0]');
        7: FDetails.Add('7 = Currency [0]');
        8: FDetails.Add('8 = Hyperlink');
        9: FDetails.Add('9 = Followed hyperlink');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
      'Identifier for built-in cell style');

    b := FBuffer[FBufferIndex];
    if FCurrRow = Row then begin
      FDetails.Add('Level for RowLevel or ColLevel style (zero-based):'#13);
      if b = $FF then
        FDetails.Add('$FF = no RowLevel or ColLevel style')
      else
      if isRowLevel then
        FDetails.Add('RowLevel = ' + IntToStr(b))
      else if isColLevel then
        FDetails.Add('ColLevel = ' + IntToStr(b));
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      'Level for RowLevel or ColLevel style (if available)');
  end;
end;


procedure TBIFFGrid.ShowStyleExt;
var
  numBytes: Integer;
  w: Word;
  b: Byte;
  bs: Byte;
  s: String;
begin
  RowCount := FixedRows + 11;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]),
    'Future record type');
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Attributes:'#13);
    if w and $0001 = 0
      then FDetails.Add('Bit 0 = 0: The containing record does not specify a range of cells.')
      else FDetails.Add('Bit 0 = 1: The containing record specifies a range of cells.');
    FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+
      'when saving the file whithout having reckognized this record.');
    FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
    'Attributes');
  numbytes := 8;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Reserved');

  numbytes := 1;
  b := FBuffer[FBufferIndex];
  if Row = FCurrRow then begin
    FDetails.Add('Flags:'#13);
    if b and $01 = 0
      then FDetails.Add('Bit 0 = 0: no built-in style')
      else FDetails.Add('Bit 0 = 1: built-in style');
    if b and $02 = 0
      then FDetails.Add('Bit 1 = 0: NOT hidden')
      else FDetails.Add('Bit 1 = 1: hidden (i.e. is displayed in user interface)');
    FDetails.Add('Bit 2: specifies whether the built-in cell style was modified '+
      'by the user and thus has a custom definition.');
    FDetails.Add('Bit 3-7: Reserved');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Flags');

  numbytes := 1;
  b := FBuffer[FBufferIndex];
  if Row = FCurrRow then begin
    FDetails.Add('Category:'#13);
    case b of
      0: FDetails.Add('Bits 0-7 = 0: Custom style');
      1: FDetails.Add('Bits 0-7 = 1: Good, bad, neutral style');
      2: FDetails.Add('Bits 0-7 = 2: Data model style');
      3: FDetails.Add('Bits 0-7 = 3: Title and heading style');
      4: FDetails.Add('Bits 0-7 = 4: Themed cell style');
      5: FDetails.Add('Bits 0-7 = 5: Number format style');
    end;
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Category');

  numbytes := 1;
  b := FBuffer[FBufferIndex];
  if Row = FCurrRow then begin
    FDetails.Add('Built-in style:'#13);
    FDetails.Add('An unsigned integer that specifies the type of the built-in cell style.');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]), 'Built-in style');
  bs := b;

  numbytes := 1;
  b := FBuffer[FBufferindex];
  if Row = FCurrRow then begin
    FDetails.Add('Outline depth level:'#13);
    FDetails.Add('An unsigned integer that specifies the depth level of row/column automatic outlining.');
    if (bs in [1, 2]) then
      FDetails.Add(Format('Bits 0-7 = %d: Outline level is %d', [b, b+1]))
    else
      FDetails.Add(Format('Bits 0-7 = $%.2x: MUST be $FF, MUST be ignoried', [b]));
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]), 'Outline depth level');

  ExtractString(FBufferIndex, 1, true, s, numBytes, true);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, s, 'Name of the style name to extend (Unicode string, 8-bit string length)');

  numbytes := 2;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'XFProps (reserved)');

  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(w), 'Count of XFProp structures to follow in array');


end;


procedure TBiffGrid.ShowTabID;
var
  numbytes: Integer;
  w: word;
  i, n: Integer;
begin
  numbytes := 2;
  n := Length(FBuffer) div numbytes;
  RowCount := FixedRows + n;
  for i := 1 to n do begin
    Move(FBuffer[FBufferIndex], w, numbytes);
    w := WordLEToN(w);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
      'Unique sheet identifier');
  end;
end;


procedure TBIFFGrid.ShowTopMargin;
var
  numBytes: Integer;
  dbl: Double;
begin
  RowCount := FixedRows + 1;
  numBytes := 8;
  Move(FBuffer[FBufferIndex], dbl, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numBytes, FloatToStr(dbl),
    'Top page margin in inches (IEEE 754 floating-point value, 64-bit double precision)');
end;


procedure TBIFFGrid.ShowWindow1;
var
  numBytes: Word;
  b: Byte;
  w: word;
begin
  RowCount := FixedRows + IfThen(FFormat < sfExcel5, 5, 9);
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Horizontal position of the document window (in twips = 1/20 pt)');

  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Vertical position of the document window (in twips = 1/20 pt)');

  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Width of the document window (in twips = 1/20 pt)');

  Move(FBuffer[FBufferIndex], w, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
    'Height of the document window (in twips = 1/20 pt)');

  if FFormat < sfExcel5 then begin
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(b)),
      '0 = Window is visible; 1 = window is hidden');
  end else begin
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:');
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 = 0: Window is visible')
        else FDetails.Add('Bit $0001 = 1: Window is hidden');
      if w and $0002 = 0
        then FDetails.Add('Bit $0002 = 0: Window is open')
        else FDetails.Add('Bit $0002 = 1: Window is minimized');
      if w and $0008 = 0
        then FDetails.Add('Bit $0008 = 0: Horizontal scrollbar hidden')
        else FDetails.Add('Bit $0008 = 1: Horizontal scrollbar visible');
      if w and $0010 = 0
        then FDetails.Add('Bit $0010 = 0: Vertical scrollbar hidden')
        else FDetails.Add('Bit $0010 = 1: Vertical scrollbar visible');
      if w and $0020 = 0
        then FDetails.Add('Bit $0020 = 0: Worksheet tab bar hidden')
        else FDetails.Add('Bit $0020 = 1: Worksheet tab bar visible');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
      'Option flags');

    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Index to active (displayed) worksheet');

    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Index of first visible tab in the worksheet tab bar');

    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Number of selected worksheets (highlighted in the worksheet tab bar)');

    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Width of worksheet tab bar (in 1/1000 of window width). '+
      'The remaining space is used by the horizontal scrollbar.');
  end;
end;


procedure TBIFFGrid.ShowWindow2;
var
  numBytes: Word;
  b: Byte;
  w: word;
  dw : DWord;
begin
  if FFormat = sfExcel2 then begin
    RowCount := FixedRows + 9;
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      '0 = Show formula results; 1 = Show formulas');
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      '0 = Do not show grid lines; 1 = Show grid lines');
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      '0 = Do not show sheet headers; 1 = Show sheet headers');
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      '0 = Panes are not frozen; 1 = Panes are frozen');
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      '0 = Show zero values as empty cells; 1 = Show zero values');
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Index to first visible row');
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Index to first visible column');
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b),
      '0 = Use manual grid line colour (below); 1 = Use automatic grid line colour');
    numbytes := 4;
    Move(FBuffer[FBufferIndex], dw, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordLEToN(dw)]),
      'Grid line RGB color');
  end else begin
    RowCount := FixedRows + IfThen(FFormat = sfExcel5, 4, 8);
    numbytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('Option flags:');
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 = 0: Show formula results')
        else FDetails.Add('Bit $0001 = 1: Show formulas');
      if w and $0002 = 0
        then FDetails.Add('Bit $0002 = 0: Do not show grid lines')
        else FDetails.Add('Bit $0002 = 1: Show grid lines');
      if w and $0004 = 0
        then FDetails.Add('Bit $0004 = 0: Do not show sheet headers')
        else FDetails.Add('Bit $0004 = 1: Show sheet headers');
      if w and $0008 = 0
        then FDetails.Add('Bit $0008 = 0: Panes are not frozen')
        else FDetails.Add('Bit $0008 = 1: Panes are frozen');
      if w and $0010 = 0
        then FDetails.Add('Bit $0010 = 0: Show zero values as empty cells')
        else FDetails.Add('Bit $0010 = 1: Show zero values');
      if w and $0020 = 0
        then FDetails.Add('Bit $0020 = 0: Manual grid line color')
        else FDetails.Add('Bit $0020 = 1: Automatic grid line color');
      if w and $0040 = 0
        then FDetails.Add('Bit $0040 = 0: Columns from left to right')
        else FDetails.Add('Bit $0040 = 1: Columns from right to left');
      if w and $0080 = 0
        then FDetails.Add('Bit $0080 = 0: Do not show outline symbols')
        else FDetails.Add('Bit $0080 = 1: Show outline symbols');
      if w and $0100 = 0
        then FDetails.Add('Bit $0100 = 0: Keep splits if pane freeze is removed')
        else FDetails.Add('Bit $0100 = 1: Remove splits if pane freeze is removed');
      if w and $0200 = 0
        then FDetails.Add('Bit $0200 = 0: Sheet not selected')
        else FDetails.Add('Bit $0200 = 1: Sheet selected');
      if w and $0400 = 0
        then FDetails.Add('Bit $0400 = 0: Sheet not active')
        else FDetails.Add('Bit $0400 = 1: Sheet active');
      if w and $0800 = 0
        then FDetails.Add('Bit $0800 = 0: Show in normal view')
        else FDetails.Add('Bit $0800 = 1: Show in page break preview');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w, w]),
      'Option flags');

    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Index to first visible row');
    Move(FBuffer[FBufferIndex], w, numbytes);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
      'Index to first visible column');

    if FFormat =sfExcel5 then begin
      numbytes := 4;
      Move(FBuffer[FBufferIndex], dw, numbytes);
      ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [DWordLEToN(dw)]),
        'Grid line RGB color');
    end else begin
      numBytes := 2;
      Move(FBuffer[FBufferIndex], w, numbytes);
      ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
        'Color index of grid line color');

      ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Not used');

      Move(FBuffer[FBufferIndex], w, numbytes);
      ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
        'Cached magnification factor in page break preview (in percent); 0 = Default (60%)');

      Move(FBuffer[FBufferIndex], w, numbytes);
      ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(WordLEToN(w)),
        'Cached magnification factor in normal view (in percent); 0 = Default (100%)');

      numBytes := 4;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, '', 'Not used');
    end;
  end;
end;


procedure TBIFFGrid.ShowWindowProtect;
var
  numBytes: Integer;
  w: Word;
begin
  RowCount := FixedRows + 1;
  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Protection state of workbook windows:'#13);
    if w = 0
      then FDetails.Add('0 = The workbook windows can be resized or moved and the window state can be changed.')
      else FDetails.Add('1 = The workbook windows cannot be resized or moved and the window state cannot be changed.');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
    'Protection state of the workbook windows');
end;


procedure TBIFFGrid.ShowWriteAccess;
var
  numbytes: Integer;
  s: String;
begin
  RowCount := FixedRows + 1;
  ExtractString(FBufferIndex, IfThen(FFormat=sfExcel8, 2, 1), FFormat=sfExcel8, s, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, s, 'User name (i.e., the name that you type when you install Microsoft Excel');
end;


procedure TBIFFGrid.ShowWriteProt;
begin
  RowCount := FixedRows + 2;
  ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Write protect: if present file is write-protected');
  ShowInRow(FCurrRow, FBufferIndex, 0, '', 'Write protection password is in FILESHARING record');
end;


procedure TBIFFGrid.ShowXF;
var
  numBytes: Word;
  b: Byte;
  w: word;
  dw : DWord;
begin
  if FFormat = sfExcel2 then begin
    RowCount := FixedRows + 4;
    numBytes := 1;
    b := FBuffer[FBufferIndex];
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(b),
      'Index to font record');
    ShowInRow(FCurrRow, FBufferIndex, numBytes, '',
      '(not used)');

    b := FBuffer[FBufferIndex];
    if Row = FCurrRow then begin
      FDetails.Add('Number format and cell flags:'#13);
      FDetails.Add(Format('Bits 0-5 = %d: Index to FORMAT record', [b and $3F]));
      if b and $40 = 0
        then FDetails.Add('Bit 6    = 0: Cell is not locked')
        else FDetails.Add('Bit 6    = 1: Cell is locked');
      if b and $80 = 0
        then FDetails.Add('Bit 7    = 0: Formula is not hidden')
        else FDetails.Add('Bit 7    = 1: Formula is hidden');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('%d ($%.2x)', [b, b]),
      'Number format and cell flags');

    b := FBuffer[FBufferIndex];
    if Row = FCurrRow then begin
      FDetails.Add('Horizontal alignment, border style and background:'#13);
      case b and $07 of
        0: FDetails.Add('Bits $07 = 0: Horizontal alignment "General"');
        1: FDetails.Add('Bits $07 = 1: Horizontal alignment "Left"');
        2: FDetails.Add('Bits $07 = 2: Horizontal alignemnt "Centered"');
        3: FDetails.Add('Bits $07 = 3: Horizontal alignment "Right"');
        4: FDetails.Add('Bits $07 = 4: Horizontal alignment "Filled"');
        5: FDetails.Add('Bits $07 = 5: Horizontal alignment "Justified"');
        6: FDetails.Add('Bits $07 = 6: Horizontal alignment "Centred across selection"');
        7: FDetails.Add('Bits $07 = 7: Horizontal alignment "Distributed"');
      end;
      if b and $08 = 0
        then FDetails.Add('Bit $08 = 0: Cell has no left border')
        else FDetails.Add('Bit $08 = 1: Cell has left black border');
      if b and $10 = 0
        then FDetails.Add('Bit $10 = 0: Cell has no right border')
        else FDetails.Add('Bit $10 = 1: Cell has right black border');
      if b and $20 = 0
        then FDetails.Add('Bit $20 = 0: Cell has no top border')
        else FDetails.Add('Bit $20 = 1: Cell has top black border');
      if b and $40 = 0
        then FDetails.Add('Bit $40 = 0: Cell has no bottom border')
        else FDetails.Add('Bit $40 = 1: Cell has bottom black border');
      if b and $80 = 0
        then FDetails.Add('Bit $80 = 0: Cell has no shaded background')
        else FDetails.Add('Bit $80 = 1: Cell has shaded background');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
      'Horizontal alignment, border style, and background');
  end
  else
  begin // XF (BIFF5 and BIFF8)
    RowCount := FixedRows + IfThen(FFormat=sfExcel5, 7, 10);
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
      'Index to font record');
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    ShowInRow(FCurrRow, FBufferIndex, numBytes, IntToStr(WordLEToN(w)),
       'Index to format record');
    numBytes := 2;
    Move(FBuffer[FBufferIndex], w, numBytes);
    w := WordLEToN(w);
    if Row = FCurrRow then begin
      FDetails.Add('XFType, cell protection, parent style XF:'#13);
      if w and $0001 = 0
        then FDetails.Add('Bit $0001 = 0: Cell is not locked')
        else FDetails.Add('Bit $0001 = 1: Cell is locked');
      if w and $0002 = 0
        then FDetails.Add('Bit $0002 = 0: Formula is not hidden')
        else FDetails.Add('Bit $0002 = 1: Formula is hidden');
      if w and $0004 = 0
        then FDetails.Add('Bit $0004 = 0: Cell XF')
        else FDetails.Add('Bit $0004 = 1: Style XF');
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.4x', [w]),
      'XFType, cell protection, parent style XF');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    if Row = FCurrRow then begin
      FDetails.Add('Alignment and text break:'#13);
      case b and $03 of
        0: FDetails.Add('Bits 0-2 = 0: Horizontal alignment "General"');
        1: FDetails.Add('Bits 0-2 = 1: Horizontal alignment "Left"');
        2: FDetails.Add('Bits 0-2 = 2: Horizontal alignemnt "Centered"');
        3: FDetails.Add('Bits 0-2 = 3: Horizontal alignment "Right"');
        4: FDetails.Add('Bits 0-2 = 4: Horizontal alignment "Filled"');
        5: FDetails.Add('Bits 0-2 = 5: Horizontal alignment "Justified"');
        6: FDetails.Add('Bits 0-2 = 6: Horizontal alignment "Centred across selection"');
        7: if FFormat = sfExcel8 then
             FDetails.Add('Bits 0-2 = 7: Horizontal alignment "Distributed"');
      end;
      if b and $08 = 0
        then FDetails.Add('Bit 3    = 0: Text is not wrapped.')
        else FDetails.Add('Bit 3    = 1: Text is wrapped at right border.');
      case (b and $70) shr 4 of
        0: FDetails.Add('Bits 4-6 = 0: Vertical alignment "Top"');
        1: FDetails.Add('Bits 4-6 = 1: Vertical alignment "Centered"');
        2: FDetails.Add('Bits 4-6 = 2: Vertical alignment "Bottom"');
        3: FDetails.Add('Bits 4-6 = 3: Vertical alignment "Justified"');
        4: if FFormat = sfExcel8 then
             FDetails.Add('Bits 4-6 = 4: Vertical alignment "Distributed"');
      end;
      if FFormat = sfExcel8 then begin
        if b and $80 = 0
          then FDetails.Add('Bit 3    = 0: Don''t justify last line in justified or distibuted text')
          else FDetails.Add('Bit 3    = 1: Justify last line in justified or distibuted text');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
      'Alignment and text break');

    numBytes := 1;
    b := FBuffer[FBufferIndex];
    if FFormat = sfExcel5 then begin
      if Row = FCurrRow then begin
        FDetails.Add('Text orientation and flags for used attribute groups:'#13);
        case (b and $03) of
          0: FDetails.Add('Bits $03 = 0: not rotated');
          1: FDetails.Add('Bits $03 = 1: not rotated, letters stacked top-to-bottom');
          2: FDetails.Add('Bits $03 = 2: text rotated 90° counter-clockwise');
          3: FDetails.Add('Bits $03 = 3: text rotated 90° clockwise');
        end;
        if b and $04 = 0
          then FDetails.Add('Bit $04 = 0: No flag for number format')
          else FDetails.Add('Bit $04 = 1: Flag for number format');
        if b and $08 = 0
          then FDetails.Add('Bit $08 = 0: No flag for font')
          else FDetails.Add('Bit $08 = 2: Flag for font');
        if b and $10 = 0
          then FDetails.Add('Bit $10 = 0: No flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction')
          else FDetails.Add('Bit $10 = 1: Flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction');
        if b and $20 = 0
          then FDetails.Add('Bit $20 = 0: No flag for border lines')
          else FDetails.Add('Bit $20 = 1: Flag for border lines');
        if b and $40 = 0
          then FDetails.Add('Bit $40 = 0: No flag for background area style')
          else FDetails.Add('Bit $40 = 1: Flag for background area style');
        if b and $80 = 0
          then FDetails.Add('Bit $80 = 0: No flag for cell protection (cell locked and formula hidden)')
          else FDetails.Add('Bit $80 = 1: Flag for cell protection (cell locked and formula hidden)');
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]),
        'Text orientation and flags for used attribute groups');
    end else
    begin  // sfExcel8
      if Row = FCurrRow then begin
        FDetails.Add('Text rotation angle:'#13);
        if b = 0 then
          FDetails.Add('not rotated')
        else if b <= 90 then
          FDetails.Add(Format('%d degrees counter-clockwise', [b]))
        else if b <= 180 then
          FDetails.Add(Format('%d degrees clockwize', [b-90]))
        else if b = 255 then
          FDetails.Add('not rotated, letters stacked top-to-bottom');
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(b), 'Text rotation angle');
    end;

    if FFormat = sfExcel8 then begin
      numBytes := 1;
      b := FBuffer[FBufferIndex];
      if Row = FCurrRow then begin
        FDetails.Add('Indentation, shrink to cell size, and text direction:'#13);
        FDetails.Add(Format('Bits 0-3: Indent level = %d', [b and $0F]));
        if b and $10 = 0
          then FDetails.Add('Bit $10 = 0: Don''t shrink content to fit into cell')
          else FDetails.Add('Bit $10 = 1: Shrink content to fit into cell');
        if b and $20 = 0
          then FDetails.Add('Bit $20 = 0: Merge Cell option is OFF')
          else FDetails.Add('Bit $20 = 1: Merge Cell option is ON');
        case (b and $C0) shr 6 of
          0: FDetails.Add('Bits 6-7 = 0: Text direction according to context');
          1: FDetails.Add('Bits 6-7 = 1: Text direction left-to-right');
          2: FDetails.Add('Bits 6-7 = 2: Text direction right-to-left');
        end;
      end;
      ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.2x', [b]),
        'Indentation, shrink to cell size, and text direction');

      numBytes := 1;
      b := FBuffer[FBufferIndex];
      if Row = FCurrRow then begin
        FDetails.Add('Flags for used attribute groups:'#13);
        if b and $04 = 0
          then FDetails.Add('Bit $04 = 0: No flag for number format')
          else FDetails.Add('Bit $04 = 1: Flag for number format');
        if b and $08 = 0
          then FDetails.Add('Bit $08 = 0: No flag for font')
          else FDetails.Add('Bit $08 = 2: Flag for font');
        if b and $10 = 0
          then FDetails.Add('Bit $10 = 0: No flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction')
          else FDetails.Add('Bit $10 = 1: Flag for hor/vert alignment, text wrap, indentation, orientation, rotation, and text direction');
        if b and $20 = 0
          then FDetails.Add('Bit $20 = 0: No flag for border lines')
          else FDetails.Add('Bit $20 = 1: Flag for border lines');
        if b and $40 = 0
          then FDetails.Add('Bit $40 = 0: No flag for background area style')
          else FDetails.Add('Bit $40 = 1: Flag for background area style');
        if b and $80 = 0
          then FDetails.Add('Bit $80 = 0: No flag for cell protection (cell locked and formula hidden)')
          else FDetails.Add('Bit $80 = 1: Flag for cell protection (cell locked and formula hidden)');
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.2x', [b]),
        'Flags for used attribute groups');

      numBytes := 4;
      Move(FBuffer[FBufferIndex], dw, numbytes);
      dw := DWordLEToN(dw);
      if Row = FCurrRow then begin
        FDetails.Add('Cell border lines and background area:'#13);
        case dw and $0000000F of
          $0000: FDetails.Add('Bits 0-3 = 0: Left border = No line');
          $0001: FDetails.Add('Bits 0-3 = 1: Left border = thin solid line');
          $0002: FDetails.Add('Bits 0-3 = 2: Left border = medium solid line');
          $0003: FDetails.Add('Bits 0-3 = 3: Left border = dashed line');
          $0004: FDetails.Add('Bits 0-3 = 4: Left border = dotted line');
          $0005: FDetails.Add('Bits 0-3 = 5: Left border = thick solid line');
          $0006: FDetails.Add('Bits 0-3 = 6: Left border = double solid line');
          $0007: FDetails.Add('Bits 0-3 = 7: Left border = hair line');
          $0008: FDetails.Add('Bits 0-3 = 8: Left border = medium dashed');
          $0009: FDetails.Add('Bits 0-3 = 9: Left border = thin dash-dotted');
          $000A: FDetails.Add('Bits 0-3 = 10: Left border = medium dash-dotted');
          $000B: FDetails.Add('Bits 0-3 = 11: Left border = thin dash-dot-dotted');
          $000C: FDetails.Add('Bits 0-3 = 12: Left border = medium dash-dot-dotted');
          $000D: FDetails.Add('Bits 0-3 = 13: Left border = slanted medium dash-dotted');
        end;
        case dw and $000000F0 of
          $0000: FDetails.Add('Bits 4-7 = 0: Right border = No line');
          $0010: FDetails.Add('Bits 4-7 = 1: Right border = thin solid line');
          $0020: FDetails.Add('Bits 4-7 = 2: Right border = medium solid line');
          $0030: FDetails.Add('Bits 4-7 = 3: Right border = dashed line');
          $0040: FDetails.Add('Bits 4-7 = 4: Right border = dotted line');
          $0050: FDetails.Add('Bits 4-7 = 5: Right border = thick solid line');
          $0060: FDetails.Add('Bits 4-7 = 6: Right border = double solid line');
          $0070: FDetails.Add('Bits 4-7 = 7: Right border = hair line');
          $0080: FDetails.Add('Bits 4-7 = 8: Right border = medium dashed');
          $0090: FDetails.Add('Bits 4-7 = 9: Right border = thin dash-dotted');
          $00A0: FDetails.Add('Bits 4-7 = 10: Right border = medium dash-dotted');
          $00B0: FDetails.Add('Bits 4-7 = 11: Right border = thin dash-dot-dotted');
          $00C0: FDetails.Add('Bits 4-7 = 12: Right border = medium dash-dot-dotted');
          $00D0: FDetails.Add('Bits 4-7 = 13: Right border = slanted medium dash-dotted');
        end;
        case dw and $00000F00 of
          $0000: FDetails.Add('Bits 8-11 = 0: Top border = No line');
          $0100: FDetails.Add('Bits 8-11 = 1: Top border = thin solid line');
          $0200: FDetails.Add('Bits 8-11 = 2: Top border = medium solid line');
          $0300: FDetails.Add('Bits 8-11 = 3: Top border = dashed line');
          $0400: FDetails.Add('Bits 8-11 = 4: Top border = dotted line');
          $0500: FDetails.Add('Bits 8-11 = 5: Top border = thick solid line');
          $0600: FDetails.Add('Bits 8-11 = 6: Top border = double solid line');
          $0700: FDetails.Add('Bits 8-11 = 7: Top border = hair line');
          $0800: FDetails.Add('Bits 8-11 = 8: Top border = medium dashed');
          $0900: FDetails.Add('Bits 8-11 = 9: Top border = thin dash-dotted');
          $0A00: FDetails.Add('Bits 8-11 = 10: Top border = medium dash-dotted');
          $0B00: FDetails.Add('Bits 8-11 = 11: Top border = thin dash-dot-dotted');
          $0C00: FDetails.Add('Bits 8-11 = 12: Top border = medium dash-dot-dotted');
          $0D00: FDetails.Add('Bits 8-11 = 13: Top border = slanted medium dash-dotted');
        end;
        case dw and $0000F000 of
          $0000: FDetails.Add('Bits 12-15 = 0: Bottom border = No line');
          $1000: FDetails.Add('Bits 12-15 = 1: Bottom border = thin solid line');
          $2000: FDetails.Add('Bits 12-15 = 2: Bottom border = medium solid line');
          $3000: FDetails.Add('Bits 12-15 = 3: Bottom border = dashed line');
          $4000: FDetails.Add('Bits 12-15 = 4: Bottom border = dotted line');
          $5000: FDetails.Add('Bits 12-15 = 5: Bottom border = thick solid line');
          $6000: FDetails.Add('Bits 12-15 = 6: Bottom border = double solid line');
          $7000: FDetails.Add('Bits 12-15 = 7: Bottom border = hair line');
          $8000: FDetails.Add('Bits 12-15 = 8: Bottom border = medium dashed');
          $9000: FDetails.Add('Bits 12-15 = 9: Bottom border = thin dash-dotted');
          $A000: FDetails.Add('Bits 12-15 = 10: Bottom border = medium dash-dotted');
          $B000: FDetails.Add('Bits 12-15 = 11: Bottom border = thin dash-dot-dotted');
          $C000: FDetails.Add('Bits 12-15 = 12: Bottom border = medium dash-dot-dotted');
          $D000: FDetails.Add('Bits 12-15 = 13: Bottom border = slanted medium dash-dotted');
        end;
        FDetails.Add(Format('Bits 16-22 = %d: Color index for left line color',  [(dw and $007F0000) shr 16]));
        FDetails.Add(Format('Bits 23-29 = %d: Color index for right line color', [(dw and $3F800000) shr 23]));
        if dw and $40000000 = 0
          then FDetails.Add('Bit 30 = 0: No diagonal line from top left to right bottom')
          else FDetails.Add('Bit 30 = 1: Diagonal line from top left to right bottom');
        if dw and $80000000 = 0
          then FDetails.Add('Bit 31 = 0: No diagonal line from bottom left to right top')
          else FDetails.Add('Bit 31 = 1: Diagonal line from bottom left to right top');
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
        'Cell border lines and background area');

      numBytes := 4;
      Move(FBuffer[FBufferIndex], dw, numbytes);
      if Row = FCurrRow then begin
        FDetails.Add('Cell border lines and background area (cont''d):'#13);
        FDetails.Add(Format('Bits 0-6 = %d: Color index for top line color',  [(dw and $0000007F)]));
        FDetails.Add(Format('Bits 7-13 = %d: Color index for bottom line color', [(dw and $00003F80) shr 7]));
        FDetails.Add(Format('Bits 14-20 = %d: Color index for diagonal line color', [(dw and $001FC000) shr 14]));
        case dw and $01E00000 shr 17 of
          $0: FDetails.Add('Bits 21-24 = 0: Diagonal line style = No line');
          $1: FDetails.Add('Bits 21-24 = 1: Diagonal line style = thin solid line');
          $2: FDetails.Add('Bits 21-24 = 2: Diagonal line style = medium solid line');
          $3: FDetails.Add('Bits 21-24 = 3: Diagonal line style = dashed line');
          $4: FDetails.Add('Bits 21-24 = 4: Diagonal line style = dotted line');
          $5: FDetails.Add('Bits 21-24 = 5: Diagonal line style = thick solid line');
          $6: FDetails.Add('Bits 21-24 = 6: Diagonal line style = double solid line');
          $7: FDetails.Add('Bits 21-24 = 7: Diagonal line style = hair line');
          $8: FDetails.Add('Bits 21-24 = 8: Diagonal line style = medium dashed');
          $9: FDetails.Add('Bits 21-24 = 9: Diagonal line style = thin dash-dotted');
          $A: FDetails.Add('Bits 21-24 = 10: Diagonal line style = medium dash-dotted');
          $B: FDetails.Add('Bits 21-24 = 11: Diagonal line style = thin dash-dot-dotted');
          $C: FDetails.Add('Bits 21-24 = 12: Diagonal line style = medium dash-dot-dotted');
          $D: FDetails.Add('Bits 21-24 = 13: Diagonal line style = slanted medium dash-dotted');
        end;
        case (dw and $FC000000) shr 26 of
          $00: FDetails.Add('Bits 26-31 = 0: Fill pattern = No fill');
          $01: FDetails.Add('Bits 26-31 = 1: Fill pattern = solid fill');
          $02: FDetails.Add('Bits 26-31 = 2: Fill pattern = medium fill');
          $03: FDetails.Add('Bits 26-31 = 3: Fill pattern = dense fill');
          $04: FDetails.Add('Bits 26-31 = 4: Fill pattern = sparse fill');
          $05: FDetails.Add('Bits 26-31 = 5: Fill pattern = horizontal fill');
          $06: FDetails.Add('Bits 26-31 = 6: Fill pattern = vertical fill');
          $07: FDetails.Add('Bits 26-31 = 7: Fill pattern = backslash fill');
          $08: FDetails.Add('Bits 26-31 = 8: Fill pattern = slash fill');
          $09: FDetails.Add('Bits 26-31 = 9: Fill pattern = coarse medium fill');
          $0A: FDetails.Add('Bits 26-31 = 10: Fill pattern = coarse medium horiz fill');
          $0B: FDetails.Add('Bits 26-31 = 11: Fill pattern = sparse horizontal fill');
          $0C: FDetails.Add('Bits 26-31 = 12: Fill pattern = sparse vertical fill');
          $0D: FDetails.Add('Bits 26-31 = 13: Fill pattern = sparse backslash fill');
          $0E: FDetails.Add('Bits 26-31 = 14: Fill pattern = sparse slash fill');
          $0F: FDetails.Add('Bits 26-31 = 15: Fill pattern = cross fill');
          $10: FDetails.Add('Bits 26-31 = 16: Fill pattern = dense backslash fill');
          $11: FDetails.Add('Bits 26-31 = 17: Fill pattern = very sparse fill');
          $12: FDetails.Add('Bits 26-31 = 18: Fill pattern = extremely sparse fill');
        end;
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
        'Cell border lines and background area (cont''d)');

      numBytes := 2;
      Move(FBuffer[FBufferIndex], w, numbytes);
      w := WordLEToN(w);
      if Row = FCurrRow then begin
        FDetails.Add('Cell border lines and background area (cont''d):'#13);
        FDetails.Add(Format('Bits 0-6 = %d: Color index for pattern color',  [(w and $007F)]));
        FDetails.Add(Format('Bits 7-13 = %d: Color index for pattern background color', [(w and $3F80) shr 7]));
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
        'Cell border lines and background area (cont''d)');
    end;

    if FFormat = sfExcel5 then begin
      numBytes := 4;
      Move(FBuffer[FBufferIndex], dw, numBytes);
      dw := DWordLEToN(dw);
      if Row = FCurrRow then begin
        FDetails.Add('Cell border lines and background area:'#13);
        FDetails.Add(Format('Bits 0-6 = %d: Color index for pattern color',  [(dw and $007F)]));
        FDetails.Add(Format('Bits 7-13 = %d: Color index for pattern background color', [(dw and $3F80) shr 7]));
        case (dw and $003F0000) shr 16 of
          $00: FDetails.Add('Bits 16-21 = 0: Fill pattern = No fill');
          $01: FDetails.Add('Bits 16-21 = 1: Fill pattern = solid fill');
          $02: FDetails.Add('Bits 16-21 = 2: Fill pattern = medium fill');
          $03: FDetails.Add('Bits 16-21 = 3: Fill pattern = dense fill');
          $04: FDetails.Add('Bits 16-21 = 4: Fill pattern = sparse fill');
          $05: FDetails.Add('Bits 16-21 = 5: Fill pattern = horizontal fill');
          $06: FDetails.Add('Bits 16-21 = 6: Fill pattern = vertical fill');
          $07: FDetails.Add('Bits 16-21 = 7: Fill pattern = backslash fill');
          $08: FDetails.Add('Bits 16-21 = 8: Fill pattern = slash fill');
          $09: FDetails.Add('Bits 16-21 = 9: Fill pattern = coarse medium fill');
          $0A: FDetails.Add('Bits 16-21 = 10: Fill pattern = coarse medium horiz fill');
          $0B: FDetails.Add('Bits 16-21 = 11: Fill pattern = sparse horizontal fill');
          $0C: FDetails.Add('Bits 16-21 = 12: Fill pattern = sparse vertical fill');
          $0D: FDetails.Add('Bits 16-21 = 13: Fill pattern = sparse backslash fill');
          $0E: FDetails.Add('Bits 16-21 = 14: Fill pattern = sparse slash fill');
          $0F: FDetails.Add('Bits 16-21 = 15: Fill pattern = cross fill');
          $10: FDetails.Add('Bits 16-21 = 16: Fill pattern = dense backslash fill');
          $11: FDetails.Add('Bits 16-21 = 17: Fill pattern = very sparse fill');
          $12: FDetails.Add('Bits 16-21 = 18: Fill pattern = extremely sparse fill');
        end;
        case dw and $01C00000 shr 22 of
          $0: FDetails.Add('Bits 22-24 = 0: Bottom line style = No line');
          $1: FDetails.Add('Bits 22-24 = 1: Bottom line style = thin solid line');
          $2: FDetails.Add('Bits 22-24 = 2: Bottom line style = medium solid line');
          $3: FDetails.Add('Bits 22-24 = 3: Bottom line style = dashed line');
          $4: FDetails.Add('Bits 22-24 = 4: Bottom line style = dotted line');
          $5: FDetails.Add('Bits 22-24 = 5: Bottom line style = thick solid line');
          $6: FDetails.Add('Bits 22-24 = 6: Bottom line style = double solid line');
          $7: FDetails.Add('Bits 22-24 = 7: Bottom line style = hair line');
        end;
        FDetails.Add(Format('Bits 25-31 = %d: Color index for bottom line color', [(dw and $FE000000) shr 25]));
      end;
      ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.8x', [dw]),
        'Cell border lines & background area');

      numBytes := 4;
      Move(FBuffer[FBufferIndex], dw, numBytes);
      dw := DWOrdLEToN(dw);
      if Row = FCurrRow then begin
        FDetails.Add('Cell border lines (cont''d):'#13);
        case dw and $00000007 of
          $00: FDetails.Add('Bits 0-2 = 0: Top border = No line');
          $01: FDetails.Add('Bits 0-2 = 1: Top border = thin solid line');
          $02: FDetails.Add('Bits 0-2 = 2: Top border = medium solid line');
          $03: FDetails.Add('Bits 0-2 = 3: Top border = dashed line');
          $04: FDetails.Add('Bits 0-2 = 4: Top border = dotted line');
          $05: FDetails.Add('Bits 0-2 = 5: Top border = thick solid line');
          $06: FDetails.Add('Bits 0-2 = 6: Top border = double solid line');
          $07: FDetails.Add('Bits 0-2 = 7: Top border = hair line');
        end;
        case (dw and $00000038) shr 3 of
          $0000: FDetails.Add('Bits 3-5 = 0: Left border = No line');
          $0001: FDetails.Add('Bits 3-5 = 1: Left border = thin solid line');
          $0002: FDetails.Add('Bits 3-5 = 2: Left border = medium solid line');
          $0003: FDetails.Add('Bits 3-5 = 3: Left border = dashed line');
          $0004: FDetails.Add('Bits 3-5 = 4: Left border = dotted line');
          $0005: FDetails.Add('Bits 3-5 = 5: Left border = thick solid line');
          $0006: FDetails.Add('Bits 3-5 = 6: Left border = double solid line');
          $0007: FDetails.Add('Bits 3-5 = 7: Left border = hair line');
        end;
        case (dw and $000001C0) shr 6 of
          $0000: FDetails.Add('Bits 6-8 = 0: Right border = No line');
          $0010: FDetails.Add('Bits 6-8 = 1: Right border = thin solid line');
          $0020: FDetails.Add('Bits 6-8 = 2: Right border = medium solid line');
          $0030: FDetails.Add('Bits 6-8 = 3: Right border = dashed line');
          $0040: FDetails.Add('Bits 6-8 = 4: Right border = dotted line');
          $0050: FDetails.Add('Bits 6-8 = 5: Right border = thick solid line');
          $0060: FDetails.Add('Bits 6-8 = 6: Right border = double solid line');
          $0070: FDetails.Add('Bits 6-8 = 7: Right border = hair line');
        end;
        FDetails.Add(Format('Bits 9-15 = %d: Color index for top line color', [(dw and $0000FE00) shr 7]));
        FDetails.Add(Format('Bits 16-22 = %d: Color index for left line color', [(dw and $007F0000) shr 16]));
        FDetails.Add(Format('Bits 23-29 = %d: Color index for right line color', [(dw and $3F800000) shr 23]));
      end;
      ShowInRow(FCurrRow, FBufferIndex, numBytes, Format('$%.8x', [dw]),
        'Cell border lines (cont''d)');
    end;
  end;
end;


procedure TBIFFGrid.ShowXFCRC;
var
  numBytes: Integer;
  w: Word;
  dw: DWord;
begin
  RowCount := FixedRows + 7;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]),
    'Future record type');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Attributes:'#13);
    if w and $0001 = 0
      then FDetails.Add('Bit 0 = 0: The containing record does not specify a range of cells.')
      else FDetails.Add('Bit 0 = 1: The containing record specifies a range of cells.');
    FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+
      'when saving the file whithout having reckognized this record.');
    FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
    'Attributes');

  numbytes := 4;
  Move(FBuffer[FBufferIndex], dw, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');
  Move(FBuffer[FBufferIndex], dw, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');

  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
    'Count of XF records');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]),
    'Checksum of XF records');
end;


procedure TBIFFGrid.ShowXFEXT;
var
  numBytes: Integer;
  w: Word;
  dw: DWord;
  i, n: Integer;
  et: Word;
  es: Word;
  ct: Word;
  buffidx: Cardinal;
  s: String;
begin
  BeginUpdate;

  RowCount := FixedRows + 100;

  numBytes := 2;
  Move(FBuffer[FBufferIndex], w, numBytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [wordLEToN(w)]),
    'Future record type');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  if Row = FCurrRow then begin
    FDetails.Add('Attributes:'#13);
    if w and $0001 = 0
      then FDetails.Add('Bit 0 = 0: The containing record does not specify a range of cells.')
      else FDetails.Add('Bit 0 = 1: The containing record specifies a range of cells.');
    FDetails.Add('Bit 1: specifies wether to alert the user of possible problems '+
      'when saving the file whithout having reckognized this record.');
    FDetails.Add('Bits 2-15: reserved (MUST be zero, MUST be ignored)');
  end;
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]),
    'Attributes');

  numbytes := 4;
  Move(FBuffer[FBufferIndex], dw, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');
  Move(FBuffer[FBufferIndex], dw, numbytes);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(DWordLEToN(dw)), 'Reserved');

  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]),
    'XF index');

  numbytes := 2;
  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('$%.4x', [w]), 'Reserved');

  Move(FBuffer[FBufferIndex], w, numbytes);
  w := WordLEToN(w);
  ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
    'Number of extension properties');
  n := w;

  for i:=1 to n do begin
    buffidx := FBufferIndex;
    numbytes := 2;
    Move(FBuffer[FBufferIndex], et, numbytes);
    et := WordLEToN(et);
    if Row = FCurrRow then begin
      FDetails.Add('Type:'#13);
      case et of
        $04: FDetails.Add('Full color extension that specifies the cell interior foreground color.');
        $05: FDetails.Add('Full color extension that specifies the cell interior background color.');
        $06: FDetails.Add('Gradient extension that specifies a cell interior gradient fill.');
        $07: FDetails.Add('Full color extension that specifies the top cell border color.');
        $08: FDetails.Add('Full color extension that specifies the bottom cell border color.');
        $09: FDetails.Add('Full color extension that specifies the left cell border color.');
        $0A: FDetails.Add('Full color extension that specifies the right cell border color.');
        $0B: FDetails.Add('Full color extension that specifies the diagonal cell border color.');
        $0D: FDetails.Add('Full color extension that specifies the cell text color.');
        $0E: FDetails.Add('2-byte unsigned integer that specifies a font scheme.');
        $0F: FDetails.Add('2-byte unsigned integer that specifies the text indentation level (MUST be <= 250).');
      end;
    end;
    ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [et, et]),
      Format('Extension property #%d: Type', [i]));
    Move(FBuffer[FBufferIndex], es, numbytes);
    es := WordLEToN(es);
    ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(es),
      Format('Extension property #%d: Data size', [i]));

    case et of
      $04, $05, $07..$0D:  // FullColorExt
        begin
          numbytes := 2;
          Move(FBuffer[FBufferIndex], ct, numbytes);
          ct := WordLEToN(ct);
          if Row = FCurrRow then begin
            FDetails.Add('Full color extension - Color type:'#13);
            case ct of
              0: FDetails.Add('0 - Automatic color');
              1: FDetails.Add('1 - Indexed color');
              2: FDetails.Add('2 - RGB color');
              3: FDetails.Add('3 - Theme color');
              4: FDetails.Add('4 - Color not set');
            end;
          end;
          ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(ct),
            Format('Extension property #%d (Full color extension): Color type', [i]));
          numbytes := 2;
          Move(FBuffer[FBufferIndex], w, numbytes);
          w := WordLEToN(w);
          ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(integer(w)),
            Format('Extension property #%d (Full color extension): Color tint', [i]));
          numbytes := 4;
          Move(FBuffer[FBufferIndex], dw, numbytes);
          dw := DWordLEToN(dw);
          case ct of
            0: s := '(dummy - MUST be 0)';
            1: s := '(index)';
            2: s := '(RGB value)';
            3: s := '(theme)';
            else s := '';
          end;
          ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]),
            Format('Extension property #%d (Full color extension): value %s', [i, s]));
          numbytes := 4;
          Move(FBuffer[FBufferIndex], dw, numbytes);
          dw := DWordLEToN(dw);
          ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]),
            Format('Extension property #%d (Full color extension): Reserved', [i]));
          Move(FBuffer[FBufferIndex], dw, numbytes);
          dw := DWordLEToN(dw);
          ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.8x)', [QWord(dw), QWord(dw)]),
            Format('Extension property #%d (Full color extension): Reserved', [i]));
        end;

      $06:  // Gradient
        begin
          ShowInRow(FCurrRow, FBufferIndex, es, '(var)',
            Format('Extension property #%d (Gradient): - not interpreted here -', [i]));
        end;

      $0E:  // Font scheme
        begin
          numbytes := 2;
          Move(FBuffer[FBufferIndex], w, numbytes);
          w := WordLEToN(w);
          if Row = FCurrRow then begin
            FDetails.Add('Font scheme:'#13);
            case w of
              0: FDetails.Add('0 - No font scheme');
              1: FDetails.Add('1 - Major scheme');
              2: FDetails.Add('2 - Minor scheme');
              3: FDetails.Add('3 - Ninched scheme');
            end;
          end;
          ShowInRow(FCurrRow, FBufferIndex, numbytes, Format('%d ($%.4x)', [w,w]),
            Format('Extension property #%d Font scheme', [i]));
        end;

      $0F:   // Text indentation level
        begin
          numbytes := 2;
          Move(FBuffer[FBufferIndex], w, numbytes);
          w := WordLEToN(w);
          ShowInRow(FCurrRow, FBufferIndex, numbytes, IntToStr(w),
            Format('Extension property #%d Text indentation level', [i]));
        end;
    end;
    FBufferIndex := buffidx + es;
  end;
  RowCount := FCurrRow;

  EndUpdate(true);
end;


end.

