AutoHotKeyでWindowsやExcelやその他の操作を改善する

最近Windowsを使う機会が増えたので、操作を楽にするためにAutoHotKeyでいくつか設定を行いました。その設定について、誰かの参考になるかもしれないので、備忘録代わりにまとめます。

## キー２連打に処理を割り当てる方法

( 2019年6月20日大部分を書き換え )

### 単独のキーの２連打に処理を割り当てる

"Ctrl"などと組み合わせるのでなければ、以下のスクリプトで２連打に処理を割り当てられます。
 
```
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Title: テンキーのピリオド2連打でカンマ入力
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/
~NumpadDot::
  If (A_PriorHotKey == A_ThisHotKey and A_TimeSincePriorHotkey < 200)
  {
    SendInput, {BackSpace 2}
    Send,{U+002c}
  }
Return
```

上記のコードは、以下の流れで処理が行われます。

1. `~NumpadDot::`

  テンキーの"."をホットキーに指定しています。行頭に`~`を追加しているので、"."の入力はシステムにも渡されて"."が入力されます。

1. `If (A_PriorHotKey == A_ThisHotKey and A_TimeSincePriorHotkey < 200)`

  組み込み変数を使って、「前回入力されたホットキーと今回のホットキーが同じ」かつ「前回のホットキー入力から今回のホットキー入力までの時間が200ミリ秒未満」という条件文を組み立てています。 `A_PriorHotKey`変数には前回押されたホットキー、`A_ThisHotKey`変数には今回押されたホットキーが格納されているので、2つの変数が等しければ同じホットキーを2回連続で入力したと判断できます。そして、`A_TimeSincePriorHotkey`変数には、前回のホットキー入力から今回のホットキー入力までの時間がミリ秒単位で格納されているので、この変数の値が200未満なら２連打したと判断しています。

1. `SendInput, {BackSpace 2}`

  この段階までに2回入力されている"."を削除するため、`SendInput, {BackSpace 2}`コマンドでBackSpaceを2回入力しています。

1. `Send,{U+002c}`

  `Send,{U+002c}`コマンドで","を入力しています。`{U+002c}`は","のUnicodeの文字コードですが、Unicodeの文字コードで指定することで、IMEがオンでも確実に半角カンマが入力できます。

### Ctrlなどと組み合わせたキーの２連打に処理を割り当てる

CtrlやAltなどのモディファイヤキーと組み合わせたキーの２連打の場合、上記より面倒になりますが、以下のスクリプトで対処できます。

```
/*
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Title: Alt-Qの2連打でアプリを閉じる
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*/
~!q::
  Input, inputText, I L1 T0.5, !q
  IfInString, ErrorLevel, EndKey:
  {
    Send, !{F4}
  }
Return
```

上記のコードは、次の処理を行っています。

1. `~!q::`

  "Alt-Q"をホットキーに指定しています。`~`を最初につけているので、"Alt-Q"はシステムにも送信されます。これにより、"Alt-Q"がショートカットキーとして使われているアプリでも、その処理を実行できます。
  
1. `Input, inputText, I L1 T0.5, !q`

  `Input`コマンドを使って、最初のピリオドを押してから0.5秒間(`T0.5`の部分)のキーボード入力を１文字まで(`L1`の部分)受け取って`inputText`変数に格納します。もし、入力されたキーが"Alt-Q"であれば、そこで`Input`の処理を終了して、組み込み変数の`ErrorLevel`に`EndKey:(入力されたキー)`を格納します。

1. `IfInString, ErrorLevel, EndKey:`

  組み込み変数の`ErrorLevel`の値に`"EndKey:"`が含まれるか否かを判定しています。`ErrorLevel`の値は、最初のAlt-Qを押してから0.5秒以内に何も押さなければ`"Timeout"`になり、ピリオド以外の文字を2文字打つと、その時点で`"Max"`になります。

1. `Send,!{F4}`

  前面に表示されているアプリに"Alt-F4,"を送信します。

この方法を応用すると、ScrollLockを２連打したらメモ帳が起動するという設定も可能です。

```
~ScrollLock::
  Input, var_dot_press, I T0.2 L1, {ScrollLock} 
  if(ErrorLevel == "EndKey:ScrollLock")
  {
    Run,notepad.exe
  }
  else
  {
    Send,%var_dot_press%
  }
Return
```
~~

## "無変換+G"でいつでもGoogle検索

