none
CRitchEditCtrlの日本語入力時の選択位置を取得したい

    質問

  • VS2013 MFC C++でソフトを開発しています。
    日本語入力は IMEです。

    添付したソースはCRichEditCtrlを継承して日本語入力の確定をPreTranslateMessageで捕まえて、
    OnCharに飛ばして処理を行うサンプルになっております。
     (CRichEditCtrlでは日本語の確定でOnCharに来ないので このサイトあった対処方法を参考にさせてもらってます)

    CEditでは OnChar内でGetSelを使って選択領域を得ているので、CRitchEditCtrlのサンプルのOnCharの位置でGetSelを使っても上手く取れないのでWM_IME_STARTCOMPOSITION時に取得していました。
    通常の日本語入力時はこれで問題なかったのですが、

    たとえば、 ”ああああとうきょう"  いう既存の文字列の最後にカーソルを移動した状態で「変換」キーを押すと、"とうきょう"が"東京"に変換されます。
    この"とうきょう"の位置 start:4 end:8 を取りたいです。 ちなみにこの操作ではWM_IME_STARTCOMPOSITIONは来ません。

    このような操作でどの領域が変換されたか 取れる方法をご教授ください。

    よろしくお願いします。

    BOOL CTestRichEditCtrl::PreTranslateMessage(MSG* pMsg) 
    {
    	if( pMsg->message == WM_IME_STARTCOMPOSITION )
    	{
    		_SelectStart = TEXTAREA_NONE;
    		_SelectEnd = TEXTAREA_NONE;
    		GetSel( _SelectStart, _SelectEnd );
    
    	} else if( pMsg->message == WM_IME_ENDCOMPOSITION )
    	{
    	} else if( pMsg->message == WM_IME_COMPOSITION )
    	{
    		if( pMsg->lParam & GCS_RESULTSTR )
    		{ // 2バイト入力の確定
    
    			TCHAR tchar[1024];
    
    			HIMC _hImc = ::ImmGetContext( m_hWnd );
    
    			// IME の変換文字列を取得します。
    			int len = ImmGetCompositionStringW( _hImc, GCS_RESULTSTR, NULL, 0 );
    			memset( tchar , NULL, BUFF_SIZE );		
    			ImmGetCompositionStringW( _hImc, GCS_RESULTSTR, tchar, len );
    			ImmReleaseContext( m_hWnd, _hImc );
    
    			for ( int i = 0; i < len; i++ )
    			{
    				PostMessageW( WM_CHAR, (WPARAM)tchar[i], 0 );
    			}
    			ImmReleaseContext( m_hWnd, _hImc );
    		}
    	}
    
       	return CRichEditCtrl::PreTranslateMessage(pMsg);
    }
    
    
    BOOL CTestRichEditCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    	LONG selectStart = -1;
    	LONG selectEnd = -1;
    	GetSel( selectStart, selectStart );  
    
    	CString strPrev;
    	GetWindowText( strPrev );    
    
    	CString strInput;
    	strInput += (TCHAR)nChar;
    	MSG msg;
    
    	// 2バイト文字入力処理
    	while (::PeekMessage(&msg, m_hWnd, WM_CHAR, WM_CHAR, PM_REMOVE) )
    	{// キューに入っている文字列を取得
    		strInput += (TCHAR)msg.wParam;
    	}
    
    	
    	//selectStart/selectEndを使って所定の位置にSetWindowTextで入れなおしたり、諸々の処理を行う
    	SetWindowText( strPrevText + strInput ); -- ①
    
    	CRichEditCtrl::OnChar(nChar, nRepCnt, nFlags);
    }

    2018年4月27日 0:13

