DcadPlot Ver.2015.5.4 for DraftSight / ARES commander

※ARES は廉価版でも COM API (ActiveX) が使えます。そちらを使ったほうがスマートです。

■概要

DraftSight / ARES で開いている図面を一括で印刷ツールです。
図面の取得、印刷には、結構時間がかかります。
図枠に属性として、シート番号、ページ番号が設定されている場合は、その順で出力できます。

※印刷設定ファイル(PrintStyle)の取得は、システム変数 FONTMAP (フォントマップファイル)のフォルダ名から推定しています。
 システム変数 FONTMAP の値が書き変わってしまった場合は、下記を参考に設定しなおして下さい。
 DraftSight
 "C:\Program Files\Dassault Systemes\DraftSight\Fonts\fonts.fmp"
 ARES
 "C:\Program Files\Graebert GmbH\ARES Commander 2015\Fonts\fonts.fmp"
 (32bit版の時は、Program Files (x86) になります)

※印刷時間は、長めに設定して下さい。印刷中にドキュメントが切り替わると、CADがエラーで継続不能になります。
※印刷待ちに、コマンドラインをキャプチャし、画像の変化による監視を追加しました。「設定」タブにて使えるかどうか確認できます。
 コマンドラインが隠れるとうまく動きませんので、注意して下さい。
 こちらの環境では、exe のプロパティ「互換」で、「高解像度DPI...」にチェックを付ける必要がありました。

やはり、「バッチ印刷」を使ったほうが、安全で快適なのだと思います。


■履歴
2015/04/26
・初版作成
2015/04/30
・DXFファイルから属性(シート番号、ページ番号)の取得を追加
2015/05/03
・コマンド送信に、[Enter]、[ESC] を追加。取得を若干高速にした。
2015/05/04
・印刷待ちに、キャプチャ画像によるコマンドライン監視を追加。取得を若干高速にした。
・自動保存を一時的にOFFにするを追加した。



■ダウンロード
 DcadPlot.zip (2015/05/04 exe本体のみ)

■ソースコード


unit DcadPlotUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, Vcl.CheckLst,Comobj, Vcl.ExtCtrls, Vcl.Grids, IniFiles, System.UITypes,
  Vcl.ComCtrls, Vcl.Buttons, ClipBrd, Imm, Printers, Winspool, System.IOUtils, System.Types;

type
  DcadPoint = array [0..2] of double;

type
  TForm5 = class(TForm)
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    Button1: TButton;
    SpeedButton4: TSpeedButton;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    GroupBox1: TGroupBox;
    Label1: TLabel;
    Label2: TLabel;
    Label4: TLabel;
    Label5: TLabel;
    ComboBox1: TComboBox;
    ComboBox2: TComboBox;
    Button2: TButton;
    Button4: TButton;
    Label3: TLabel;
    Edit1: TEdit;
    StringGrid1: TStringGrid;
    ProgressBar1: TProgressBar;
    Label6: TLabel;
    ComboBox3: TComboBox;
    Label8: TLabel;
    SpeedButton9: TSpeedButton;
    Label17: TLabel;
    SpeedButton3: TSpeedButton;
    TabSheet2: TTabSheet;
    GroupBox2: TGroupBox;
    Label7: TLabel;
    Edit2: TEdit;
    Label9: TLabel;
    Label10: TLabel;
    Edit3: TEdit;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Edit4: TEdit;
    Label15: TLabel;
    GroupBox3: TGroupBox;
    Label16: TLabel;
    Edit5: TEdit;
    Edit6: TEdit;
    Label18: TLabel;
    Edit7: TEdit;
    Edit8: TEdit;
    Label19: TLabel;
    Label20: TLabel;
    SpeedButton5: TSpeedButton;
    Image1: TImage;
    SpeedButton6: TSpeedButton;
    Image2: TImage;
    CheckBox1: TCheckBox;
    Label21: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
    procedure StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer);
    procedure StringGrid1KeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure SpeedButton1Click(Sender: TObject);
    procedure SpeedButton2Click(Sender: TObject);
    procedure ComboBox1Change(Sender: TObject);
    procedure ComboBox2Change(Sender: TObject);
    procedure SpeedButton4Click(Sender: TObject);
    procedure StringGrid1Click(Sender: TObject);
    procedure SpeedButton9Click(Sender: TObject);
    procedure SpeedButton3Click(Sender: TObject);
    procedure PageControl1Change(Sender: TObject);
    procedure SpeedButton5Click(Sender: TObject);
    procedure SpeedButton6Click(Sender: TObject);

  private
    { Private 宣言 }
    LastPrinter, LastCtbStb :string;
    MbRow, MbCol : integer;
  public
    { Public 宣言 }
    RunFlag : boolean;

    procedure DispCheckedCount;
  end;

var
  Form5: TForm5;


  // メインウィンドウ
  DcadMainWinHandle : HWND;
  DcadMDIClientWinHandle : HWND;
  DcadCommandWinHandle : HWND;
  DcadInputWinHandle : HWND;
  DcadOutputWinHandle : HWND;
  DcadMDIActiveWinHandle : HWND;
  DcadMDIChildWinHandle : HWND;
  DCadProcessID : DWORD;     //プロセスID

  SleepDocChg, SleepPrint, SleepSysVar : integer;

// *****************************
// プリンター用紙名を取得
// *****************************
procedure GetPrinterPaperNames(iIndex :integer; sl: TStrings);

implementation

{$R *.dfm}
//****************************************
// 画面の指定位置をBitmapに変換
//****************************************
procedure CaptureToBmp(Lf, Tp, W, H: Integer; bmp: TBitmap);
const
  CAPTUREBLT = $40000000;
var
  hdcScreen : HDC;
begin
  bmp.Width := W;
  bmp.Height := H;
  hdcScreen := CreateDC('DISPLAY', nil, nil, nil);
  try
    BitBlt( bmp.Canvas.Handle, 0, 0, W, H, hdcScreen, Lf, Tp, SRCCOPY or CAPTUREBLT);
  finally
    DeleteDC(hdcScreen);
  end;
end;
//****************************************
// 最後の指定文字列より後を得る
//****************************************
function LastSubstringAfter(const ststr, s: string):String;
var
  st, stlen : integer;
  temp : string;