[こちらのQiitaの記事](https://qiita.com/dada_moreo/items/b9ba5e4712a28376fd73)とほぼ同じですが、"無変換"キーを"無変換"キーとしても使えるよう一工夫しています。

```
~vk1Dsc07B & g::
InputBox, sword, Input Search Word , , ,300,110
if ErrorLevel <> 0
{
  Return
}
else
{
  searchWord := RegExReplace(sword, "\s+", "+")
  Run, "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" -new-tab "http://www.google.co.jp/search?hl=ja&lr=lang_ja&ie=UTF-8&q=%sword%"
}
Return
```

1. `~vk1Dsc07B & g::`
  `vk1Dsc07B`は"無変換"キーを指定するときのキーコードです。`&`を使うことで、"無変換＋g"という組み合わせで処理が始まるようにしています。
  なお、キーコードの前に`~`を付けない場合、"無変換"を単独で機能させることができなくなります。私は"無変換"を単独で使う必要があるため、上記のとおりキーコードの前に`~`を付けています。

1. `InputBox, sword, Input Search Word , , ,300,110`
  検索用語を入力するための入力画面を表示させます。`Input Search Word`が入力画面のタイトルとして表示される部分で、`300, 100`は、入力画面の横幅と高さの指定です。入力した文字列は、`sword`変数に格納されます。

1. `if ErrorLevel <> 0`
  `ErrorLevel`が`0`以外の値になる場合は、入力画面の"cancel"ボタンが押されたか、上記では設定していないですが、設定したタイムアウト時間を超過した場合です。この場合、処理することがないので、`Return`で処理を終了しています。

1. `searchWord := RegExReplace(sword, "\s+", "+")`
  `RegExReplace`コマンドを使って、検索用語に含まれる空白文字を"+"に置換した文字列を`searchWord`変数に格納しています。

1. `Run, "C:\Program Files~~~""`
  コマンドラインからChromeの新規タブを開き、アドレスバーを通じてGoogleで検索します。

## 好きなウィンドウをいつでも最前面に固定する

"Pause"キーを押すと、その時点のアクティブウィンドウを最前面に固定/解除する設定です。

```
~Pause::WinSet, AlwaysOnTop, toggle, A
```

`AlwaysOnTop, toggle`の部分を`AlwaysOnTop, On`にすれば最前面固定のみ行う動作になり、`AlwaysOnTop, Off`にすれば最前面固定の解除のみ行う動作になります。

## Excelの操作を改善する

( 2019年6月20日 スクリプトに`Sleep 30`の処理を追加 )

ExcelではCtrl-Spaceで列全体を選択できて、Shift-Spaceで行全体を選択できるのですが、このショートカットキーはIMEがONだと機能しません。そこで、IMEがONでもこの２つのショートカットキーが使えるようにします。

```
#Include  %A_ScriptDir%/IME.ahk
Return

#ifWinActive ahk_exe EXCEL.EXE
  $^Space::
    if (IME_GET())
    {
      IME_SET(0)
      Sleep 30
      Send,^{Space}
      Sleep 30
      IME_SET(1)
    }
    else
    {
      Send,^{Space}
    }
    Return
    
  $+Space::
    if (IME_GET())
    {
      IME_SET(0)
      Sleep 30
      Send,+{Space}
      Sleep 30
      IME_SET(1)
    }
    else
    {
      Send,+{Space}
    }
    Return
#ifWinActive
```

`#ifWinActive`は、指定したウィンドウでのみ有効なキーバインドを設定したい場合に使います。今回は、`ahk_exe EXCEL.EXE`とすることで、この設定をExcelだけで有効にしています。

`IME_GET`や`IME_SET`は、[IME制御 - eamat @Cabinet - アットウィキ](https://www6.atwiki.jp/eamat/pages/17.html)で配布されている`IME.ahk`という関数群のファイルをincludeすることで使える関数です。このファイルは、AutoHotKeyでIMEを制御するための関数が定義されており、IMEを制御したいときは必須のアイテムです。私の環境では、自作の設定ファイルと同じフォルダに保存していますので、`#Include  %A_ScriptDir%/IME.ahk`で読み込んでいます。（`%A_ScriptDir%`は、それを記述した設定ファイルが保存されているフォルダを示す組み込み変数です。）


処理の流れは次のとおりです。

1. `if(IME_GET())`でIMEの状態を取得
1. IMEがON（`IME_GET()`の返り値が1 = AutoHotKeyでは真）なら`IME_SET(0)`でIMEをOFFにする
1. `Send,^{Space}`, `Send,+{Space}`で"ctrl(shift)-Space"を実行する。
1. `IME_SET(1)`でIMEをONに戻す
1. IMEがOFF（`IME_GET()`の返り値が0 = AutoHotKeyでは偽）なら、IMEは操作しないで`Send,^{Space}`, `Send,+{Space}`で"ctrl(shift)-Space"を実行する。


次は、F1キーでヘルプを開かないという設定です。Excel2016ではヘルプ画面がアプリのウィンドウの右側のペインに出てくるのですが、これを閉じるにはマウスで閉じるボタンをクリックする必要があり、キーボードから手を放す必要が生じます。

これだけならまだ我慢はできるのですが、F2キーで入力済みのセルの値を編集しようとした際にF1キーも押してしまい、そのたびにキーボードから手を放すのが非常に面倒なので、思い切ってF1キーを無効化し、ヘルプを開くときは"Shift-F1"で開くようにしました。

```
#ifWinActive ahk_exe EXCEL.EXE
  $F1::Return
  $+F1::SendInput, {F1}
#ifWinActive
```

## 編集した設定ファイルを簡単に再読み込みする

AutoHotKeyの設定ファイルを編集した場合、タスクトレイのアイコンを右クリックして"Reload This Script"を実行しないと編集後の設定が反映されませんが、以下のとおり設定すると、Ctrl-Alt-Rで設定ファイルの再読み込みができるようになります。

どのキーに割り当てるにせよ、非常に便利な機能です。

```
^!r::Reload
```

## 上記のコードに登場したコマンドのリファレンス

Input -> [Input - AutoHotkey Wiki](http://ahkwiki.net/Input) 
Send, SendInput -> [Send / SendRaw / SendEvent / SendInput / SendPlay - AutoHotkey Wiki](http://ahkwiki.net/Send)
InputBox -> [InputBox - AutoHotkey Wiki](http://ahkwiki.net/InputBox)
RegExReplace -> [RegExReplace() - AutoHotkey Wiki](http://ahkwiki.net/RegExReplace)
WinSet -> [WinSet - AutoHotkey Wiki](http://ahkwiki.net/WinSet)
Reload -> [Reload - AutoHotkey Wiki](http://ahkwiki.net/Reload)
if文 -> [スクリプト仕様 - AutoHotkey Wiki](
http://ahkwiki.net/Scripts#.E6.9D.A1.E4.BB.B6.E5.88.86.E5.B2.90)
\#ifWinActive -> [#IfWinActive / #IfWinExist - AutoHotkey Wiki](http://ahkwiki.net/-IfWinActive)