- 追加された行はこのように表示されます。
- 削除された行は
このように表示されます。
{{category 開発情報}}
{{category 陰郎の書いた記事,nolink}}
!!!概要
CForm 派生クラスのイベントマップマクロでは、vchrHard1 などのハードボタンイベントを処理することができません。このページでは、この問題について説明し、これらのボタンイベントを処理する方法を解説します。
!!!説明
!!問題点
以下のコードで示される CMyForm クラスでは、EVENT_MAP マクロでハードボタン1〜4に対して OnHardButton( ) メソッドを割り当てています。
class CMyForm : public CForm {
public:
CMyForm( );
public:
// Form event handlers
Boolean OnOpen(EventType* pEvent, Boolean& bHandled);
Boolean OnClose(EventType* pEvent, Boolean& bHandled);
Boolean OnHardButton(EventType* pEvent, Boolean& bHandled);
private:
// Event map
BEGIN_EVENT_MAP(CForm)
EVENT_MAP_ENTRY(frmOpenEvent, OnOpen)
EVENT_MAP_ENTRY(frmCloseEvent, OnClose)
EVENT_MAP_KEY_DOWN_ENTRY(vchrHard1, 0, OnHardButton)
EVENT_MAP_KEY_DOWN_ENTRY(vchrHard2, 0, OnHardButton)
EVENT_MAP_KEY_DOWN_ENTRY(vchrHard3, 0, OnHardButton)
EVENT_MAP_KEY_DOWN_ENTRY(vchrHard4, 0, OnHardButton)
END_EVENT_MAP()
};
しかし、この OnHardButton( ) は決して呼び出されることがありません。なぜなら、POL のイベントハンドリングでは、ハードボタンの押下は CForm 派生クラスまで到達しないからです。ハードボタンの押下イベントは、ユーザー定義アプリケーションクラスの基底クラスである CPalmApp のレベルで処理されてしまいます。そのため、ハードボタン押下をフックしたければアプリケーションクラスで対処する必要があります。
!!CPalmApp::EventLoop( )の実装
アプリケーションクラスで対処するといっても、POL ではイベント処理のほとんどは CPalmApp クラスの内部で行われています。ユーザー定義のメソッドでオーバーライドできる仮想関数は AppHandleEvent( ) と PreSystemEventHook( ) しかありません。これらの仮想関数を呼び出している CPalmApp::EventLoop( ) の実装は以下のようになっています(元のソースから括弧のスタイルなどを整形してあります)。
void CPalmApp::EventLoop( ) {
if( m_wCommand == sysAppLaunchCmdNormalLaunch ) {
UInt16 error;
EventType event;
do {
EvtGetEvent( &event, m_dwEventWaitDuration );
if( !PreSystemEventHook( &event ) )
if( !SysHandleEvent( &event ) )
if( !MenuHandleEvent( 0, &event, &error ) )
if( !AppHandleEvent( &event ) )
FrmDispatchEvent( &event );
} while( event.eType != appStopEvent );
}
}
ハードボタンの処理は SysHandleEvent( ) で行われてしまうため、AppHandleEvent( ) をオーバーライドしてもハードボタンの処理をフックすることはできません。そのため、PreSystemEventHook( ) をオーバーライドする必要があります。
PreSystemEventHook( ) をオーバーライドしても、実際に押下されたハードボタンに対してどのような動作をするのは CForm 派生クラス側になります。そのため、アプリケーションクラスがフックしたハードボタンイベントを CForm 派生クラスに伝達する方法が必要になります。
!!PreSystemEventHook( ) から CForm 派生クラスへの伝達
フックしたイベントを他のオブジェクト( 画面クラス )へ伝達するための仕組みについて考えます。通常の(たとえば画面上のボタンなどのタップ)イベントについては POL の正規のルートで処理するという前提で、PreSystemEventHook( ) でフックする必要のあるイベントに限定した仕組みとして、以下の EventReceiver インタフェイスを考えます。
class EventReceiver {
public:
virtual Boolean HandleEvent( EventType* pEvent ) = 0;
};
このインタフェイス(を実装するクラスのオブジェクト)をアプリケーションクラスに登録し、CPalmApp 派生クラスでオーバーライドする PreSystemEventHook( ) から呼び出すようにします。
class EventReceiver;
class CMyApp : public CPalmApp {
public:
CMyApp( );
public:
// :
// :
virtual Boolean PreSystemEventHook( EventType* pEvent );
// :
// :
// Form map
BEGIN_FORM_MAP()
FORM_MAP_ENTRY(MYFORM, CMyForm)
END_FORM_MAP()
private:
EventReceiver* m_pEventReceiver;
public:
static void RegisterEventReceiver( EventReceiver* pReceiver );
};
大雑把ですが、CMyApp::PreSystemEventHook( ) の実装は以下のような感じになるでしょう。
Boolean CMyApp::PreSystemEventHook( EventType* pEvent ) {
if( pEvent->eType != keyDownEvent || !m_pEventHandler )
return false;
switch( pEvent->data.keyDown.chr ) {
case vchrHard1:
case vchrHard2:
case vchrHard3:
case vchrHard4:
return m_pEventReceiver->HandleEvent( pEvent );
}
return false;
}
画面クラス側では、CForm( あるいは CModalForm )と同時に EventReceiver も継承します。
class CMyForm : public CForm, public EventReceiver {
// :
// :
public:
// <<EventReceiver>>
virtual Boolean HandleEvent( EventType* pEvent );
// :
// :
};
この CMyForm クラスの frmOpenEvent ハンドラで CMyApp::RegisterEventReceiver( ) を呼び出して自身を登録することで、PreSystemEventHook( ) から CMyForm オブジェクトへハードボタン押下イベントを伝達できるようになります。また、frmCloseEvent ハンドラで登録を解除するために NULL を指定して再び CMyApp::RegisterEventReceiver( ) を呼び出す必要があることに注意してください。
//!!!参考情報
//
//
!!!注意事項
このページで紹介した方法は、筆者がハードボタン押下イベントを処理するために試行錯誤した結果であり、もっと良い(あるいはより正しい)方法が別に存在するかもしれません。より良い情報をお持ちの方はコメントいただけると幸いです。
!!!コメント
::Re: - 陰郎 (2007年05月07日 08時54分02秒)
::: 思いついたので自分でコメントしてしまいますが、PreSystemEventHook( ) 内から FrmDispatchEvent( ) を呼び出してしまえばそれで済むような気が...(無論条件を満たした場合だけ)。今職場なので確認できませんが、後ほど試してみます。
::Re: - 陰郎 (2007年05月08日 00時43分53秒)
::: PreSystemEventHook( ) 内から FrmDispatchEvent( ) を呼び出す方式を試してみたところ、何の問題もなく動作しました。ということで、時間を見つけて記事本体を修正することにします。
{{comment multi}}