|
/*
[MngBackgoundColorText.sbp]
背景色を管理するクラスの定義。
Fontは窓に紐づくが、背景色は描画のタイミングで明示的に設定するものなので、
必ずしも紐づける必要はない。--> このクラスで別管理する。
※IME変換中の文節などは、下線ではなく背景色で対応。変換中の項目を青味かける。
【使い方】
1.以下のメソッドを用いて、背景色とフォントを設定する。
SetColorForTextOnExitBox( dwTextColorNew As DWord, dwTextLigthColorNew As DWord )
SetBackgoundColorForTextOnExitBox( dwColorTextBackgroundNew As DWord )
SetLogiaclFontForIME( ByRef stLogFont As LOGFONT )
2.WM_CTLCOLOREDITメッセージを捉まえたら、以下のメソッドを呼び出して、
その戻り値をreturnする。
Function WndProcFor_WM_CTLCOLOREDIT( hDC As HDC, hEdit As HWND ) As Long
3.下記のIME関連のメッセージを捉まえたら、
→ WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_NOTIFY
以下のメソッドを呼び出す。
Function ImmProc( hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM ) As Char
このメソッドが「FALSE」を返した時はデフォルト動作なので、スルーすること(デフォルト窓プロシージャへ流す)。
'// 一方で「TRUE」を返した時は、独自処理済みなので窓プロシージャを戻り値0で抜けること。
※IME変換中の背景色は、固定値で itsRgbColorImmBackground で持っているので、
必要であれば変更のこと。
以上ー。
*/
'// 各種、必要なWinAPI32の宣言と関数定義。
'// #include にも一部あるが、不足なので、ここで明示的に宣言する。
TypeDef HIMC = DWord
Declare Function ImmSetCompositionFont Lib "imm32" Alias "ImmSetCompositionFontA" (hIMC As HIMC, ByRef lplf As LOGFONT) As Long
Const CFS_DEFAULT = &H0000
Const CFS_RECT = &H0001
Const CFS_POINT = &H0002
Const CFS_FORCE_POSITION = &H0020
Const CFS_CANDIDATEPOS = &H0040
Const CFS_EXCLUDE = &H0080
Type COMPOSITIONFORM
dwStyle As Long
ptCurrentPos As POINTAPI
rcArea As RECT
End Type
Declare Function ImmSetCompositionWindow Lib "imm32" (hIMC As HIMC, ByRef lpCompForm As COMPOSITIONFORM) As Long
Declare Function ImmGetContext Lib "imm32" (hWnd As HWND) As Long
Declare Function ImmReleaseContext Lib "imm32" (hWnd As HWND, hIMC As HIMC) As Long
'// +++ ここから、api_imm.sbp に無い宣言を自前で追加 +++
Const GCS_RESULTSTR = &H0800
Const GCS_COMPSTR = &H0008
Const GCS_COMPATTR = &H0010
Const GCS_DELTASTART = &H0100
'// attribute for COMPOSITIONSTRING Structure
Const ATTR_INPUT = &H00
Const ATTR_TARGET_CONVERTED = &H01
Const ATTR_CONVERTED = &H02
Const ATTR_TARGET_NOTCONVERTED = &H03
Const ATTR_INPUT_ERROR = &H04
Const ATTR_FIXEDCONVERTED = &H05
Declare Function ImmGetCompositionString Lib "imm32" Alias "ImmGetCompositionStringA" (hIMC As HIMC, dwIndex As DWord, lpBuf As BytePtr, dwBufferSize As DWord ) As Long
Const IMM_ERROR_NODATA = -1
Const IMN_OPENCANDIDATE = &H0005
Type CANDIDATEFORM
dwIndex As DWord
dwStyle As DWord
ptCurrentPos As POINTAPI
rcArea As RECT
End Type
Declare Function ImmSetCandidateWindow Lib "imm32" (hIMC As HIMC, ByRef lpCandidateFoem As CANDIDATEFORM) As Char
'// --- ここまで(imm.hを参照して定義 [C:\borland\bcc55\Include\imm.h] ) ---
'// ※Web上で参照するなら http://katahiromz.web.fc2.com/mathai/immdev.h 辺り。
'// +++ 以下も追加 +++
Declare Function ExtTextOut Lib "Gdi32" Alias "ExtTextOutA" ( hDC As HDC, nXStart As Long, nYStart As Long, dwOptions As DWord, ByRef lpRect As RECT, lpString As BytePtr, cbString As Long, lpDx As *Long)
Const ETO_CLIPPED = &H0004
'// --- ここまで ---
#include
'// WsColorVector クラスを利用のため。→外部に公開時はソコを削除しようか。
Class ManagementAndDrowTextBackGroundColor
itsBrushTextBackground As HBRUSH
itsRgbColorTextBackground As DWord
itsRgbColorText As Dword
itsRgbColorTextAtNotFocused As Dword
itsImmLastLength As DWord
itsImmLastLengthMax As DWord
itsLogFontIME AS LOGFONT
itsLogFontIME_IsEnable As Char
itsRgbColorImmBackground As DWord '暫定処置(変換時の属性処理が不完全)
'// ※将来に、calloc() を Heep〜に置き換える考慮でここにまとめておく。
Function _HeapWorikngArea( dwSize As DWord ) As VoidPtr
_HeapWorikngArea = calloc( dwSize )
End Function
Sub _FreeWorkingArea( pMark As VoidPtr )
free( pMark )
End Sub
Public
Sub ManagementAndDrowTextBackGroundColor()
'テキストの色と背景色(任意に変えることを考慮して追加)
itsBrushTextBackground = NULL '処理の共通化のために、NULLで初期化
This.SetColorForTextOnExitBox( RGB( &h00,&h00,&h00 ), DISABLED_RGB )
This.SetBackgoundColorForTextOnExitBox( RGB( &hFF,&hFF,&hFF ) )
'IMMの変換領域の初期値
itsImmLastLength = 0
itsRgbColorImmBackground = RGB( &H44, &H44, &H44 ) '// 変換中の背景色【暫定固定値】
itsLogFontIME_IsEnable = FALSE
End Sub
Sub ~ManagementAndDrowTextBackGroundColor()
If itsBrushTextBackground <> NULL Then
DeleteObject( itsBrushTextBackground )
End If
End Sub
'文字色を設定(エディット窓の文字色+非フォーカス時の淡色)。
Sub SetColorForTextOnExitBox( dwTextColorNew As DWord, dwTextLigthColorNew As DWord )
itsRgbColorText = dwTextColorNew
itsRgbColorTextAtNotFocused = dwTextLigthColorNew
End Sub
'文字の背景色を設定(エディット窓の背景色)
Sub SetBackgoundColorForTextOnExitBox( dwColorTextBackgroundNew As DWord )
itsRgbColorTextBackground = dwColorTextBackgroundNew
If itsBrushTextBackground <> NULL Then
DeleteObject( itsBrushTextBackground )
End If
itsBrushTextBackground = CreateSolidBrush( itsRgbColorTextBackground )
End Sub
'文字色の取得
Function GetColorForTextOnExitBox() As DWord
GetColorForTextOnExitBox = itsRgbColorText
End Function
'文字の背景色の取得
Function GetBackgoundColorForTextOnExitBox() As DWord
GetBackgoundColorForTextOnExitBox = itsRgbColorTextBackground
End Function
'非フォーカス時の淡色化の有無を取得
Function IsTextColorUseLight()
IsTextColorUseLight = (DISABLED_RGB <> itsRgbColorTextAtNotFocused)
End Function
'IME変換中に利用するフォントの「論理フォント構造体」を設定する。→内部でコピーして保持する。
Sub SetLogiaclFontForIME( ByRef stLogFont As LOGFONT )
memcpy( VarPtr(itsLogFontIME), VarPtr(stLogFont), sizeOf(LOGFONT) )
itsLogFontIME_IsEnable = TRUE
End Sub
'// 親窓に WM_CTLCOLOREDIT が通知された時の動作。
'// ※lParam As HWND, wParam As HDC で対象が渡されてくるので、
'// 適切に文字色と背景色を設定して返すと、その色でテキストを描画してくれる。
'// ref.[コントロールの背景色/フォント変更] - http://www50.tok2.com/home2/StillGreen/knowledge/program/backcolor.htm
Function WndProcFor_WM_CTLCOLOREDIT( hDC As HDC, hEdit As HWND ) As Long
'テキストの文字色
SetBkMode( hDC, OPAQUE ) '背景を塗りつぶしモードに設定
If itsRgbColorTextAtNotFocused = DISABLED_RGB Then '非フォーカス時の扱いで分岐
SetTextColor( hDC, itsRgbColorText )
Else
If GetFocus() = hEdit Then
SetTextColor( hDC, itsRgbColorText )
Else
SetTextColor( hDC, itsRgbColorTextAtNotFocused )
End If
End If
'テキストの背景色
SetBkColor( hDC, itsRgbColorTextBackground ) 'for テキストが書かれている部分
WndProcFor_WM_CTLCOLOREDIT = itsBrushTextBackground As Long 'for テキストが書かれていない部分
End Function
Private
Function IsDarkColorBackgroundForIME()
Dim colorCls As WsColorVector( itsRgbColorTextBackground )
Dim xBrightness As Single
xBrightness = colorCls.GetBrightness() '0.0〜1.0で規格化された輝度"
If xBrightness < 0.4 Then
'充分にブラックに近い場合は(輝度が低い)Yesとする。
IsDarkColorBackgroundForIME = TRUE
'変換中背景色は固定的に下記を用いる。
itsRgbColorImmBackground = RGB( &H44, &H44, &H44 )
Else
IsDarkColorBackgroundForIME = FALSE
End If
End Function
Public
'// エディットボックスをサブクラス化して、IME関連の下記のメッセージを捉まえたときの動作。
'// --> WM_IME_STARTCOMPOSITION, WM_IME_COMPOSITION, WM_IME_ENDCOMPOSITION, WM_IME_NOTIFY
'// (※通知先は、親窓かもしれない?その場合はサブクラス化不要)
'//
'// この関数が「FALSE」を返した時はデフォルト動作なのでスルーすること。
'// 一方で「TRUE」を返した時は、独自処理済みなので窓プロシージャを戻り値0で抜けること。
Function ImmProc( hWnd As HWND, dwMsg As DWord, wParam As WPARAM, lParam As LPARAM ) As Char
Dim hImc As HIMC
ImmProc = FALSE
'Win Vistaより前では、IMM関連の挙動が異なるので弾く。
If OS_Vista > GetOsType() Then
Exit Function
End If
'【TODO】変換中属性の処理が不完全なので、一定値以下の黒背景でのみ、本関数を有効にする。
'※ここを抜けた時点で、itsRgbColorImmBackground は設定済み。
If FALSE = IsDarkColorBackgroundForIME() Then
Exit Function
End If
'// IMMコンテキストを取得
hImc = ImmGetContext( hWnd )
If hImc = NULL Then
Exit Function
End If
Select Case dwMsg
'// 文字変換開始
Case WM_IME_STARTCOMPOSITION
ImmProc = ImeStartComposition( hWnd, hImc, wParam, lParam )
'// 文字変換処理中
Case WM_IME_COMPOSITION
If lParam = 0 Then
'// 変化はあったがlParam=0(変化の内容が未定義)は、
'// 「1byteのみ入力で、やっぱキャンセル」された状態の様子(仕様見つからず。実機確認)。
'// 一旦「WM_IME_ENDCOMPOSITION」扱いする。
'// …で、本来は「ImeEndComposition()」を呼びたいのだが、再描画範囲(itsImmLastLengthMax)が
'// ただしく機能していないようなので、、、無理やりだが「全体再描画」で対応。
'// 【ToDo】もう少しスマートな再描画をしたい。at 2015.09.02
InvalidateRgn( hWnd, NULL, TRUE )
Else
ImmProc = ImeComposition( hWnd, hImc, wParam, lParam )
End If
'// 文字変換終了
Case WM_IME_ENDCOMPOSITION
ImmProc = ImeEndComposition( hWnd, hImc, wParam, lParam )
'// その他のIME関連の通知
Case WM_IME_NOTIFY
Select Case wParam
'// 変換候補リストが表示されようとしている
Case IMN_OPENCANDIDATE
ImmProc = ImmSetCandidateWindowPos( hWnd, hImc )
Case Else
ImmProc = FALSE
End Select
Case Else
ImmProc = FALSE
End Select
'// IMMコンテキストを解放
ImmReleaseContext( hWnd, hImc )
End Function
Private
Function ImeStartComposition( hWnd As HWND, hImc As HIMC, wParam As WPARAM, lParam As LPARAM ) As Char
itsImmLastLength = 0 '// 内部変数を初期化
itsImmLastLengthMax = 0 '// 内部変数を初期化
ImeStartComposition = FALSE '// IMEに対しては何もしない(デフォルト動作)。
End Function
Function ImeEndComposition( hWnd As HWND, hImc As HIMC, wParam As WPARAM, lParam As LPARAM ) As Char
Dim hDC As HDC
Dim stPoint As POINTAPI
Dim stRect As RECT
Dim fontExtentPoint As SIZE
Dim data As Word
'// 変換窓で描画した範囲を「再描画」して後始末する。
hDC = GetDC( hWnd )
data = &HA4A4 '// 全角の「い」に相当。
GetTextExtentPoint32( hDC, VarPtr(data), sizeOf(Word), fontExtentPoint )
GetCaretPos( stPoint )
stRect.right = stPoint.x
stRect.top = stPoint.y
stRect.left = stPoint.x + itsImmLastLengthMax
stRect.bottom = stPoint.y + fontExtentPoint.cy
InvalidateRect( hWnd, stRect, TRUE )
ReleaseDC( hWnd, hDC )
ImeEndComposition = FALSE '// IMEに対しては何もしない(デフォルト動作)。
End Function
Function ImeComposition( hWnd As HWND, hImc As HIMC, wParam As WPARAM, lParam As LPARAM )
Dim pszImmString As BytePtr
Dim pAttrArray As *Char
ImeComposition = FALSE
pszImmString = NULL
If (lParam and GCS_RESULTSTR) <> 0 Then
'// 文字列の変換状態が全て確定した場合に GCS_RESULTSTRフラグがONになる
'// →確定した文字列を取得する。
pszImmString = GetCompositionString( hImc, GCS_RESULTSTR )
If pszImmString <> NULL Then
'// 呼び出し元のエディット窓に対して、確定文字列を挿入する。
SendMessage( hWnd, EM_REPLACESEL, TRUE, pszImmString )
'// 利用した領域を解放
_FreeWorkingArea( pszImmString )
'// IMEに対して独自処理をしたとして、デフォルト動作をSkipするよう設定。
ImeComposition = TRUE
End If
End If
'// ※「確定」状態で且つ、次の「未確定(=変換状態に変化)がある」状態もあるので留意。
'// ↑↓なので★続けて★処理する。
If (lParam and GCS_COMPSTR) <> 0 Then
'// 変換状態に何か変化があった場合に GCS_COMPSTRフラグがONになる
'// →変換中(IME途中)の文字列を取得する。
pszImmString = GetCompositionString( hImc, GCS_COMPSTR )
If pszImmString <> NULL Then
'// 文字列が取れたなら、その属性(下記の5種類)を取得する(1文字当たり8bitの定数)。
'// ・選択されていなく、変換されていない文字。:ATTR_INPUT
'// ・選択されていて、変換されている文字。
'// ・選択されていなく、変換されている文字。
'// ・選択されていて、変換されていない文字。
'// ・無効な文字
pAttrArray = GetCompositionString( hImc, GCS_COMPATTR ) As *Char
'// 変換中の文字列を、IME窓へ明示的に表示(IME窓=Composition窓)
'// →背景色などを任意にできる♪
DisplayCompositionString( hWnd, pszImmString, pAttrArray )
'// 利用した領域を解放
_FreeWorkingArea( pszImmString )
If pAttrArray <> NULL Then
_FreeWorkingArea( pAttrArray )
End If
'// IMEに対して独自処理をしたとして、デフォルト動作をSkipするよう設定。
ImeComposition = TRUE
End If
End If
End Function
'// ImmGetCompositionStringA()に対するラッパー関数。【Unicodeは未考慮】
Function GetCompositionString( hImc As HIMC, dwType As DWord ) As BytePtr
Dim nSize As Long
Dim pszImmString As BytePtr
nSize = ImmGetCompositionString( hImc, dwType, NULL, 0 )
if nSize > 0 Then
pszImmString = _HeapWorikngArea( nSize +1 )
ImmGetCompositionString( hImc, dwType, pszImmString, nSize )
pszImmString[ nSize ] = NULL
'// この文字列( or 属性配列)がNULL文字で終わるとは限らない。
'// 従って、bufに返された文字列の長さを戻り値によって判定しなければならない。
'// http://nienie.com/~masapico/api_ImmGetCompositionString.html
'// →文字列と仮定して、NULLを終端に追加することで、返却先に終端を伝えるようにしようか。
'// ただし、属性配列はATTR_INPUT=0x00なので、終端の区別が出来ない。利用時は注意。
End If
GetCompositionString = pszImmString
End Function
'// IME変換中文字列を自前で描画する。
'// ⇒これにより、背景色の設定などが可能になる。
Sub DisplayCompositionString( hWnd As HWND, pszImmString As BytePtr, pAttrArray As *Char )
Dim hDC As HDC
Dim stPoint As POINTAPI
Dim stClientRect As RECT
Dim stImeRect As RECT
Dim hBrush As HBRUSH
Dim hPen As HPEN
Dim hFont As HFONT
Dim fontExtentPoint As SIZE
Dim data As Word
hDC = GetDC( hWnd )
'【TODO】変換中の文字列である旨をユーザーへ伝える方法として、背景色を変更。
' 通常のIMEだと下線とかフォント側の変更でやってるね。
' 背景色の方が楽なので、暫定でこの方法。
hBrush = SelectObject( hDC, CreateSolidBrush( itsRgbColorImmBackground ) )
hPen = SelectObject( hDC, CreatePen( PS_SOLID, 1, itsRgbColorImmBackground ) )
If TRUE = itsLogFontIME_IsEnable Then
'【TODO】本来は、属性に応じて一文字ごとに下線の有り無し等設定すべきだが、一括設定で暫定。
hFont = SelectObject( hDC, CreateFontIndirect( itsLogFontIME ) )
End If
data = &HA4A4 '// 全角の「い」に相当。
GetTextExtentPoint32( hDC, VarPtr(data), sizeOf(Word), fontExtentPoint )
GetCaretPos( stPoint )
GetClientRect( hWnd, stClientRect )
If itsImmLastLength > 0 Then
'// 背景を塗りつぶす(だいぶ無理やり…)
stImeRect.left = stPoint.x
stImeRect.top = stPoint.y
stImeRect.bottom = stPoint.y + fontExtentPoint.cy
stImeRect.right = stPoint.x + itsImmLastLength
If stImeRect.right > stClientRect.right Then
stImeRect.right = stClientRect.right -1
End If
Rectangle( hDC,
stImeRect.left, stImeRect.top,
stImeRect.right, stImeRect.bottom )
If itsImmLastLength > itsImmLastLengthMax Then
itsImmLastLengthMax = itsImmLastLength
End If
End If
itsImmLastLength = ( lstrlen( pszImmString ) +1 ) * (fontExtentPoint.cx / 2) '// 横幅を更新
_TextOutWithAttr( hDC,
VarPtr(stPoint),
VarPtr(fontExtentPoint),
stClientRect.right,
pszImmString,
pAttrArray )
DeleteObject( SelectObject( hDC, hBrush ) ) '// HDCから解放したBRUSHは、本関数内で作成したものなので破棄する。
DeleteObject( SelectObject( hDC, hPen ) ) '// HDCから解放したPENは、本関数内で作成したものなので破棄する。
If TRUE = itsLogFontIME_IsEnable Then
DeleteObject( SelectObject( hDC, hFont ) ) '// HDCから解放したFONTは、本関数内で作成したものなので破棄する。
End If
ReleaseDC( hWnd, hDC )
End Sub
'// IME属性(選択中、変換中etc)に応じて異なる背景色で表示
Sub _TextOutWithAttr( hDC As HDC, pstPoint As *POINTAPI, pFontExtentPoint As *SIZE, nMaxClientRightPos As Long, pszImmString As BytePtr, pAttrArray As *Char )
Dim textLength As DWord
Dim stImeRectParts As RECT
Dim nX As Long
Dim nY As Long
Dim rgbList[ 6 ] As DWord
Dim sameAttrLength As DWord
Dim pMarkStart As BytePtr
Dim nDX As DWord
Dim i As DWord
'// 【暫定】IME変換中の背景色を元に、変換対象や選択状態の背景色を生成。
rgbList[ ATTR_INPUT ] = itsRgbColorImmBackground '// RGB( &H44, &H44, &H44 ) '// 入力直後
rgbList[ ATTR_TARGET_CONVERTED ] = itsRgbColorImmBackground + RGB( &H33, &H33, &H77 ) '// 変換中!→青味かける。
rgbList[ ATTR_CONVERTED ] = itsRgbColorImmBackground '// 変換中だけど未選択。→「入力直後」と同じ色にする。
rgbList[ ATTR_TARGET_NOTCONVERTED ] = itsRgbColorImmBackground '// 無い?→「入力直後」と同じ色にする。
rgbList[ ATTR_INPUT_ERROR ] = itsRgbColorImmBackground '// 無い?→「入力直後」と同じ色にする。
'// 参考サイトhttp://nienie.com/~masapico/api_ImmGetCompositionString.html
SetTextColor( hDC, itsRgbColorText )
textLength = lstrlen( pszImmString )
'// pAttrArrayにしたがって、『属性の切り替わる単位で』背景書を変換して書こうか。
'// http://nienie.com/~masapico/api_ImmGetCompositionString.html
'//
nDX = pFontExtentPoint->cx / 2
nX = pstPoint->x
nY = pstPoint->y
i = 0
pMarkStart = pszImmString
While( i < textLength )
'// 属性の変わる直前までをピックアップ
sameAttrLength = 1
While( (pAttrArray[ i ] = pAttrArray[ i+1 ]) and (i < textLength) ) '// 終端にNULLが入る仕様を利用(オーバーフローしない)
sameAttrLength++
i++
Wend
'// 属性単位で文字列を描画
SetBkColor( hDC, rgbList[ pAttrArray[ i ] ] )
stImeRectParts.left = nX
stImeRectParts.top = nY
stImeRectParts.right = nX + sameAttrLength * nDX
stImeRectParts.bottom = nY + pFontExtentPoint->cy
If stImeRectParts.right > nMaxClientRightPos Then
stImeRectParts.right = nMaxClientRightPos -1
i = textLength '// ループを抜けるフラグとして設定。
End If
ExtTextOut( hDC,
nX, nY,
ETO_CLIPPED,
stImeRectParts,
pMarkStart, sameAttrLength,
NULL )
nX = stImeRectParts.right
pMarkStart += sameAttrLength
i++
Wend
End Sub
'// IME変換リストの表示位置を設定する。
Function ImmSetCandidateWindowPos( hWnd As HWND, hImc As HIMC ) As Char
Dim stPoint As POINTAPI
Dim stImmCandidateForm As CANDIDATEFORM
GetCaretPos( stPoint )
ZeroMemory( VarPtr(stImmCandidateForm), Len(stImmCandidateForm) )
stImmCandidateForm.ptCurrentPos.x = stPoint.x
stImmCandidateForm.ptCurrentPos.y = stPoint.y
stImmCandidateForm.dwStyle = CFS_CANDIDATEPOS
ImmSetCandidateWindow( hImc, stImmCandidateForm )
'// その後のIME動作はデフォルトの流れに任せる。
ImmSetCandidateWindowPos = FALSE
End Function
End Class
|