回答

  • 前述の方法では正確に変換される文字の位置が取得できない事が判りました。

    GCS_CURSORPOS/GCS_DELTASTARTの時に GetSelではなくて、下記のようにImmGetCompositionStringでカーソル位置を取ろうとしましたが、lDeltaStart が0でlCursorPos は変換された文字数しか戻ってこない状態でした。

    LONG lDeltaStart = ImmGetCompositionString(_hImc, GCS_DELTASTART, NULL, 0);
    LONG lCursorPos = ImmGetCompositionString(_hImc, GCS_CURSORPOS, NULL, 0);

    これから変換される範囲を得たいので、良い方法がありましたら、ご教授お願いします。

    2018年4月30日 9:15

すべての返信

  • 自己スレです。

    まだ完全途中ですが、試行錯誤で添付したソースを考えています。

    日本語入力しないで、「変換」キーだけおしたパターンをとらえて GCS_CURSORPOS/GCS_DELTASTARTでカーソル位置を取得する方法です。

    確かな方法かどうかまだわからない状態です。

    // クラス変数
    LONG SelectStart;
    LONG SelectEnd;
    
    BOOL CVerticalEditCtrl::PreTranslateMessage(MSG* pMsg) 
    {
    	if( pMsg->message == WM_IME_STARTCOMPOSITION ) { 
    		// 2バイト文字入力の開始
    		_SelectStart = -1;
    		_SelectEnd = -1;
    		GetSel( _SelectStart, _SelectEnd );
    	
    		_bStartComposition = TRUE;
    	} else if( pMsg->message == WM_IME_ENDCOMPOSITION ) { 
     		// 2バイト文字入力の終了
    
    		_bStartComposition = FALSE;
    	} else if( pMsg->message == WM_IME_COMPOSITION ) {
    		if ( ! _bStartComposition ) { 
    		   // WM_IME_STARTCOMPOSITIONを介さないで変換 (日本語入力しないで変換ボタンだけ押した場合)
    
    			if ( pMsg->lParam & GCS_CURSORPOS )
    			{
    				if ( ! (pMsg->lParam & GCS_COMPREADSTR ) )
    				{
    					LONG SelectStart2 = -1;
    					GetSel( SelectStart2, _SelectEnd );
    				}
    			}
    			if ( pMsg->lParam & GCS_DELTASTART )
    			{
    				LONG SelectEnd2 = -1;
    				GetSel( _SelectStart, SelectEnd2 );
    				_SelectStart--;   // なぜか1つ減らさないと 位置が合わない
    			}
    		}
    
    		if( pMsg->lParam & GCS_RESULTSTR )
    		{ // 2バイト入力の確定
    
    			// _SelectStart/SelectEnd で変換される文字の位置が入っている
    
    			TCHAR tchar[1024];
    
    			HIMC _hImc = ::ImmGetContext( m_hWnd );
    
    			// IME の変換文字列を取得します。
    			int len = ImmGetCompositionStringW( _hImc, GCS_RESULTSTR, NULL, 0 );
    			memset( tchar , NULL, BUFF_SIZE );		
    			ImmGetCompositionStringW( _hImc, GCS_RESULTSTR, tchar, len );
    			ImmReleaseContext( m_hWnd, _hImc );
    
    			for ( int i = 0; i < len; i++ )
    			{
    				PostMessageW( WM_CHAR, (WPARAM)tchar[i], 0 );
    			}
    			ImmReleaseContext( m_hWnd, _hImc );
    		}
    	}
    
       	return CRichEditCtrl::PreTranslateMessage(pMsg);
    }
    

    2018年4月28日 11:13
  • 前述の方法では正確に変換される文字の位置が取得できない事が判りました。

    GCS_CURSORPOS/GCS_DELTASTARTの時に GetSelではなくて、下記のようにImmGetCompositionStringでカーソル位置を取ろうとしましたが、lDeltaStart が0でlCursorPos は変換された文字数しか戻ってこない状態でした。

    LONG lDeltaStart = ImmGetCompositionString(_hImc, GCS_DELTASTART, NULL, 0);
    LONG lCursorPos = ImmGetCompositionString(_hImc, GCS_CURSORPOS, NULL, 0);

    これから変換される範囲を得たいので、良い方法がありましたら、ご教授お願いします。

    2018年4月30日 9:15