unit fEntityInspector;

interface

uses
  Windows, Messages, Forms, Grids, SysUtils, Math, Controls, StdCtrls, Classes, ComCtrls,
  DXFConv, DXFImage, sgDrawingNavigator, sgConsts, ExtCtrls, Buttons;

type
  TfmEntityInspector = class(TForm)
    StatusBar: TStatusBar;
    StringGridObject: TStringGrid;
    pnlCombo: TPanel;
    ComboBox: TComboBox;
    SpeedButtonStayOnTop: TSpeedButton;
    procedure FormResize(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure ComboBoxSelect(Sender: TObject);
    procedure StringGridObjectSelectCell(Sender: TObject; ACol,
      ARow: Integer; var CanSelect: Boolean);
    procedure StringGridObjectSetEditText(Sender: TObject; ACol,
      ARow: Integer; const Value: String);
    procedure StringGridObjectKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure StringGridObjectExit(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure SpeedButtonStayOnTopMouseUp(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    procedure pnlComboResize(Sender: TObject);
  private
    FDNavigator: TsgDrawingNavigator;
    Column, Row: Integer;// selected cell
    FChangeCellText, Transition: Boolean;
    PrevCellText, CellText: String;
    FComboBoxWndProc: TWndMethod;
    function GetCADImage: TsgDXFImage;
    procedure SetChangeCellText(const Value: Boolean);
    property CADImage: TsgDXFImage read GetCADImage;
    property ChangeCellText: Boolean read FChangeCellText write SetChangeCellText;
    procedure ComboBoxWndProc(var Message: TMessage);
  public
    { Public declarations }
    procedure AddEntity(AEnt: TsgDXFEntity; ARefresh: Boolean);
    procedure Clear;
    procedure ChangeObjectParams;    
    procedure Load(AImage: TsgDrawingNavigator);
    procedure RefreshGrid(Entity: TsgDXFEntity);
    procedure UpdateEntity(AEntity: TsgDXFEntity);
  end;

var
  fmEntityInspector: TfmEntityInspector;

implementation
{$INCLUDE SGDXF.INC}

const
  ROW_NAME      = 1;
  ROW_LAYER     = 2;
  ROW_COLOR     = 3;
  ROW_BOXLEFT   = 4;
  ROW_BOXTOP    = 5;
  ROW_BOXRIGHT  = 6;
  ROW_BOXBOTTOM = 7;
  ROW_COUNT     = 8;

{$R *.dfm}

procedure TfmEntityInspector.FormCreate(Sender: TObject);
begin
  {$IFDEF SGDEL_5}
  BiDiMode := bdLeftToRight;
  DockSite := True;
  DragKind := dkDock;
  DragMode := dmAutomatic;
  OldCreateOrder := True;
  ParentBiDiMode := False;
  UseDockManager := True;
  ComboBox.Anchors := [akLeft, akTop, akRight];
  StringGridObject.Anchors := [akLeft, akTop, akRight, akBottom];
  StringGridObject.BiDiMode := bdLeftToRight;
  StringGridObject.ParentBiDiMode := False;
  {$ENDIF}
  FComboBoxWndProc := ComboBox.WindowProc; // for capability with D3,4
  ComboBox.WindowProc := ComboBoxWndProc; // to provide OnCloseUp event
  // To forbid editing
  //StringGridObject.Options := StringGridObject.Options - [goEditing];
  Transition := False;
  ChangeCellText := False;
  Column := -1;
  Row := -1;
  StringGridObject.RowCount := ROW_COUNT;
  StringGridObject.Align := alClient;
  with StringGridObject do
  begin
    StringGridObject.FixedCols := 1;
    StringGridObject.FixedRows := 1;
    Height:= (DefaultRowHeight+GridLineWidth)*RowCount+GridLineWidth;
    Width:= fmEntityInspector.ClientWidth;
    // Next values a fixed
    Rows[0].Strings[0] := ' Property ';
    Rows[0].Strings[1] := ' Value ';
  end;
end;

procedure TfmEntityInspector.FormDestroy(Sender: TObject);
begin
  // Restore inherited ComboBox WindowProc property
  ComboBox.WindowProc := FComboBoxWndProc;
end;

procedure TfmEntityInspector.FormResize(Sender: TObject);
begin
  StringGridObject.DefaultColWidth := Ceil(Extended(StringGridObject.Width/2.0))-2;
end;

procedure TfmEntityInspector.RefreshGrid(Entity: TsgDXFEntity);
var
  I, Cnt: Integer;
begin
  ComboBox.ItemIndex := ComboBox.Items.IndexOfObject(Entity);

  if ComboBox.ItemIndex <> -1 then
  begin
    StringGridObject.RowCount := ROW_COUNT;
    StringGridObject.Rows[ROW_NAME].Strings[0] := ' Name ';
    StringGridObject.Rows[ROW_LAYER].Strings[0] := ' Layer ';
    StringGridObject.Rows[ROW_COLOR].Strings[0] := ' Color (RGB)';
    StringGridObject.Rows[ROW_BOXLEFT].Strings[0] := ' Box.Left ';
    StringGridObject.Rows[ROW_BOXTOP].Strings[0] := ' Box.Top ';
    StringGridObject.Rows[ROW_BOXRIGHT].Strings[0] := ' Box.Right ';
    StringGridObject.Rows[ROW_BOXBOTTOM].Strings[0] := ' Box.Bottom ';

    StringGridObject.Rows[ROW_NAME].Strings[1] := Entity.EntName;
    if Entity.Layer <> nil then
      StringGridObject.Rows[ROW_LAYER].Strings[1] := Entity.Layer.Name
    else
      StringGridObject.Rows[ROW_LAYER].Strings[1] := 'not set';
    if Entity.Color = clByLayer then
      StringGridObject.Rows[ROW_COLOR].Strings[1] := 'clByLayer'
    else if Entity.Color = clByBlock then
      StringGridObject.Rows[ROW_COLOR].Strings[1] := 'clByBlock'
    else
      StringGridObject.Rows[ROW_COLOR].Strings[1] := '('+IntToStr(Entity.Color and $000000FF) +', '+ IntToStr((Entity.Color shr 16) and $000000FF) + ', ' + IntToStr((Entity.Color shr 32) and $000000FF)+')';
    StringGridObject.Rows[ROW_BOXLEFT].Strings[1] := FloatToStr(Entity.Box.Left);
    StringGridObject.Rows[ROW_BOXTOP].Strings[1] := FloatToStr(Entity.Box.Top);
    StringGridObject.Rows[ROW_BOXRIGHT].Strings[1] := FloatToStr(Entity.Box.Right);
    StringGridObject.Rows[ROW_BOXBOTTOM].Strings[1] := FloatToStr(Entity.Box.Bottom);

    if Entity is TsgDXFCustomVertex then
    begin
      StringGridObject.RowCount := ROW_COUNT + 1;
      StringGridObject.Rows[ROW_COUNT].Strings[0] := ' Point ';
      StringGridObject.Rows[ROW_COUNT].Strings[1] := Format('(%.4f; %.4f; %.4f)', [TsgDXFCustomVertex(Entity).Point.X, TsgDXFCustomVertex(Entity).Point.Y, TsgDXFCustomVertex(Entity).Point.Z]);
      if (Entity is TsgDXFInsert) and (TsgDXFInsert(Entity).Attribs.Count>0) then
      begin
        Cnt := TsgDXFInsert(Entity).Attribs.Count;
        StringGridObject.RowCount := ROW_COUNT + 1 + Cnt;
        for I := 0 to Cnt - 1 do
        begin
          StringGridObject.Rows[ROW_COUNT + 1 + I].Strings[0] := ' Attrib ['+IntToStr(I)+']';
          StringGridObject.Rows[ROW_COUNT + 1 + I].Strings[1] := TsgDXFAttrib(TsgDXFInsert(Entity).Attribs[I]).Text;
          StringGridObject.Rows[ROW_COUNT + 1 + I].Objects[1] := TsgDXFInsert(Entity).Attribs[I];
        end;
      end
      else
        if Entity is TsgDXFText then
        begin
          StringGridObject.RowCount := ROW_COUNT + 1 + 1;
          if Entity.ClassType=TsgDXFAttdef then
          begin
            StringGridObject.Rows[ROW_COUNT + 1].Strings[0] := ' Tag ';
            StringGridObject.Rows[ROW_COUNT + 1].Strings[1] := TsgDXFAttdef(Entity).Tag;
          end
          else
          begin
            StringGridObject.Rows[ROW_COUNT + 1].Strings[0] := ' Text ';
            StringGridObject.Rows[ROW_COUNT + 1].Strings[1] := TsgDXFText(Entity).Text;
          end;
          StringGridObject.Rows[ROW_COUNT + 1].Objects[1] := TsgDXFText(Entity);
        end;
    end;
  end
  else
  begin
    StringGridObject.RowCount := 1;
  end;
end;

procedure TfmEntityInspector.ComboBoxSelect(Sender: TObject);
begin
  if (ComboBox.ItemIndex > 0) and (ComboBox.ItemIndex < ComboBox.Items.Count) then
    RefreshGrid(TsgDXFEntity(ComboBox.Items.Objects[ComboBox.ItemIndex]));
end;

procedure TfmEntityInspector.StringGridObjectSetEditText(Sender: TObject; ACol,
  ARow: Integer; const Value: string);
var
  s: string;
begin
  if StringGridObject.Objects[ACol,ARow] = nil then
  begin
    StringGridObject.Cells[ACol,ARow] := PrevCellText;
    Exit;
  end;
  if ComboBox.ItemIndex<0 then
  begin
    StringGridObject.Cells[ACol,ARow] := '';
    Exit;
  end;
  if Transition then
  begin
    Transition := False;
    Exit;
  end;
  if not ChangeCellText then
  begin
    Column := ACol;
    Row := ARow;
    ChangeCellText := True;
  end
  else
  begin
    s := Value;
    if Length(Value)>=256 then
    begin
      Delete(s, 256, 1);
      StringGridObject.Cells[ACol,ARow] := s;
    end;
  end;
  CellText := StringGridObject.Cells[ACol,ARow];
end;

procedure TfmEntityInspector.StringGridObjectKeyDown(Sender: TObject;
  var Key: Word; Shift: TShiftState);
begin
  if ChangeCellText and (Key=VK_RETURN) then
  begin
    ChangeObjectParams;
    ChangeCellText := False;
    Transition := True;
    PrevCellText := StringGridObject.Cells[Column,Row];//?!
  end;
end;

procedure TfmEntityInspector.StringGridObjectSelectCell(Sender: TObject; ACol,
  ARow: Integer; var CanSelect: Boolean);
begin
  if ChangeCellText then
  begin
    Transition := True;// transition to SetEditText, when SelectCell after editing
    ChangeObjectParams;
  end;
  PrevCellText := StringGridObject.Cells[ACol,ARow];
  ChangeCellText := False;
end;

procedure TfmEntityInspector.StringGridObjectExit(Sender: TObject);
begin
 if ChangeCellText then
   StringGridObject.Cells[Column,Row] := PrevCellText;
end;

procedure TfmEntityInspector.ChangeObjectParams;
var
  vEnt: TsgDXFentity;
  vObj: TObject;

  procedure ApplyEntity(AEntity: TsgDXFEntity);
  begin
    CADImage.Converter.Loads(AEntity);
    UpdateEntity(AEntity);
  end;
begin
  vEnt := TsgDXFEntity(ComboBox.Items.Objects[ComboBox.ItemIndex]);
  if vEnt is TsgDXFText then
  begin
    if vEnt.ClassType=TsgDXFAttdef then
      TsgDXFAttdef(vEnt).Tag := StringGridObject.Cells[Column, Row]
    else
      TsgDXFText(vEnt).Text := StringGridObject.Cells[Column, Row];
    ApplyEntity(vEnt);
  end;
  vObj := StringGridObject.Rows[Row].Objects[Column];
  if vObj <> nil then
  begin
    if vObj is TsgDXFText then
    begin
      TsgDXFText(vObj).Text := StringGridObject.Cells[Column, Row];
      //TsgDXFAttrib(vObj).Tag := StringGridObject.Cells[Column, Row];
      ApplyEntity(TsgDXFEntity(vObj));
    end;
  end;
end;

procedure TfmEntityInspector.Load(AImage: TsgDrawingNavigator);
var
  I: Integer;
begin
  FDNavigator := AImage;
  for I := 0 to CADImage.Converter.Counts[csEntities] - 1 do
    AddEntity(CADImage.Converter.Sections[csEntities].Entities[I], False);
end;

procedure TfmEntityInspector.Clear;
begin
  fmEntityInspector.RefreshGrid(nil);
  ComboBox.Clear;
end;

procedure TfmEntityInspector.AddEntity(AEnt: TsgDXFEntity; ARefresh: Boolean);
begin
  ComboBox.Items.AddObject(AEnt.EntName, AEnt);
  if ARefresh then
    RefreshGrid(AEnt);
end;

procedure TfmEntityInspector.SetChangeCellText(const Value: Boolean);
begin
  FChangeCellText := Value;
end;

procedure TfmEntityInspector.UpdateEntity(AEntity: TsgDXFEntity);
var
  vInvRect: TRect;
begin
  if AEntity <> nil then
  begin
    vInvRect := FDNavigator.PictureRect;
    InvalidateRect(FDNavigator.Parent.Handle, @vInvRect, False);
  end;
end;

function TfmEntityInspector.GetCADImage: TsgDXFImage;
begin
  Result := TsgDXFImage(FDNavigator.Picture.Graphic);
end;

procedure TfmEntityInspector.FormShow(Sender: TObject);
begin
  // To stay main form foreground

end;

procedure TfmEntityInspector.ComboBoxWndProc(var Message: TMessage);
begin
  case TWMCommand(Message).NotifyCode of
    CBN_CLOSEUP:
      ComboBoxSelect(ComboBox); // Dispatch ComboBox OnCloseUp event
    else
    begin
      FComboBoxWndProc(Message);
      if Message.Msg = WM_KEYDOWN then // when key down
        if TWMKey(Message).CharCode in [VK_PRIOR .. VK_DOWN] then // and if keys is arrows and grey
          ComboBoxSelect(ComboBox); // Dispatch ComboBox OnCloseUp event
    end;
  end;
end;

procedure TfmEntityInspector.SpeedButtonStayOnTopMouseUp(Sender: TObject;
  Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
  if SpeedButtonStayOnTop.Down then
    SetWindowPos(Handle, HWND_NOTOPMOST, Left, Top, Width, Height, SWP_SHOWWINDOW)
  else
    SetWindowPos(Handle, HWND_TOPMOST, Left, Top, Width, Height, SWP_SHOWWINDOW);
end;

procedure TfmEntityInspector.pnlComboResize(Sender: TObject);
begin
  SpeedButtonStayOnTop.Left := pnlCombo.Width - SpeedButtonStayOnTop.Width;
  ComboBox.Width := SpeedButtonStayOnTop.Left;
end;

end.
