Bricscad V19 MouseMove 1.09 2019/01/09

2019/01/09 根本的な不具合(コマンドラインの文字列取得)を修正 (Ver.1.09)
2019/01/07 終了処理を修正 (Ver.1.07)
2019/01/06 終了処理を修正 (Ver.1.06)
2019/01/06 2 重起動の防止を追加、終了処理を修正 (Ver.1.01)


マウス移動により [Enter] を発行、コマンド入力待ちの時、IME をオフにするツールです。Bricscad V19 専用です。
OneKey2,3 の不具合が特定できなかったので、新たに作り直しました。エラー除けの小細工、キーボードフックは削除しました。

 コマンド入力中は右上方向のみ、コマンドオプション入力中は全方向のマウス移動で [Enter] を発行します。
 コマンドオプション文字が優先され、一時オブジェクトスナップの省略文字に合う場合は、一時Oスナップに置き換え、[Enter] を発行します。
 それ以外では、何もしません。
 ※入力文字は、大文字・小文字を問いません。

 

■一時オブジェクトスナップの省略文字 (OneKey と同じ)

   M, MI, MD, MM :  mid (中点)
   C, CE, CN, CC :  cen (中心)
   Q, QU, QA, QQ :  qua (四半円点)
   I, IT, I I :  int (交点)
   E, EN, ED, EE :  end(端点)
   T, TA, TN, TT :  tan (接線)
   N, NE, NA :  nea (近接点)
   P, PE, PR, PP :  per (垂線)
   S, IN, IS :  ins (挿入基点)
   O, D, ND :  nod (点)
   NN, NO :  non (解除)
   V, M2, MT, MP, MMM :  m2p (2点間中点)
   EX, ET :  ext (延長)
   F, FR, FM :  from (基点設定)
   A, AP :  app (仮想交点)
   PA :  par (平行)
   X, XX :  [ESC]キー
   G, GC : gce (図心)
  ※コマンドオプションが優先されます。


■著作権、免責事項等
 ・本ツールの著作権は、作者 f.izawa が所有し、これを主張します。
 ・本ツールを使用したことによる事故、損害等の一切について作者はその責を負いません。

■開発環境
 ・Delphi 10.2.3 Tokyo Community Edition
 ・Bricscad V19 Pro
 ・Windows 10 64bit

■作者連絡先
 e-mail: f.izawa@dream.com
 URL: http://www.izawa-web.com/

■ダウンロード

 ・BC19MMV.zip (Ver.1.09 EXE 本体のみ)

 ・BcadCtrl19.zip (ソースコード 2019/01/09 BcadCtrl19.pas ※実行には不要です)


■ソースコード

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  BcadCtrl19, Vcl.StdCtrls, Vcl.ExtCtrls, IMM, IniFiles;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Edit6: TEdit;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
    // 基準カーソル位置とマウス移動の判断の閾値
    curX, curY, dXY : integer;
    // Bricscad が起動中 ON
    bcadFlag : boolean;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
procedure HwndSendKeys(h: HWND; const keystr: string);
{ Hwnd をアクティブにして、Keyを押す}
var
  i : integer;
  s: string;
begin
  if IsWindowEnabled(h) then begin
    SetForegroundWindow(h);
    s := UpperCase(keystr);
    for i := 1 to Length(s) do begin
      keybd_event(Byte(s[i]), 0, 0, 0);
      keybd_event(Byte(s[i]), 0, KEYEVENTF_KEYUP, 0);
      Sleep(1);
    end;
  end;
end;