begin
  Result := '';
  if Pos(ststr, s) > 0 then begin
    temp := s;

    stlen := Length(ststr);
    while Pos(ststr, temp) > 0 do begin
      st := Pos(ststr, temp) + stlen;
      temp := Copy(temp, st, Length(temp) - st + 1);
      if temp = '' then break;
    end;
    Result := Trim(temp);
  end;
end;

//****************************************
//最初に現れたststrからedstrまでを返す
//****************************************
function StrAfterStrBefore(const ststr, edstr, s:string):String;
var
  st, ed:integer;
  temp : string;
begin
  Result:='';
  st:=Pos(ststr,s);
  if st > 0 then begin
    temp := s;
    Delete(temp,1,st + Length(ststr)-1);
    ed := Pos(edstr, temp)-1;
    if ed >= 0 then
      Result := Trim(Copy(temp, 1, ed));
  end;
end;

//****************************************
// 実際には、MDIClientではない
//****************************************
function EnumCWinProc_MDIClient(h:HWND;lparam:Integer):Bool;stdcall;
var
  Title : array [0..255] of char;
begin
  Result:=True;
  if GetWindowText(h, Title, 255) <> 0 then begin
    if 'CFxWorkspaceImplClassWindow' = Title then begin
      DcadMDIClientWinHandle := GetWindow(h, GW_CHILD);
      DcadMDIChildWinHandle := GetWindow(DcadMDIClientWinHandle, GW_CHILD);
      Result := False;
    end;
  end;
end;

//****************************************
// 実際には、MDIClientではない
//****************************************
function GetDcadMDIClientHandle:HWND;
begin
  EnumChildWindows(DcadMainWinHandle,@EnumCwinProc_MDIClient, 0);
  result:=DcadMDIClientWinHandle;
end;
//****************************************
// コマンドウィンドウ
//****************************************
function EnumCWinProc_CommandWinHandle(h: HWND; lparam: Integer):Bool;stdcall;
var
  Title : array [0..255] of char;
begin
  Result:=True;
  if GetWindowText(h, Title, 255) <> 0 then begin
    if 'CFxCommandWindowWindow' = Title then begin
      DcadCommandWinHandle := h;
      Result:=False;
    end;
  end;
end;

//****************************************
// コマンドウィンドウ Input/OutputWindow
//****************************************
function GetDcadCommandWinHandle:HWND;
begin
  EnumChildWindows(DcadMainWinHandle, @EnumCwinProc_CommandWinHandle, 0);
  result := DcadCommandWinHandle;
  if result <> 0 then begin
    DcadOutputWinHandle := GetWindow(DcadCommandWinHandle, GW_CHILD);
    DcadInputWinHandle  := GetWindow(DcadOutputWinHandle, GW_HWNDNEXT);
  end;
end;

//****************************************
// メインウィンドウ
//****************************************
function EnumWindowProcMainWin(h: HWND; lp: LParam): BOOL; stdcall;
var
  Title : array [0..255] of char;
  ClassName : array [0..255] of char;
begin
  Result := true;
  GetClassName(h, ClassName, 255);
  if 'Qt5QWindowIcon' = ClassName then begin
    // タイトルを得る
    if GetWindowText(h, Title, 255) <> 0 then begin
      if (Pos('DraftSight - [', Title) = 1) or
        (Pos('ARES Commander 2015 - [', Title) = 1) then begin
        DCadMainWinHandle := h;
        GetWindowThreadProcessId(h, @DCadProcessID);
        Result := false;
      end;
    end;
  end;
end;

//****************************************
// メインウィンドウのハンドルを得る
//****************************************
function GetDCadMainWinHandle : HWND;
begin

  EnumWindows(@EnumWindowProcMainWin, 0);
  Result := DCadMainWinHandle;
end;
//****************************************
// 文字列送信
//****************************************
function SendHwndCmdLine(hCmdLine: HWND; const cmd : string):boolean;
var
  i : integer;
