AutoHotKeyで、AzPainter2を少し便利に。
AzPainter2を少し便利にするスクリプト作った。今回実現したのはキー操作によるカレントレイヤの切り替えと、17段階の明度切り替え。
カーソル上下でレイヤ切り替え。スクロールには非対応。ごくまれにレイヤ順が乱れることがあるが、気にしない方向で。発動時にカーソル動かすとレイヤ移動になっちゃうので、BlockInput入れた。解除されない場合、Alt+Ctrl+Deleteで強制解除できる。AzDには標準でカレントレイヤ切り替えあるのになー。
PgUp、PgDnで明度変更。厳密にいうとHSVモードだと明度だが、RGBモードだと青。一度実行するとAzP2が終了するまで常駐するが、常駐時はスポイト等で明度を変更しても自動的に17段階のいずれかに補正される。
こんなんなにに使うのかというと、Seeneon用の深度マップを作るのにめっちゃ便利。17段階(-8 ~ 0 ~ +8)なのも、中央(128)が欲しかったから。
;--------------------------------------------------------------------------------------
; AzPainter2
;--------------------------------------------------------------------------------------
#IfWinActive AzPainter2 - ahk_class Main
Numpad0::Space ; Numpad0でもスクロール
Up:: ; ひとつ上をカレントレイヤに
ALayerChange(1)
return
Down:: ; ひとつ下をカレントレイヤに
ALayerChange(0)
return
PGUP::
.:: ; HSVモード時、明度を上げる(17段階)
if (CVLv < 16)
CVLv++
C3SetLevel(CVLv)
return
PGDN::
,:: ; HSVモード時、明度を下げる(17段階)
if (CVLv > 0)
CVLv--
C3SetLevel(CVLv)
return
ALayerChange(SW) {
IfWinNotExist, レイヤ ahk_class Layer
return
BlockInput, On
WinGetPos, WX, WY
ControlGetPos, CX, CY, CW, CH, LayerView1
CX += 4, CY -= 16
BaseX := WX + CX, BaseY := WY + CY
MaxX := BaseX + CW, MaxY := BaseY + CH
CoordMode, Pixel
PixelSearch, , PY, BaseX, BaseY, MaxX, MaxY, 0xC6D1FF, 0, Fast
if ErrorLevel {
BlockInput, Off
return
}
LayerNum := (PY - BaseY) // 35 + 1
SW ? LayerNum-- : LayerNum++
CCY := CY + 35 * LayerNum
ControlClick, X%CX% Y%CCY%, , , L, 1, Pos NA
BlockInput, Off
}
CheckCV:
IfWinNotExist, AzPainter2 - ahk_class Main
{
ToolTip, , , , 2
SetTimer, CheckCV, Off
return
}
ControlGetText, CVNum, Edit3, コントロール ahk_class #32770
if (BefCVNum == CVNum)
return
BefCVNum := CVNum
CVLv := CVNum // 16
if (8 <= (CVNum - CVLv * 16))
CVLv++
C3SetLevel(CVLv)
return
C3SetLevel(CVLv) {
IfWinNotExist, コントロール ahk_class #32770
return
CoordMode, ToolTip
WinGetPos, WX, WY
ShowCVLv := CVLv - 8
ToolTip, %ShowCVLv%, %WX%, %WY%, 2
SetTimer, CheckCV, 300
CVNum := 16 * CVLv
ControlSetText, Edit3, %CVNum%
}
レイヤの数は限られてるんだし、PixelSearchで広範囲を走査するより、その数だけPixelGetColorした方が早いんじゃね?と思ったが、そんなことはなかったぜ!
あと、PixelSearchってFastがデフォじゃないんだな。他のコマンドはデフォが高速側な印象。
AutoHotKey_Lで、引数を動作中のソフトに渡すツール作った。ver.2.1.0
S2WAは、あらかじめAppListを用意しておき、リスト内のソフトが起動済みならそのソフトに、そうでなければリストの一番上のソフトに、自身が受け取った引数を丸投げするツール。
例えばいくつかのブラウザを使い分けている環境で、HTTPなどをS2WAに関連付けておけば、リンクを踏んだ時起動済みのブラウザで開かれるため、無駄にブラウザを起動せずに済む。
ブラウザならBrokenURLが高機能。
ver.2.1.0では、OperaNext15が妙なディレクトリ構成なので、それに対応すべくAppListで正規表現を使えるようにした。
; ////////////////////////////////////////////////////////////////////////////////
;
; S2WA.ahk ver.2.1.0
;
; ////////////////////////////////////////////////////////////////////////////////
FileEncoding, CP1200
IfNotExist, %A_ScriptDir%\AppList.txt
{
MsgBox, %A_ScriptDir%\AppList.txt が存在しません!
ExitApp
}
for process in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process")
if process.ExecutablePath
ProcessList .= process.ExecutablePath "`n"
TrgExe := ""
Loop, Read, %A_ScriptDir%\AppList.txt
{
if !A_LoopReadLine
continue
StringLeft, CharBuf, A_LoopReadLine, 1
if ("*" == CharBuf)
StringTrimLeft, SearchExe, A_LoopReadLine, 1
else {
StringReplace, SearchExe, A_LoopReadLine, \, \\, All
StringReplace, SearchExe, SearchExe, ., \., All
StringReplace, SearchExe, SearchExe, [, \[, All
StringReplace, SearchExe, SearchExe, ], \], All
StringReplace, SearchExe, SearchExe, +, \+, All
StringReplace, SearchExe, SearchExe, {, \{, All
StringReplace, SearchExe, SearchExe, }, \}, All
StringReplace, SearchExe, SearchExe, ^, \^, All
StringReplace, SearchExe, SearchExe, $, \$, All
StringReplace, SearchExe, SearchExe, (, \(, All
StringReplace, SearchExe, SearchExe, ), \), All
}
if RegExMatch(ProcessList, "im`n)^(" SearchExe ")$", $) {
MsgBox, %$1%
TrgExe := $1
break
}
}
If !TrgExe
FileReadLine, TrgExe, %A_ScriptDir%\AppList.txt, 1
Loop, %0%
{
CmdlineBuf := %A_Index%
Run, %TrgExe% "%CmdlineBuf%"
Sleep, 1000
}
使用するには、スクリプトと同じディレクトリにAppList.txtを用意し、S2WAを呼び出すだけ。S2WAはAppListを上から順に起動済みプロセスと照合し、いずれもHitしなければ一番上のソフトを起動する。
AppListの仕様は以下の通り。
- 文字コードはUnicode(UTF-16LE)。
- 1行につきひとつのフルパス。
- 行が*から始まった場合、その行は正規表現とみなされる。
ただし、1行目は正規表現であってはならない。OperaNext15を最優先にしたいなら、以下のように記述する。
R:\Opera Next\launcher.exe
*R:\\Opera Next\\.*\\opera\.exe
WindowsVista以降、httpなどのプロトコルへの関連付けは面倒になっている。詳しくはver.2.0.0の記事参照。
OperaNext15を試して、Chrome拡張入れてみた。
Opera15(以下OPR)のOperaNextが出たので色々いじってみた。なおOperaNextというのは、Operaの次期バージョンのプレビュー版であり、αとかβとかウィークリービルドのたぐいであって、間違っても正式版ではないので注意。
OPRでは、エンジンがWebKit(Blink)へと変更になるが、スタッフが「Operaらしさはそのまま」といってたもんだから、ワシは「Opera in Webkit」的なものが出てくると思っていた。だがそこに「Chromium Opera Edition」としかいいようがないものが出てきたせいで、ワシのみならず、Operaクラスタは阿鼻叫喚と化したのだった。そのあとでスタッフが「不安定な機能はまだ実装してないだけ」「高速リリース採用するので、いずれOperaらしくなるよ!」などと弁明したため、現状は様子見である。
などといきなりdisり気味だが、現時点でもChromeの代替としては十分であり、ワシはサブとして使っていたChromeを捨てた! OPRが真にOperaの代替となるまでは、メインはOpera12が続投すればいいじゃない!
で。OPRでは拡張機能もWebKitの物を使うことになる。Operaの拡張ストアにOPRでアクセスするとOPR対応版だけが表示されるが、その数はまだ多くはない。そこで、OPR拡張がないならChrome拡張を使えばいいじゃない!というのが本題である(前置き長ぇ。
Download Chrome Extensionを導入することで、普通にChromeストアから拡張をインストールできる。
普通にChromeのストアに行っても、OPRでは「Chromeに追加」が表示されないので、手動でインストールせねばならない。
まず、crxファイルをDLする。crxはhttp://clients2.google.com/service/update2/crx?response=redirect&x=id%3D
と%26uc%26lang%3Den-US&prod=chrome
で、IDをはさんだURLで入手できる。IDは拡張の個別ページのURLに含まれている、32文字くらいのアスキー文字列。例えばLastPassのhttps://chrome.google.com/webstore/detail/lastpass/hdokiejnpimakedhajhdlcegeplioahd
ならhdokiejnpimakedhajhdlcegeplioahd
がそうなので、crxのURLはhttp://clients2.google.com/service/update2/crx?response=redirect&x=id%3Dhdokiejnpimakedhajhdlcegeplioahd%26uc%26lang%3Den-US&prod=chrome
となる。OPRで入力しても「Operaのストアじゃないからダメ」といわれるだけなので、IEなどで適当なフォルダに保存する。
あとは野良拡張と同じ。OPRの拡張管理画面にドロップすると一覧に追加され確認が出るので、吹き出しの「インストール」を押せばおk。
Opera Next 16では、opera://flags内のDynamic extension toolbar scaling
を有効にすることで、アイコンの表示数を増やすことができるが、ウィンドウ幅依存なので表示しきれない可能性がある。
ただし注意点がいくつかある。OPRの拡張バーは現状、最初の6個までしかアイコンを表示できない。非表示でも拡張自体は有効だが、アクセス手段がアイコンしかない場合は実質、利用不可能となる。アイコンの並び順=インストール順なので、アイコン必須の拡張からインストールし、一時的にアイコンが必要になった場合に上位の拡張を無効化するなどの工夫が必要となる。
以下、試してみたよさげな拡張。
OPRストア
LastPass -- バージョン情報に「Chrome版」とある。
AdBlock
Chromeストア
はてなブックマーク GoogleChrome 拡張
Tab Manager -- タブ一覧。ファビコンのみ、あるいはファビコンとタイトル。
Tabs Outliner -- タブ一覧。ファビコンとタイトル。ポップアップでなく別ウィンドウ。OPRに未実装の「ウィンドウ間でのタブ移動」が可能。
Tab Expose -- タブ一覧。サムネイル。
theTabs -- タブ一覧。サムネイル。ポップアップでなく独立タブ。ひとつめのウィンドウのみ。
Zoom Text Only -- テキストのみ拡縮。ショートカットキーによる動作だが、設定はアイコンからしかできない。
PageRank -- Googleウェブ履歴にちゃんとタイトルが残る物の中で、シンプルなやつ。
AutoPatchWork
ToggleLink: Select Text From Link -- リンク上でshiftを叩くとただの文字列になる。5秒で元に戻る。
AutoHotKey_Lで、EverythingSearchのカラムとウィンドウ幅を自動調整してみた。
一般的なListView等には「Ctrl+NumpadAdd(テンキーのプラス)」でカラム(列)幅の自動調整を行うショートカットが存在するらしく、Everythingも例外ではない。だが当然ながらこの機能はカラム幅を変更するだけで、ウィンドウサイズはそのままだ。ゆえに、普段はウィンドウを小さくしている場合、面倒なことになる。
Everythingの1.3βにはウィンドウサイズの変更ショートカットがあり、「自動」もあるのだが、この機能はマルチモニタ環境を考慮しないため、ひとつのモニタに収まりきらない可能性がある。そもそも二回もショートカット使うというのがスマートでない。自動化すべきである。
というわけで、Everything上でCtrl+NumpadAddすると、カラム幅を自動調節したのち、それがすべて見渡せるようにウィンドウ幅も自動調節する(ウィンドウのあるモニタ幅が上限)AHKスクリプト書いた。少し変えればEverything以外にも使えるかも?
;--------------------------------------------------------------------------------------
; Everything
;--------------------------------------------------------------------------------------
#IfWinActive ahk_class ahk_class EVERYTHING
$^NumpadAdd::
ACtrl :=
while ("EVERYTHING_LISTVIEW1" != ACtrl) {
ControlFocus, EVERYTHING_LISTVIEW1
ControlGetFocus, ACtrl
}
Send, ^{NumpadAdd}
F12::
WinGetPos, WinX, WinY, WinW, WinH
WinYC := WinH // 2 + WinY, WinXC := WinW // 2 + WinX
MntNum := 2 ; 有効なモニタ数(頻繁に変更するなら↓をアンコメント)
;~ SysGet, MntNum, 80
Loop, %MntNum%
{
SysGet, Mnt, MonitorWorkArea, %A_Index%
if (MntTop < WinYC) && (WinYC < MntBottom) && (MntLeft < WinXC) && (WinXC < MntRight)
break
}
if (MntLeft < WinX)
AftWinX := WinX
else
AftWinX := MntLeft
AftWinW := MntRight - AftWinX
Sleep, 50
SysGet, FW, 32
SysGet, VSW, 2
ControlGet, Hwnd, Hwnd, , EVERYTHING_LISTVIEW1
GetScrollInfoAll(Hwnd, SB_HORZ, , nMax)
AftWinW2 := nMax + VSW + FW * 2 + 1
if (AftWinW > AftWinW2)
AftWinW := AftWinW2
WinMove, , , %AftWinX%, , %AftWinW%
return
; https://sites.google.com/site/agkh6mze/strage 窓調査.ahk
GetScrollInfoAll(hwnd, type, ByRef nMin="", ByRef nMax="", ByRef nPage="", ByRef nPos="", ByRef nTrackPos="") {
VarSetCapacity(SCROLLINFO, 7*4, 0x00)
NumPut(28, SCROLLINFO, 0, "Int")
NumPut(0x17, SCROLLINFO, 4, "Int") ; SIF_RANGE & SIF_PAGE & SIF_POS & SIF_TRACKPOS = 0x17
nMin:="", nMax:="", nPage:="", nPos:="", nTrackPos:=""
if (!DllCall("user32.dll\GetScrollInfo", "UInt", hwnd, "Int",type, "Ptr", &SCROLLINFO, "Int"))
Return false
nMin := NumGet(SCROLLINFO, 8, "Int")
nMax := NumGet(SCROLLINFO, 12, "Int")
nPage := NumGet(SCROLLINFO, 16, "Int")
nPos := NumGet(SCROLLINFO, 20, "Int")
nTrackPos := NumGet(SCROLLINFO, 24, "Int")
Return true
}
横スクロールバーの範囲を取得して、ウィンドウサイズをそれ+縦スクロールバーの幅に拡縮してるだけ。
たまにちゃんと働かんことがあるのだが、何度か繰り返す運用でカバー。
AutoHotKey_Lで、Operaの起動を便利にしてみた。 ver.2.3.0
AutoHotKey_Lで、Operaの起動を便利にしてみた。をちょっと強化。AutoSave.winをブックマークに変換する際に、スタックごとにフォルダで分ける機能の追加。あと小規模改善。
; --------- --------- --------- --------- ---------
; RunOpera.ahk ver.2.3.0
; --------- --------- --------- --------- ---------
#WinActivateForce
OperaPath := "R:\Opera" ; Opera.exeの存在するパス。末尾"\"は不可。
if %0% ; 引数があった場合、Operaのパスと見なす
OperaPath = %1%
SessionsPath := OperaPath "\profile\sessions" ; セッションフォルダのフルパスを指定。末尾"\"は不可。
BookmarksPath := OperaPath "\profile\bookmarks.adr" ; bookmarks.adrのフルパスを指定。
TimeLimit := 30 ; 最終セッション退避確認ダイアログを自動でスキップするまでの秒数。
AutoConvSW := 1 ; 最終セッションを退避させる場合、自動でブックマークに変換する設定。 0:変換しない。 1:変換する。
ACMoveSW := 1 ; 自動変換(↑)したセッションを隔離する設定。 0:隔離しない。 1:隔離する。
StackSW := 1 ; スタックの設定。 0:無視する。 1:スタックごとにフォルダを分ける。
HisReadSW := 0 ; タブ履歴(タブで表示したページの履歴)の設定。 0:最新のみ読み込む。 1:すべて読み込む。
His0PrefixSW := 0 ; タブ履歴がなかった場合のプレフィックスの設定。 0:なし。 1:0を明示する。
WriteTrgSW := 1 ; ブックマーク情報を書き込む場所の設定。 0:セッションフォルダにテキストで保存。 1:bookmarks.adrに書き込む。
; --------- --------- --------- --------- ---------
IfNotExist, %SessionsPath%\
{
MsgBox, 16, , セッションフォルダが見つかりません。`n%SessionsPath%
ExitApp
}
If WriteTrgSW {
IfNotExist, %BookmarksPath%
{
MsgBox, 16, , bookmarks.adrが見つかりません。`n%BookmarksPath%
ExitApp
}
}
IfNotExist, %SessionsPath%\autosave.win
{
IfExist, %SessionsPath%\autosave.win.bak
FileMove, %SessionsPath%\autosave.win.bak, %SessionsPath%\autosave.win
}
IfExist, %SessionsPath%\autosave.win
{
FileCopy, %SessionsPath%\autosave_RunOpBak.win, %SessionsPath%\autosave_RunOpBak2.win, 1 ; ↓セッション退避etc.を実行しない時も念のため2世代バックアップ。
FileCopy, %SessionsPath%\autosave.win, %SessionsPath%\autosave_RunOpBak.win, 1
GoSub, SessionAnalyze
}
FileCopy, %OperaPath%\ui\fastforward_backup.ini, %OperaPath%\ui\fastforward.ini, 1 ; アップデートのたびにFastForward.iniが初期化される問題の対策。
Run, %OperaPath%\opera.exe, , , OpPid
WinWait, ahk_pid %OpPid%, , 60
if !ErrorLevel {
ifWinExist, Opera へようこそ ahk_pid %OpPid%
{
WinActivate
WinWaitClose
}
} else {
return
}
WinWait, マスターパスワードを入力してください ahk_pid %OpPid%, , 5
If ErrorLevel
return
WinSet, Topmost, ON
WinActivate
Sleep, 300
ExitApp
; --------- --------- --------- --------- ---------
SessionAnalyze:
FileRead, OSession, *P65001 *t %SessionsPath%\autosave.win
If ErrorLevel {
MsgBox, 48, , Sessionの読み込みに失敗しました。`n%SessionsPath%\autosave.win
return
}
Idx := 0
WinTitles := ""
Loop {
Idx := RegExMatch(OSession, "\[(\d+)history url\][\s\S]+?count=(\d+)", $, Idx + 1)
If !Idx
Break
WinNum := $1
HisNum := $2 - 1
HisTitle := GetHisTitle(OSession, WinNum, HisNum, Idx)
WinTitles .= " " HisTitle "`n"
}
If !WinTitles
return
SetTimer, RenewCount, 1000
MsgBox, 259, 退避確認(30秒後に通常起動), 起動時に最終セッションを退避しますか?`n`n◆最終セッションの内容◆`n%WinTitles%, %TimeLimit%
SetTimer, RenewCount, Off
IfMsgBox, Yes
{
TimeNum := A_Now
BackupName := TimeNum ".win"
Loop {
FileMove, %SessionsPath%\autosave.win, %SessionsPath%\%BackupName%
If !ErrorLevel ; 最終セッションのリネームに成功なら
Break
BackupName := TimeNum "_" A_Index ".win"
}
FileDelete, %SessionsPath%\autosave.win.bak
If AutoConvSW
GoSub, SessionConvert
}
IfMsgBox, Cancel
ExitApp
return
; --------- --------- --------- --------- ---------
RenewCount:
IfWinExist, 退避確認( ahk_class #32770
{
TimeLimit--
WinSetTitle, 退避確認(%TimeLimit%秒後に通常起動)
if !TimeLimit
SetTimer, RenewCount, Off
}
return
; --------- --------- --------- --------- ---------
SessionConvert:
If WriteTrgSW
FileCopy, %BookmarksPath%, %BookmarksPath%_%A_Now% ; bookmarks.adrのバックアップは無制限なので注意
CmdLine := SessionsPath "\" BackupName
SplitPath, CmdLine, , , , SessionName
Result := "#FOLDER`n NAME=[Session] " SessionName "`n`n"
ResultBuf := Result
Idx := 0
BefGroupNum := 0
His0Prefix :=
Loop {
Idx := RegExMatch(OSession, "\[(\d+)\][\s\S]+?group=(\d+)[\s\S]+?\[\1history url\][\s\S]+?count=(\d+)", $, Idx + 1)
If !Idx
Break
WinNum := $1
GroupNum := $2
HisNum := $3 - 1
HisTitle := GetHisTitle(OSession, WinNum, HisNum, Idx)
HisUrl := GetHisUrl(OSession, WinNum, HisNum, Idx)
if StackSW
if (BefGroupNum != GroupNum) {
if BefGroupNum
Result .= "-`n`n"
if GroupNum
Result .= "#FOLDER`n NAME=[Stack] " GroupNum "`n`n"
BefGroupNum := GroupNum
}
If !HisReadSW { ; タブ履歴を無視する場合
Result .= "#URL`n NAME=" HisTitle "`n URL=" HisUrl "`n`n"
Continue
}
StringLen, HisNumLen, HisNum
If !HisNum {
if His0PrefixSW
His0Prefix := "[" ZeroSupply(HisNum, HisNumLen) "] "
Result .= "#URL`n NAME=" HisPrefix HisTitle "`n URL=" HisUrl "`n`n"
Continue
}
Result .= "#FOLDER`n NAME=" HisTitle "`n`n#URL`n NAME=[" ZeroSupply(HisNum, HisNumLen) "] " HisTitle "`n URL=" HisUrl "`n`n"
Loop, %HisNum% {
HisNum--
HisTitle := GetHisTitle(OSession, WinNum, HisNum, Idx)
HisUrl := GetHisUrl(OSession, WinNum, HisNum, Idx)
Result .= "#URL`n NAME=[" ZeroSupply(HisNum, HisNumLen) "] " HisTitle "`n URL=" HisUrl "`n`n"
}
Result .= "-`n`n"
}
If (ResultBuf == Result)
return
Result .= "-`n`n"
If WriteTrgSW {
FileRead, BMBuf, *t *P65001 %BookmarksPath%
StringGetPos, Idx, BMBuf, #DELETED
if !Idx {
StringMid, BMBufP, BMBuf, 1, Idx
StringMid, BMBufS, BMBuf, Idx + 1
Result := BMBufP Result BMBufS
FileDelete, %BookmarksPath%
}
FileAppend, %Result%, %BookmarksPath%, CP65001
} Else {
FileDelete, %CmdLine%.txt
FileAppend, %Result%, %CmdLine%.txt, CP65001
}
If ACMoveSW {
IfNotExist, %SessionsPath%\AutoConv\
FileCreateDir, %SessionsPath%\AutoConv\
FileMove, %CmdLine%, %SessionsPath%\AutoConv\%BackupName%
If ErrorLevel
MsgBox, 48, , 変換済みセッションの移動に失敗しました。`n%SessionsPath%\AutoConv\
}
return
; --------- --------- --------- --------- ---------
GetHisTitle(OSession, WinNum, HisNum, Idx){
RegExMatch(OSession, "\[" WinNum "history title\][\s\S]+?" HisNum "=(.*?)\r?\n", $)
return $1
}
GetHisUrl(OSession, WinNum, HisNum, Idx){
RegExMatch(OSession, "\[" WinNum "history url\][\s\S]+?" HisNum "=(.*?)\r?\n", $)
return $1
}
ZeroSupply(Num, NumDigit){
StringLen, NumLen, Num
LoopNum := NumDigit - NumLen
Loop, %LoopNum%
Num := 0 Num
return, Num
}
セッション確認ダイアログ
ブックマーク変換例。