function makeObjectSnapStr(const cmdo: string; const optstr: string): string;
begin
  result := '';
  // オプションリストに存在
  if (optstr = '') or (cmdo.IndexOf(',' + optstr + ',') < 0) then begin
    // 一時オブジェクトスナップ
    // 中点:MID
    if optstr = 'M' then result := 'id' + #13
    else if optstr = 'MI' then result := 'd' + #13
    else if optstr = 'MD' then result := #08 + 'id' + #13
    else if optstr = 'MM' then result := #08 + 'id' + #13
    // 中心:CEN
    else if optstr = 'C' then result := 'en' + #13
    else if optstr = 'CE' then result := 'n' + #13
    else if optstr = 'CN' then result := #08+'en' + #13
    else if optstr = 'CC' then result := #08+'en' + #13
    // 四半円点:QUA
    else if optstr = 'Q' then result := 'ua' + #13
    else if optstr = 'QU' then result := 'a' + #13
    else if optstr = 'QA' then result := #08 + 'ua' + #13
    else if optstr = 'QQ' then result := #08 + 'ua' + #13
    // 端点:END
    else if optstr = 'E' then result := 'nd' + #13
    else if optstr = 'EN' then result := 'd' + #13
    else if optstr = 'ED' then result := #08 + 'nd' + #13
    else if optstr = 'EE' then result := #08 + 'nd' + #13
    // 交点:INT
    else if optstr = 'I' then result := 'nt' + #13
    else if optstr = 'IT' then result := #08 + 'nt' + #13
    else if optstr = 'II' then result := #08 + 'nt' + #13
    // 接線:TAN
    else if optstr = 'T' then result := 'an' + #13
    else if optstr = 'TA' then result := 'n' + #13
    else if optstr = 'TN' then result := #08 + 'an' + #13
    else if optstr = 'TT' then result := #08 + 'an' + #13
    // 近接点:NEA
    else if optstr = 'N' then result := 'ea' + #13
    else if optstr = 'NE' then result := 'a' + #13
    else if optstr = 'NA' then result := #08 + 'ea' + #13
    // 垂線:PER
    else if optstr = 'P' then result := 'er' + #13
    else if optstr = 'PE' then result := 'r' + #13
    else if optstr = 'PR' then result := #08 + 'er' + #13
    else if optstr = 'PP' then result := #08 + 'er' + #13
    // 挿入基点:INS
    else if optstr = 'S' then result := #08 + 'ins' + #13
    else if optstr = 'IS' then result := #08 + 'ns' + #13
    else if optstr = 'IN' then result := 's' + #13
    // 点:NOD
    else if optstr = 'D' then result := #08 + 'nod' + #13
    else if optstr = 'O' then result := #08 + 'nod' + #13
    else if optstr = 'ND' then result := #08 + 'od' + #13
    // 解除:NON
    else if optstr = 'NN' then result := #08 + 'on' + #13
    else if optstr = 'NO' then result := 'n' + #13
    // 2点間中点:M2P
    else if optstr = 'V' then result := #08 + 'm2p' + #13
    else if optstr = 'MT' then result := #08 + 'tp' + #13
    else if optstr = 'M2' then result := #08 + '2p' + #13
    else if optstr = 'MP' then result := #08 + '2p' + #13
    else if optstr = 'MMM' then result := #08#08 + '2p' + #13

    // 延長:EXT
    //else if optstr = 'E' then result := 'xt' + #13
    else if optstr = 'EX' then result := 't' + #13
    else if optstr = 'ET' then result := #08 + 'xt' + #13
    // 基点設定:FROM
    else if optstr = 'F' then result := 'rom' + #13
    else if optstr = 'FR' then result := 'om' + #13
    else if optstr = 'FM' then result := #08 + 'rom' + #13
    // 仮想交点:APP
    else if optstr = 'A' then result := 'pp' + #13
    else if optstr = 'AP' then result := 'p' + #13
    // 平行:PAR
    else if optstr = 'PA' then result := 'r' + #13
    //else if optstr = 'PR' then result := #08 + 'ar' + #13
    // [ESC]
    else if optstr = 'X' then result := #27
    else if optstr = 'XX' then result := #27
    // 図心 : GCE
    else if optstr = 'G' then result := 'ce'+#13
    else if optstr = 'GC' then result := 'e'+#13;
  end;
end;

function makeCmdOptionList(const cmds: string): string;
var
  n, m : integer;
  s, s2 : string;
begin
  result := '';
  s := cmds;
  if (s.IndexOf('(') > 0) and (s.IndexOf(')') > 0) then begin
    m := Pos('[', s);
    n := Pos(']', s);
    if (m > 0) and (n > m) then s := Copy(s, m + 1, n-m-1);
    if s <> '' then begin
      while True do begin
        m := Pos('(', s);
        n := Pos(')', s);
        if (m > 0) and (n > m) then begin
          s2 := Copy(s, m + 1, n - m -1);
          if (s2.Length > 0) and (s2.Length <= 5) then result := result + s2 + ',';
          Delete(s, 1, n);
        end
        else break;
      end;
    end;
  end
  else if (s.IndexOf('選択:') > 0) then begin
    result := 'W,L,C,BOX,ALL,F,WP,CP,G,CL,A,R,M,P,U,AU,SI,';
  end;
  // OPT の始まりと終わりは、','で囲まれている状態にする
  if result <> '' then result := ',' + result;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
  ini : TIniFile;