begin
  Result := False;
  if hCmdLine <> 0 then begin
    // 文字列を送信
    for i := 1 to Length(cmd) do begin
      if cmd[i] = #13 then begin
        SendMessage(hCmdLine,WM_KEYDOWN,VK_RETURN,0);
        SendMessage(hCmdLine,WM_KEYUP,VK_RETURN,0);
        Sleep(1);
      end
      else if (cmd[i] = #27) or (cmd[i] = #3) then begin
        SendMessage(hCmdLine,WM_KEYDOWN,VK_ESCAPE,0);
        SendMessage(hCmdLine,WM_KEYUP,VK_ESCAPE,0);
        Sleep(1);
      end
      else begin
        SendMessage(hCmdLine, WM_CHAR, Word(cmd[i]), 0);
        if cmd[i] = #20 then Sleep(1);
      end;
    end;
    result := true;
  end;
end;

//******************************************
// ウィンドウのタイトル(キャプション)を得る
//******************************************
function GetWindowCaption(h : HWND) : string;
var
  Title : array [0..255] of char;
begin
  result := '';
  if GetWindowText(h, Title, 255) <> 0 then
    result := Title;
end;

//****************************************
// コマンド送信
//****************************************
function SendDcadCommand(const cmd: string): boolean;
begin
  GetDcadCommandWinHandle;
  Result := SendHwndCmdLine(DcadInputWinHandle, cmd);
end;

//****************************************
// システム変数取得
//****************************************
function GetDcadVariable(const SysVar: string): string;
var
  s : string;
begin
  Result := '';

  GetDCadMainWinHandle;

  SetForegroundWindow(DcadMainWinHandle);
  SendDcadCommand(SysVar + #13#3);

  Sleep(10);

  SendDcadCommand('COPYHISTORY'#13);
  Sleep(200);
  s := '';
  try
    s := ClipBoard.AsText;
    s := LastSubstringAfter(': ' + SysVar, s);

    Result := '';
    if (Pos('=', s) > 0) and (Pos('(', s) > 1) then
      Result := Trim(StrAfterStrBefore('=', '(', s));
    if (Result = '') and (Pos('(', s) > 0) and (Pos(')', s) > 1) then
      Result := Trim(StrAfterStrBefore('(', ')', s));
    if (Result = '') and (Pos('デフォルト:', s)>0) then begin
      Result := Trim(StrAfterStrBefore('デフォルト:', #13, s));
    end;
  except
    ;
  end;
end;

//****************************************
// DraftSight / ARES commander をアクティブに
//****************************************
function SetDcadActive: boolean;
begin
  Result := False;
  if GetDCadMainWinHandle <> 0 then begin
    SetForegroundWindow(DcadMainWinHandle);
    Result := True;
  end;
end;
function WaitForDcadCommandReady(timeout: integer): boolean;
var
  cnt : integer;
  ARect : TRect;
  AHeight,ATop : integer;
  Bmp : TBitMap;
  co : Integer;
  i,j: integer;
  pByte :PByteArray;
  Flag : Boolean;
  msec : integer;
  n : integer;
begin
  Result := False;
  msec := 10;

  GetDcadMainWinHandle;
  GetDcadCommandWinHandle;
  //Form5.Caption := IntToHex(DcadInputWinHandle, 8);
  GetWindowRect(DcadInputWinHandle, ARect);
  ATop := ARect.Top;

  AHeight := ARect.Bottom - ARect.Top;
  ATop := ATop+ AHeight -20;

  //AWidth := ARect.Right - Arect.Left;
  //Form5.Caption := IntToStr(AHeight);
  Bmp := TBitmap.Create;
  try
    Bmp.Width := 100;
    Bmp.Height := 20;
    Bmp.PixelFormat :=pf24bit;

    n := 0;
    cnt := 0;
    while True do begin
      Sleep(msec);
      Application.ProcessMessages;
      CaptureToBmp(Arect.Left + 18, ATop, Bmp.Width, Bmp.Height, bmp);
      Form5.Image1.Picture.Assign(bmp);
      pByte := Bmp.ScanLine[0];
      co := pByte[0];
      Flag := True;
      for i := 1 to Bmp.Height - 1 do begin
        pByte := Bmp.ScanLine[i];
        for j := 0 to Bmp.Width -1 do begin
          if co <> pByte[j] then begin
            Flag := False;
            n := 0;
            Break;
          end;
        end;
      end;
      if Flag then Inc(n);
      if n > 3 then begin
        Result := True;
        Break;
      end;

      Inc(cnt);
      if cnt * msec > timeout then Break;
    end;
  finally
    Bmp.Free;
  end;

end;

//****************************************
// 座標文字列を3D座標に
//****************************************
procedure StrPointToDcadPoint(const StrPoint: string; var pt: DcadPoint);
var
  n : integer;
  s: string;
begin
  s := StrPoint;
  n := Pos(',', s);
  pt[0] := StrToFloatDef(Copy(s, 1, n - 1), 0);

  s := Copy(s, n + 1);

  n := Pos(',', s);
  if n > 0 then begin
    pt[1] := StrToFloatDef(Copy(s, 1, n - 1), 0);
    pt[2] := StrToFloatDef(Copy(s, n + 1), 0);
  end
  else begin
    pt[1] := StrToFloatDef(s, 0);
    pt[2] := 0;
  end;
end;
// ************************************
// DXFファイルからシステム変数(2D座標)を取得する
// ************************************
function ReadDxfVariablePoint2DString(dxfSl: TStrings; const sysName: string):string;
var
  idx, i : integer;
  code : integer;
  s : string;
  dxfSys : string;
begin
  Result := '';

  dxfSys := '$' + Uppercase(sysName);

  idx := dxfSL.IndexOf('ENTITIES');
  for i := 0 to idx div 2 do begin
    code := StrToInt(dxfSl[i *2]);
    s := dxfSl[i * 2 + 1];
    if (code = 9) and (s = dxfSys) then begin
      result := dxfSl[(i + 1) * 2 + 1]  + ',' +  dxfSl[(i + 2) * 2 + 1];
      break;
    end;
  end;
end;

// ************************************
// DXFファイルから属性を取得する
// ************************************
function ReadDxfAttRib(dxfSl: TStrings; const InsertName: string; const AttRibName: string): string;
var
  i : integer;
  insFlag, attFlag, hasAtt : boolean;
  s : string;
  AblkName , AattName, AattString : string;
  code, idx : integer;
begin
  Result := '';
  insFlag := False;
  hasAtt  := False;
  attFlag := False;

  // ENTITIESセクションを探す
  idx := dxfsl.IndexOf('ENTITIES');

  for i := idx div 2 + 1 to dxfsl.Count div 2 - 1 do begin
    // DXFコード
    code := StrToInt(dxfsl[i * 2]);
    // その値
    s := dxfsl[i * 2 + 1];

    if insFlag then begin

      // 属性取得終了
      if hasAtt and (code = 0) and (s = 'SEQEND') then begin
        insFlag := False;
        hasAtt  := False;
        attFlag := false;
      end;

      if hasAtt and (AblkName = '') and (code = 2) then
        AblkName := s;

      if attFlag then begin
        // 属性の値を保持
        if code = 1 then
          AattString := s;
        // 属性名
        if code = 2 then begin
          AattName := s;
          // ブロック名、属性名が同じ
          if (InsertName = ABlkName) and (AttRibName = AattName) then begin
            Result := AattString;
            Break;
          end;
        end;
      end;
      if hasAtt and (code = 0) and (s = 'ATTRIB') then
        attFlag := True;
    end;

    if (code = 0) and (s = 'INSERT') then begin
      insFlag := True;
      hasAtt  := False;
      attFlag := False;
      AblkName := '';
    end;
    // 属性有
    if (code = 66) and (Trim(s) = '1') then
      hasAtt := True;
    // ENTITIES セクション終わり
    if (code = 0) and (s = 'ENDSEC') then
      Break;
  end;

end;

// ************************************
// DXFファイルを作成する
// ************************************
function SaveDxfSl(dxfsl : TStringList):boolean;
var
  fname : TFileName;
  cnt : integer;
begin
  Result := False;
  fname := ChangeFileExt(ParamStr(0), '.dxf');

  DeleteFile(fname);

  cnt := 0;
  while True do begin
    Sleep(10);
    if not FileExists(fname) then begin
      Break;
    end;
    Inc(cnt);
    if cnt > 5 then Break;
  end;

  if SetDcadActive then begin
    SendDcadCommand('FILEDIA'#13'0'#13);
    Sleep(10);

    SendDcadCommand('DXFOUT'#13 + fname +#13'Vesion'#13'R18'#13'16'#13);
    Sleep(10);
    SendDcadCommand('''FILEDIA'#13'1'#13);
    cnt := 0;

    // ファイル作成待ち
    while True do begin
      Sleep(10);
      if FileExists(fname) then begin
        Sleep(10);
        Break;
      end;
      Inc(cnt);
      if cnt > 50 then Break;
    end;
    if FileExists(fname) then begin
      dxfsl.LoadFromFile(fname);
      Result := dxfsl.Count > 0;
    end;
  end;
end;

// *****************************
// 印刷チェック数をカウント
// *****************************
procedure TForm5.DispCheckedCount;
var
  i, cnt : integer;
begin
  cnt := 0;
  with StringGrid1 do begin
    for i := 1 to RowCount - 1 do begin
      if (Cells[1, i] <> '') and Bool(Objects[1, i]) then Inc(cnt);
    end;
  end;

  // 印刷ボタン
  Button2.Enabled := cnt > 0;

  Label5.Caption := IntToStr(cnt);
end;

// *****************************
// StringGrid でのキー操作
// *****************************
procedure SgKeyDown(SG: TSTringGrid; var Key: Word; Shift:TShiftState);
var
  i, j, k, n : integer;
  sl : TStringList;
  s, s1 : string;
  xflag : boolean;
begin
  if Key = VK_DELETE then begin
    with SG do begin
      if (Selection.Top <> Selection.Bottom) or
         (Selection.Left <> Selection.Right) then begin
        Key := 0;
        for i := Selection.Top to Selection.Bottom do begin
          for j := Selection.Left to Selection.Right do begin
            Cells[j, i] := '';
          end;
        end;
      end;
    end;
  end;
  if ssCtrl in Shift then begin

    if true then begin
      xflag := (Key = Ord('X')) or (Key = Ord('x'));
      if (Key = Ord('C')) or (Key = Ord('c')) or xflag then begin

        Key := 0;
        Clipboard.AsText := '';
        with SG do begin
          for i := Selection.Top to Selection.Bottom do begin
            for j := Selection.Left to Selection.Right do begin
              Clipboard.AsText := Clipboard.AsText + Cells[j, i];
              if j < Selection.Right then Clipboard.AsText := Clipboard.AsText + #9
              else Clipboard.AsText := Clipboard.AsText + #13#10;
            end;
          end;
          if xflag then begin
            for i := Selection.Top to Selection.Bottom do begin
              for j := Selection.Left to Selection.Right do begin
                Cells[j, i] := '';
              end;
            end;
          end;
        end;
      end
      else if (Key = Ord('V')) or (Key = Ord('v')) then begin
        //with SG do
        //  if EditorMode then EditorMode := false;
        Key := 0;
        with SG do begin
          sl := TStringList.Create;
          try
            s := Clipboard.AsText;
            while true do begin
              k := Pos(#13#10, s);
              if k = 0 then break
              else begin
                sl.Add(Copy(s, 1, k - 1));
                Delete(s, 1, k + 1);
              end;
            end;
            for i := 0 to sl.Count-1 do begin
              s := SL[i];
              j := 0;
              while true do begin
                k := Pos(#9, s);
                if k = 0 then begin
                  s1 := Copy(s, 1, Length(s));
                end
                else begin
                  s1 := Copy(s, 1, k - 1);
                  Delete(s, 1, k);
                end;
                Cells[Selection.Left + j,Selection.Top + i] := s1;
                n := 1;
                while true do begin
                  if Selection.Bottom < Selection.Top + i + (sl.Count * n) then
                    break
                  else begin
                    Cells[Selection.Left + j, Selection.Top + i + (sl.Count * n)] := s1;
                  end;
                  Inc(n);
                end;
                if k = 0 then break;
                Inc(j);
              end;
            end;
          finally
            sl.Free;
          end;
        end;
      end;
    end;
  end;
end;


// *****************************
// StringGrid の Col の値でソート
// *****************************
procedure SgSortByCol2(sg : TStringGrid; col1, col2 :integer; NumFlag:boolean);
var
  i, j : integer;
  sl, sltemp : TStringList;
  s1, s0 : string;
begin
  // ソート
  sl := TStringList.Create;
  try
    sltemp := TStringList.Create;
    try
      with sg do begin
        for i := 1 to RowCount -2 do begin
          s0 := '';
          if col1 >= 0 then s0 := s0 + Cells[col1, i];
          if col2 >= 0 then s0 := s0 + Cells[col2, i];
          for j := i + 1 to RowCount -1 do begin
            s1 := '';
            if col1 >= 0 then s1 := s1 + Cells[col1, j];
            if col2 >= 0 then s1 := s1 + Cells[col2, j];

            if (not NumFlag and (s0 > s1)) or
               (NumFlag and (StrToIntDef(s0, 0) > StrToIntDef(s1, 0))) then begin
              slTemp.Assign(Rows[i]);
              Rows[i] := Rows[j];
              Rows[j] := slTemp;
              s0 := s1;
            end;
          end;
        end;
      end;
    finally
      slTemp.Free;
    end;
  finally
    sl.Free;
  end;
end;

// *****************************
// 取得
// *****************************
procedure TForm5.Button1Click(Sender: TObject);
var
  i : integer;
  sdir : string;
  cnt : integer;

  j : integer;
  s :string;
  arycnt : integer;
  dwgname , dwgprefix :string;
  limmax, limmin : DcadPoint;
  h, hdwg : HWND;
  dwgTitle, ext : string;

  fnames: TStringDynArray;
  fpath, fname : TFileName;
  sl : TStringList;
  Title : string;
  count : integer;
begin
  Title := '';
  // メニューを操作して図面を切り替えるため、
  // 一度図面を切り替えて、そのファイル名を取得して記憶する

  // メインウィンドウのハンドルを取得
  if GetDCadMainWinHandle = 0 then Exit;

  // 最小化されていれば戻す
  if isIconic(DcadMainWinHandle) then begin
    OpenIcon(DcadMainWinHandle);
    // 画面描画待ち
    Sleep(SleepDocChg);
  end;

  // MDI のウィンドウを探す
  GetDcadMDIClientHandle;
  h := DcadMDIChildWinHandle;
  cnt := 0;

  // ここでは、ドキュメントの数を数えるだけ
  while h <> 0 do begin
    hdwg := GetWindow(h, GW_CHILD);
    dwgTitle := GetWindowCaption(hdwg);
    // ファイル名を取得
    ext := Uppercase(ExtRactFileExt(dwgTitle));

    // 念のため、拡張子を比較
    if (ext = '.DWG') or (ext = '.DWG*') then
      Inc(cnt);

    // 次のウィンドウを探す
    h := GetWindow(h, GW_HWNDNEXT);
  end;
  if cnt = 0 then Exit;

  SendDcadCommand(#3'SAVETIME'#13'0'#13);

  sdir := GetDcadVariable('DWGPREFIX');
  Edit1.Text := sdir;

  arycnt := cnt;

  if arycnt > 0 then begin
    with Progressbar1 do begin
      Max := arycnt;
      Position := 0;
    end;

    with StringGrid1 do begin
      RowCount := arycnt + 1;

      for i := 1 to RowCount -1 do
        for j := 0 to ColCount -1 do
          Cells[j, i] := '';
    end;

    cnt := 0;
    sl := TStringList.Create;
    try

      // メニューを操作
      for i := 0 to arycnt - 1 do begin
        // CADをアクティブに
        SetForegroundWindow(DcadMainWinHandle);

        keybd_event(VK_MENU, 0, 0, 0);
        keybd_event(Ord('W'), 0, 0, 0);
        keybd_event(Ord('W'), 0, KEYEVENTF_KEYUP, 0);
        keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

        for j := 0 to i + 4 do begin
          keybd_event(VK_DOWN, 0 ,0 ,0);
          keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
        end;

        keybd_event(VK_RETURN, 0, 0, 0);
        keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

        count := 0;
        while True do begin
          Sleep(10);
          if Title <> GetWindowCaption(DcadMainWinHandle) then begin
            Break;
          end;
          Inc(count);
          if Count > 60 then Break;
        end;

        // 図面切り替わり待ち
        Sleep((count + 2) * 50);

        // 編集の図面名を取得
        s := GetWindowCaption(DcadMainWinHandle);
        Title := s;

        dwgname := StrAfterStrBefore('[', ']', s);
        dwgprefix := GetDcadVariable('DWGPREFIX');

        // 同じフォルダ名の図面のみを取得
        if sdir = dwgprefix then begin
          with StringGrid1 do begin
            Cells[0, cnt + 1] := IntToStr(i + 1);
            Cells[1, cnt + 1] := dwgname;
            if SaveDxfSL(sl) then begin

              // シート番号を取得
              s := ReadDxfAttRib(sl, Edit5.Text, Edit6.Text);
              Cells[7, cnt + 1] := s;
              //ページ番号を取得
              s := ReadDxfAttRib(sl, Edit7.Text, Edit8.Text);
              Cells[8, cnt + 1] := s;
              s := ReadDxfVariablePoint2DString(sl, 'LIMMAX');
              Cells[9, cnt + 1] := s;
              StrPointToDcadPoint(s, limmax);

              s := ReadDxfVariablePoint2DString(sl, 'LIMMIN');
              Cells[10, cnt + 1] := s;
              StrPointToDcadPoint(s, limmin);

              Cells[2, cnt + 1] := Format('%.1f',[limmax[0] - limmin[0]]);
              Cells[3, cnt + 1] := Format('%.1f',[limmax[1] - limmin[1]]);
              // ScaleA3
              Cells[4, cnt + 1] := Format('%.3f',[limmax[0] / 420]);
              // ScaleA4
              Cells[5, cnt + 1] := Format('%.3f',[limmax[0] / 297]);
              Objects[1, cnt + 1] := TObject(True);
            end;
          end;
          with Progressbar1 do Position := Position + 1;
          Inc(cnt);
        end;
      end;
    finally
      sl.Free;
    end;

    with StringGrid1 do begin
      RowCount := cnt + 1;
      Row := 1;
      Col := 2;
      SetFocus;
    end;

    label6.Caption := '/' + cnt.ToString;
    Progressbar1.Position := 0;

    // プリンター名一覧を Windows から取得
    ComboBox1.Items.Assign(Printer.Printers);
    with ComboBox1 do begin
      if LastPrinter <> '' then begin
        for i := 0 to Items.Count - 1  do begin
          if Items[i] = LastPrinter then begin
            ItemIndex := i;
            Break;
          end;
        end;
      end
      else begin
        if Items.Count > 0 then
          ItemIndex := 0
        else
          ItemIndex := - 1;
      end;
    end;

    // 用紙名一覧を Windows から取得
    GetPrinterPaperNames(ComboBox1.ItemIndex, ComboBox3.Items);
    with ComboBox3 do begin
      for i := 0 to Items.Count - 1 do begin
        if Pos('A4', Items[i]) > 0 then begin
          ItemIndex := i;
          break;
        end;
      end;
    end;

    Sleep(SleepSysVar);

    // PrintStyle をDraftShight / ARES commander のインストールフォルダから取得
    // フォントマップファイルの保存先を取得
    s := GetDcadVariable('FONTMAP');
    fpath := ExtractFilePath(ExtractFileDir(s)) + 'Default Files\Print Styles\';
    if DirectoryExists(fpath) then begin
      fnames := TDirectory.GetFiles(fpath, '*.?tb', TSearchOption.soTopDirectoryOnly);
      with ComboBox2 do begin
        Items.Clear;
        for fname in fnames do
          Items.Add(ExtractFileName(fname));
        ItemIndex := Items.IndexOf(LastCtbStb);
        if (ItemIndex < 0) and (Items.Count > 0) then
          ItemIndex := 0;
      end;
    end;
  end;
  SendDcadCommand(#3'SAVETIME'#13'10'#13);

  DispCheckedCount;
  // シート番号順
  SpeedButton3Click(self);
end;


// *****************************
// プリンター用紙名を取得
// *****************************
procedure GetPrinterPaperNames(iIndex :integer; sl: TStrings);
type
  //用紙名リスト用.用紙名の文字数の最大は64
  TPaperName = array [0..63] of Char;
var
  ADevice      : array [0..MAX_PATH-1] of Char;
  ADriver      : array [0..MAX_PATH-1] of Char;
  APort        : array [0..MAX_PATH-1] of Char;
  ADeviceMode  : THandle;
  Count        : Integer;
  PaperNames   : array of TPaperName;
  i            : Integer;
begin

  sl.Clear;

  //選択したプリンタを現在のプリンタとする
  Printer.PrinterIndex := iIndex;

  //現在のプリンタに関する情報を取り出す
  Printer.GetPrinter(ADevice, ADriver, APort, ADeviceMode);

  //そのプリンタADeviceのAPortの用紙名の数を取得
  Count := Winspool.DeviceCapabilities(ADevice, APort, DC_PAPERNAMES, nil, nil);

  //その分だけ用紙名配列の長さと用紙番号の配列の長さを確保
  SetLength(PaperNames, Count);

  //その配列に用紙名と用紙番号を取得
  Winspool.DeviceCapabilities(ADevice, APort, DC_PAPERNAMES, PChar(PaperNames), nil);

  //用紙名
  for i := 0 to Count - 1 do sl.Add(String(PaperNames[i]));
end;
// *****************************
// 印刷実行
// *****************************
procedure TForm5.Button2Click(Sender: TObject);
var
  i, j : integer;
  scale : string;
  papersize: string;
  idx : integer;
  devname : string;
  sl : TStringList;
  fname : TFileName;
begin
  fname := ChangeFileExt(ParamStr(0), '.scr');
  LastCtbStb := 'monochrome.ctb';

  if MessageDlg('選択ファイルを印刷しますか?', mtInformation,mbYesNo,0) = mrYes then begin
    // 自動保存をOFF
    SendDcadCommand(#3'SAVETIME'#13'0'#13);

    with ComboBox1 do begin
      if ItemIndex >= 0 then
        LastPrinter := Items[ItemIndex]
      else
        LastPrinter := '';
    end;

    with ComboBox3 do begin
      if ItemIndex >= 0 then
        papersize := Items[ItemIndex]
      else
        papersize := 'A4';
    end;

    with ComboBox2 do begin
      if ItemIndex >= 0 then
        LastCtbStb := Items[ItemIndex]
      else
        LastCtbStb := '';
    end;

    if (LastPrinter <> '') and (LastCtbStb <> '') then begin
      sl := TStringList.Create;
      try

        devname := LastPrinter;

        RunFlag := True;

        Button2.Enabled := False;
        Button4.Enabled := True;

        if True then begin

          with StringGrid1 do begin
            ProgressBar1.Max := RowCount - 1;
            ProgressBar1.Position := 0;
            for i := 1 to RowCount - 1 do begin
              // A3 -> A4 縮小
              if Pos('A4', papersize) > 0 then
                scale := '1=' + Format('%.3f',[StrToFloat(Cells[5, i])])
              // A3 -> A3
              else
                // papersize := 'A3';
                scale := '1=' + Format('%.3f',[StrToFloat(Cells[4, i])]);

              with Progressbar1 do Position  := Position + 1;

              Application.ProcessMessages;

              if RunFlag and Bool(Objects[1, i]) then begin
                idx := StrToInt(Cells[0, i]) - 1;

                if GetDCadMainWinHandle <> 0 then begin

                  SetForegroundWindow(DcadMainWinHandle);

                  // メニュー操作
                  keybd_event(VK_MENU, 0, 0, 0);
                  keybd_event(Ord('W'), 0, 0, 0);
                  keybd_event(Ord('W'), 0, KEYEVENTF_KEYUP, 0);
                  keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

                  for j := 0 to idx + 4 do begin
                    keybd_event(VK_DOWN, 0 ,0 ,0);
                    keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
                  end;

                  keybd_event(VK_RETURN, 0, 0, 0);
                  keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);

                  // 図面切り替わり待ち
                  Sleep(SleepDocChg);

                  // スクリプトファイル作成
                  with sl do begin
                    Clear;

                    // コマンドラインプロット
                    Add('-PLOT');
                    // 印刷オプションの詳細設定?
                    Add('Y');
                    // シート名を指定»
                    Add('Model');
                    // プリンタ名を指定»
                    Add(devname);
                    Sleep(SleepSysVar);
                    // ペーパー サイズを指定»
                    Add(papersize);
                    //  単位を指定» インチ(I) または ミリメートル(M)
                    Add('M');
                    // 方向を指定>>縦(P) または 横(L)
                    Add('L');
                    // 上下を逆にして印刷しますか?
                    Add('Y');
                    // 印刷範囲を指定»
                    Add('S');
                    // 始点コーナーを指定»
                    Add(Cells[10, i]);
                    // 反対側のコーナーを指定»
                    Add(Cells[9, i]);
                    // 印刷尺度を指定»
                    Add(scale);
                    // 印刷 X,Y オフセットを指定»
                    Add('C');
                    // 印刷スタイル テーブルを使用?
                    Add('Y');
                    // 印刷スタイル名»
                    Add(LastCtbStb);
                    // 割り当てられた線幅を使用?
                    Add('Y');
                    // オプション指定»
                    // 表示どおり(D), 隠線(H), レンダリング(R) または ワイヤフレーム(W)
                    Add('D');
                    // ファイルに出力?
                    Add('N');
                    // 印刷設定をシートに適用?
                    Add('Y');
                    // 今すぐ印刷しますか?
                    Add('Y');
                    Add('FILEDIA 1');

                    SaveToFile(fname);
                    if FileExists(fname) then begin
                      SendDcadCommand('FILEDIA'#13'0'#13);
                      Sleep(10);
                      // スクリプト読込
                      SendDcadCommand('LOADSCRIPT'#13 + fname + #13);
                    end;
                    SetForeGroundWindow(DcadMainWinHandle);
                    // 印刷終了待ち
                    if SleepPrint < 5000 then SleepPrint := 5000;
                    if CheckBox1.Checked then begin
                      WaitForDcadCommandReady(SleepPrint);
                      Sleep(100);
                    end
                    else
                      Sleep(SleepPrint);
                  end;
                end;
              end;
            end;
          end;
        end;

        ProgressBar1.Position := 0;
        if RunFlag then
          ShowMessage('印刷が終了しました.')
        else
          ShowMessage('印刷を中止しました.');

        Button2.Enabled := True;
        Button4.Enabled := False;
      finally
        sl.Free;
      end;
    end;
    SendDcadCommand(#3'SAVETIME'#13'10'#13);
  end;
end;

// *****************************
// 中止ボタン
// *****************************
procedure TForm5.Button4Click(Sender: TObject);
begin
  RunFlag := False;
  Application.ProcessMessages;
end;


// *****************************
// プリンター変更
// *****************************
procedure TForm5.ComboBox1Change(Sender: TObject);
var
  i : integer;
begin
  with ComboBox1 do begin
    if ItemIndex >= 0 then
      LastPrinter := Items[ItemIndex];
  end;
  GetPrinterPaperNames(ComboBox1.ItemIndex, ComboBox3.Items);
  ComboBox3.Sorted := True;

  with ComboBox3 do begin
    for i := 0 to Items.Count - 1 do begin
      if Pos('A4', Items[i]) > 0 then begin
        ItemIndex := i;
        break;
      end;
    end;
  end;
end;

// *****************************
// 印刷設定変更
// *****************************
procedure TForm5.ComboBox2Change(Sender: TObject);
begin
  with ComboBox2 do begin
    if ItemIndex >= 0 then
      LastCtbStb := Items[ItemIndex];
  end;
end;


// *****************************
// フォーム作成
// *****************************
procedure TForm5.FormCreate(Sender: TObject);
var
  ini : TIniFile;
begin
  PageControl1.ActivePageIndex := 0;

  Edit1.Text := '';
  //Caption := Application.Title;

  with StringGrid1 do begin
    RowCount := 2;
    ColCount := 11;

    ColWidths[0] := 30;
    ColWidths[1] := 230;
    ColWidths[2] := 50;
    ColWidths[3] := 50;
    ColWidths[4] := 50;
    ColWidths[5] := 50;
    ColWidths[6] := 0;
    ColWidths[7] := 50;
    ColWidths[8] := 50;
    ColWidths[9] := -1;
    ColWidths[10] := -1;

    Cells[0, 0] := 'No.';
    Cells[1, 0] := 'ファイル名';
    Cells[2, 0] := 'Lim W';
    Cells[3, 0] := 'Lim H';
    Cells[4, 0] := 'at A3';
    Cells[5, 0] := 'at A4';
    Cells[6, 0] := 'FilePath';
    Cells[7, 0] := 'SHEET';
    Cells[8, 0] := 'PAGE';
    Cells[9, 0] := 'LimMax';
    Cells[10, 0] := 'LimMin';

  end;

  SleepDocChg := 500;
  SleepPrint := 5000;
  SleepSysVar := 100;

  ini := TIniFile.Create(ChangeFileExt(ParamStr(0), 'ini'));
  with ini do begin
    try
      LastPrinter := ReadString('Plot', 'LastPrinter', '');
      LastCtbStb  := ReadString('Plot', 'LastCtbStb', '');

      with StringGrid1 do
        ColWidths[1] := ReadInteger('Form', 'FNameWidth', ColWidths[1] );

      SleepDocChg := ReadInteger('Timer', 'SleepDocChg', SleepDocChg);
      SleepPrint := ReadInteger('Timer', 'SleepPrint', SleepPrint);
      SleepSysVar := ReadInteger('Timer', 'SleepSysVar', SleepSysVar);

      Edit5.Text := ReadString('SheetAttRib', 'BlockName', Edit5.Text);
      Edit6.Text := ReadString('SheetAttRib', 'AttName',   Edit6.Text);

      Edit7.Text := ReadString('PageNoAttRib', 'BlockName', Edit7.Text);
      Edit8.Text := ReadString('PageNoAttRib', 'AttName',   Edit8.Text);
      CheckBox1.Checked := ReadBool('CheckBox', 'Uses CmdCapt', CheckBox1.Checked);


    finally
      Free;
    end;
  end;

  // 待ちタイマー設定
  Edit2.Text := SleepDocChg.ToString;
  Edit3.Text := SleepPrint.ToString;
  Edit4.Text := SleepSysVar.ToString;

end;

// *****************************
// フォーム破棄
// *****************************
procedure TForm5.FormDestroy(Sender: TObject);
var
  ini : TIniFile;
begin
  ini := TIniFile.Create(ChangeFileExt(ParamStr(0), 'ini'));
  with ini do begin
    try
      WriteString('Plot', 'LastPrinter', LastPrinter);
      WriteString('Plot', 'LastCtbStb',  LastCtbStb);

      WriteInteger('Form', 'FNameWidth', StringGrid1.ColWidths[1] );

      WriteInteger('Timer', 'SleepDocChg', SleepDocChg);
      WriteInteger('Timer', 'SleepPrint',  SleepPrint);
      WriteInteger('Timer', 'SleepSysVar', SleepSysVar);
      WriteBool('CheckBox', 'Uses CmdCapt', CheckBox1.Checked);


      WriteString('SheetAttRib', 'BlockName', Edit5.Text);
      WriteString('SheetAttRib', 'AttName',   Edit6.Text);

      WriteString('PageNoAttRib', 'BlockName', Edit7.Text);
      WriteString('PageNoAttRib', 'AttName',   Edit8.Text);

    finally
      Free;
    end;
  end;
end;

// 待機タイマー設定
procedure TForm5.PageControl1Change(Sender: TObject);
begin
  if PageControl1.ActivePageIndex = 0 then begin
    SleepDocChg := StrToIntDef(Edit2.Text, SleepDocChg);
    SleepPrint  := StrToIntDef(Edit3.Text, SleepPrint );
    SleepSysVar := StrToIntDef(Edit4.Text, SleepSysVar);
  end;
end;

// *****************************
// 印刷チェック「すべてON」
// *****************************
procedure TForm5.SpeedButton1Click(Sender: TObject);
var
  i : integer;
begin
  with StringGrid1 do begin
    for i := 1 to RowCount -1 do
      Objects[1, i] := TObject(True);
  end;
  DispCheckedCount;

end;
// *****************************
// 印刷チェック「すべてOFF」
// *****************************
procedure TForm5.SpeedButton2Click(Sender: TObject);
var
  i : integer;
begin
  with StringGrid1 do begin
    for i := 1 to RowCount - 1 do
      Objects[1, i] := TObject(False);
  end;
  DispCheckedCount;
end;

procedure TForm5.SpeedButton3Click(Sender: TObject);
begin
  // シート番号順
  SgSortByCol2(StringGrid1, 7, -11, False);
end;

// *****************************
// 印刷「ファイル名順」
// *****************************
procedure TForm5.SpeedButton4Click(Sender: TObject);
begin
  // ソート
  SgSortByCol2(StringGrid1, 6, 1, False);
end;

procedure TForm5.SpeedButton5Click(Sender: TObject);
begin
  // ページ番号順ソート
  SgSortByCol2(StringGrid1, 8, -1, True);

end;

procedure TForm5.SpeedButton6Click(Sender: TObject);
var
  ARect : TREct;
  ATop, AHeight: integer;
  Bmp : TBitmap;
begin
  GetDcadMainWinHandle;
  GetDcadCommandWinHandle;
  //Form5.Caption := IntToHex(DcadInputWinHandle, 8);
  GetWindowRect(DcadInputWinHandle, ARect);
  ATop := ARect.Top;

  AHeight := ARect.Bottom - ARect.Top;
  ATop := ATop+ AHeight -20;

  Bmp := TBitmap.Create;
  try
    Bmp.Width := 100;
    Bmp.Height := 20;
    Bmp.PixelFormat :=pf24bit;
    CaptureToBmp(Arect.Left, ATop, Bmp.Width, Bmp.Height, bmp);
    Image2.Picture.Assign(Bmp);
  finally
    Bmp.Free;
  end;

end;

// *****************************
// 印刷:取得順ソート
// *****************************
procedure TForm5.SpeedButton9Click(Sender: TObject);
begin
  SgSortByCol2(StringGrid1, 0, - 1, True);
end;

// *****************************
// 印刷用StringGridクリック
// *****************************
procedure TForm5.StringGrid1Click(Sender: TObject);
var
  idx ,j  : integer;
begin
  with StringGrid1 do begin
    if Row > 0 then begin
      idx := StrToIntDef(Cells[0, Row], - 1) - 1;

      if GetDCadMainWinHandle <> 0 then begin

        SetForegroundWindow(DcadMainWinHandle);

        // メニュー操作
        keybd_event(VK_MENU, 0, 0, 0);
        keybd_event(Ord('W'), 0, 0, 0);
        keybd_event(Ord('W'), 0, KEYEVENTF_KEYUP, 0);
        keybd_event(VK_MENU, 0, KEYEVENTF_KEYUP, 0);

        for j := 0 to idx + 4 do begin
          keybd_event(VK_DOWN, 0 ,0 ,0);
          keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
        end;
        keybd_event(VK_RETURN, 0, 0, 0);
        keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
      end;
    end;
  end;
end;

// *****************************
// 印刷用StringGrid描画
// *****************************
procedure TForm5.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer;
  Rect: TRect; State: TGridDrawState);
var
  TempRect : TRect;
  BoxRect  : TRect;
  uState   : Cardinal;
  sg : TStringGrid;
begin
  sg := TStringGrid(Sender);
  TempRect := Rect;

  if (ARow > (sg.FixedRows - 1)) and (ACol = 1) then begin
    // 背景を消す
    sg.Canvas.FillRect(Rect);

    //チェックボックスのサイズを設定
    BoxRect.Left   := Rect.Left + 5;
    BoxRect.Top    := Rect.Top + 3;
    BoxRect.Bottom := Rect.Bottom - 3;
    BoxRect.Right  := BoxRect.Left + (BoxRect.Bottom - BoxRect.Top);
    Rect.Right     := Rect.Bottom - Rect.Top;

    TempRect.Left := TempRect.Left + (BoxRect.Right - BoxRect.Left) + 8;
    TempRect.Top  := TempRect.Top + 3;

    //Objectsプロパティの値に応じてチェック状態を描画
    if Bool(sg.Objects[ACol, ARow]) then begin
      sg.Canvas.Font.Color := clWindowText;
      uState := DFCS_BUTTONCHECK or DFCS_CHECKED;
    end else begin
      sg.Canvas.Font.Color := clRed;
      uState := DFCS_BUTTONCHECK;
    end;

    DrawText(sg.Canvas.Handle,
             PChar(sg.Cells[ACol,ARow]), - 1, TempRect, DT_LEFT or DT_SINGLELINE);
    DrawFrameControl(sg.Canvas.Handle, BoxRect, DFC_BUTTON, uState);
  end;
end;

// *****************************
// 印刷用StringGrid キー操作
// *****************************
procedure TForm5.StringGrid1KeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
  AColLeft   : Integer;
  ARowTop    : Integer;
  ARowBottom : Integer;
  i          : Integer;
  ABool      : Boolean;
  sg : TStringGrid;
begin
  sg := TStringGrid(Sender);
  with sg do begin
    if Key = VK_SPACE then begin
      //選択中のセルの範囲を調査
      AColLeft   := Selection.Left;
      ARowTop    := Selection.Top;
      ARowBottom := Selection.Bottom;

      //複数行選択に対応
      if AColLeft = 1 then begin
        for i := ARowTop to ARowBottom do begin
          if Cells[AColLeft, i] <> '' then begin
            ABool :=  Bool(Objects[AColLeft, i]);
            Objects[AColLeft, i] := TObject(not ABool);
          end;
        end;
        DispCheckedCount;
      end;
    end;
  end;
end;

// *****************************
// 印刷用StringGrid マウス操作
// *****************************
procedure TForm5.StringGrid1MouseDown(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  ACol  : Integer;
  ARow  : Integer;
  ABool : Boolean;
  sg : TStringGrid;
begin
  sg := TStringGrid(Sender);
  with sg do begin
    if Button = mbLeft then begin
      MouseToCell(X, Y, ACol, ARow);
      // ダブルクリックのために記憶
      MbRow := ARow;
      MbCol := ACol;

      if (ARow > (FixedRows - 1)) and (ACol = 1) then begin
        if Cells[ACol,ARow] <> '' then begin
          ABool   :=  Bool(Objects[ACol, ARow]);
          Objects[ACol, ARow] := TObject(not ABool);
          DispCheckedCount;
        end;
      end;
    end;
  end;
end;

end.