begin
  ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  try
    ini.WriteInteger('OneKey', 'MouseMove', dXY);
    ini.WriteBool('OneKey','ImeOff', CheckBox1.Checked);
    ini.WriteBool('OneKey','OneKey', CheckBox2.Checked);
  finally
    ini.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  ini : TIniFile;
begin
  Edit6.Text := '';
  dXY := 40;
  // スクリーンの右下隅に表示
  Left := Screen.Width  - Width  - 10;
  Top  := Screen.Height - Height - 130;
  ini := TIniFile.Create(ChangeFileExt(ParamStr(0), '.ini'));
  try
    dXY := ini.ReadInteger('OneKey','MouseMove', dXY);
    CheckBox1.Checked := ini.ReadBool('OneKey','ImeOff', True);
    CheckBox2.Checked := ini.ReadBool('OneKey','OneKey', True);
  finally
    ini.Free;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
  PT: TPoint;
  cmdline, cmdstr, cmdopt, optstr, kbstr : string;
  n : integer;
  dX, dY :double;
  optFlag, cmdFlag : boolean;
  IME : HWND;
  termFlag : boolean;
begin
  termFlag := False;
  Timer1.Enabled := False;
  try
    cmdline := GetBcadCmdLine;
  except
    cmdline := '';
    Caption := 'ERR1';
  end;
  // IME Off
  if CheckBox1.Checked and (cmdLine = ': ') then begin
    IME := ImmGetDefaultIMEWnd(BcadCmdLineHandle);
    if IME <> 0 then begin
      // ON であれば OFF にする
      if Boolean(SendMessage(IME, WM_IME_CONTROL, $0005, 0)) then
         SendMessage(IME, WM_IME_CONTROL, $0006, 0);
    end;
  end;
  // MouseMove
  if CheckBox2.Checked and IsWindowEnabled(BcadMainWinHandle) and GetCursorPos(PT) then begin
    // 前回との偏差(CAD 座標と同じ方向)
    dx := PT.X -curX;
    dy := curY -PT.Y;

    Edit6.Text := dX.ToString + ',' + dY.ToString;
    // コマンドオプションリスト
    cmdopt := makeCmdOptionList(cmdline);

    n := LastDelimiter(':', cmdline);
    // コマンド入力中
    if n = 1 then cmdstr := Trim(Copy(cmdline, 2))
    // コマンドオプション待ち
    else if n > 1 then optstr := Trim(Copy(cmdline, n + 1))
    else optstr := '';

    // コマンド入力中
    cmdFlag := cmdstr <> '';
    // コマンドオプション入力中
    if optstr <> '' then begin
      optstr := UpperCase(optStr);
      // オプションリストに存在
      optFlag := Pos(',' + optStr + ',', cmdopt) > 0;
    end
    else
      optFlag := False;
    // コマンド入力中は、右上方向のみ
    if cmdFlag and (dX > dXY) and (dY > dXY) then begin
      HwndSendKeys(BcadMainWinHandle, #13);
      Sleep(500);
    end
    // オプションの場合は全方向
    else if optFlag and (abs(dX) > dXY) and (abs(dY) > dXY) then begin
      HwndSendKeys(BcadMainWinHandle, #13);
      Sleep(500);
    end
    // 一時オブジェクトスナップに変換できる時は、全方向
    else if (optstr <> '') and (abs(dX) > dXY) and (abs(dY) > dXY) then begin
      kbstr := makeObjectSnapStr('', optstr);
      if kbstr <> '' then begin
        HwndSendKeys(BcadMainWinHandle, kbstr);
        Sleep(500);
      end;
    end;
    // 基準座標を更新
    curX := PT.X; curY := PT.Y;
  end;

  // 追従して終了
  if not bcadflag then begin
    if IsWindow(BcadMainWinHandle) then begin
      bcadflag := True;
      SetForegroundWindow(BcadMainWinHandle);
    end;
  end
  else begin
    if not IsWindow(BcadMainWinHandle) then begin
      termFlag := True;
      Close;
    end;
  end;
  if not termFlag then
    Timer1.Enabled := True;
end;

end.