第60回 フォーカス変更を検出して処理する (タブブラウザーを作る)

tabbrowser274.gif
前回はフォーカス関連のバグ修正のためビューウインドウをCAxWindowからCAxHostWindowに移行した。今回はIEコントロールがフォーカスを受け取ったことを検出して、正常に処理されるようにする。

まずはCMainFrameのPreTranslateMessage内で、ビューウインドウのPreTranslateMessageが呼ばれるようにする。ATL/WTLアプリケーションウイザードでは標準で同じ様な処理が実装されているが、ウイザードのバグにより正常にビューウインドウのPreTranslateMessageが呼ばれていない。次のバージョンでは修正してもらいたいものだ。。。
	virtual BOOL PreTranslateMessage(MSG* pMsg)
	{
		//■追加
		{
			CTabBrowser100View*	pView;

			pView = GetActivePageView();
			if(pView && pView->PreTranslateMessage(pMsg))
				return	TRUE;
		}

		if(TranslatePopupMessage(pMsg->hwnd,pMsg->message,pMsg->wParam,pMsg->lParam,pMsg))
			return	TRUE;

		if(_wndAddressBar.IsChild(::GetFocus()) == FALSE)		//アドレスバーにフォーカスがあるときはPreTranslateMessageを回さない(アドレスバーでCtrl+Cなどを使えるようにするため)
		{
			if(CFrameWindowImpl<CMainFrame>::PreTranslateMessage(pMsg))
				return TRUE;
		}

		return m_view.PreTranslateMessage(pMsg);
	}

tabbrowser275.gif
そしてビューウインドウ内にPreTranslateMessageを追加する。ビューに対してWM_FORWARDMSGを投げると、CAxHostWindow内で自動的にTranslateAcceleatorが呼び出される。

また、フォーカスの変更を取得するためのウインドウCSubclassIEWndを追加する。今回はWM_SETFOCUSもしくはWM_KILLFOCUSがIEコントロールに届いたらそれをビューウインドウへ送るようにした。
	//■追加
	//「Internet Explorer_Server」をサブクラス化
	class	CSubclassIEWnd : public CWindowImpl<CSubclassIEWnd>
	{
		CTabBrowser100View*	_pView;
	public:
		CSubclassIEWnd(CTabBrowser100View* pView)
		{
			_pView = pView;
		}
		BEGIN_MSG_MAP(CSubclassIEWnd)
			if(uMsg == WM_SETFOCUS || uMsg == WM_KILLFOCUS)// || uMsg == WM_MOUSEACTIVATE)
			{
				if(_pView)
					_pView->SendMessage(uMsg,wParam,lParam);
			}

			if(uMsg == WM_DESTROY)
			{
				UnsubclassWindow();
			}
		END_MSG_MAP()
	};

	//■追加
	CSubclassIEWnd	_wndIEServer;

public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())	//CAxHostWindow::ではなくCAxWindow::のままなことに注意


	CTabBrowser100View(CTabView* pTabView)
		: _wndReflect(this)
		,_wndIEServer(this)
	{
		_pTabView = pTabView;
		_bPageError = false;
	}


	//■追加
	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if((pMsg->message < WM_KEYFIRST || pMsg->message > WM_KEYLAST) &&
		   (pMsg->message < WM_MOUSEFIRST || pMsg->message > WM_MOUSELAST))
		{
			return FALSE;
		}

		BOOL bRet = FALSE;

		//IOleInPlaceActiveObject::TranslateAccelerator(pMsg)の呼び出し
		if(pMsg->hwnd == m_hWnd || IsChild(pMsg->hwnd))
			bRet = (BOOL)SendMessage(WM_FORWARDMSG, 0, (LPARAM)pMsg);

		return bRet;
	}

tabbrowser276.gif IEコントロールが持つ「Internet Explorer_Server」ウインドウはドキュメントがそろったときに生成される。そのためOnDocumentComplete内でサブクラス化する。
	//HTMLページが読み終わったときに呼ばれる処理
	void	_stdcall OnDocumentComplete(IDispatch* pDisp, VARIANT* vURL)
	{
		if(pDisp != _pIWebBrowser2)
			return;			//フレームなどの読み込み終了は無視

		//■追加
		//IEをサブクラス化
		if(_wndIEServer.IsWindow() == FALSE && _pIWebBrowser2)
		{
			HWND	hWnd;
			HRESULT	hr = E_FAIL;
			CComPtr<IDispatch>	pIDispatch;
			CComPtr<IOleWindow>	pIOleWindow;

			_pIWebBrowser2->get_Document(&pIDispatch);
			if(pIDispatch)
				pIDispatch->QueryInterface(&pIOleWindow);
			if(pIOleWindow)
				hr = pIOleWindow->GetWindow(&hWnd);
			if(SUCCEEDED(hr))
				_wndIEServer.SubclassWindow(hWnd);
		}

		ChangeUIHandler();			//UIHandlerを変更する

		HICON		hIcon;
		CAtlString	strFile;

tabbrowser278.gif そしてビューウインドウに届いたWM_SETFOCUSなどをメッセージマップで処理する。
	BEGIN_MSG_MAP(CTabBrowser100View)
		MESSAGE_HANDLER(WM_DNP_SCRIPTERROR, OnDnpScriptError)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		MESSAGE_HANDLER(WM_DNP_SHOWSCRIPTERROR, OnShowScriptError)
		MESSAGE_HANDLER(WM_DNP_SHOWPRIVACYREPORT, OnShowPrivacyReport)
		MESSAGE_HANDLER(WM_DNP_SHOWPSECURITYREPORT, OnShowSecurityReport)
		MESSAGE_HANDLER(WM_DNP_SHOWZONECONFIGURE, OnShowZoneConfigure)
		MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)	//■追加
		MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)	//■追加
		CHAIN_MSG_MAP(CAxHostWindow)
	END_MSG_MAP()


private:

	//■追加
	LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		ATLTRACE("●OnSetFocus\n");

		SetFocusChange(true);
		bHandled = FALSE;
		return	0;
	}

	//■追加
	LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		ATLTRACE("●OnKillFocus\n");

		SetFocusChange(false);
		bHandled = FALSE;
		return	0;
	}

tabbrowser277.gif これまでアドレスバーの中でフォーカスの検出をしていた。この処理はもう不要なので削除する。クラス自体必要性がなくなってしまうのだが、とりあえずこのまま残しておく。
//アドレスバー用クラス
class	CAddressBarCtrl	: public CWindowImpl<CAddressBarCtrl,CComboBoxEx>
{
	//■内部処理削除
	class	CInnerComboBox	: public CWindowImpl<CInnerComboBox>
	{
	public:
		BEGIN_MSG_MAP(CInnerComboBox)
			//MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus)
			//MESSAGE_HANDLER(WM_MOUSEACTIVATE, OnMouseActivate)
		END_MSG_MAP()


		////マウスによるフォーカス取得時の処理
		//LRESULT OnMouseActivate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		//{
		//	GetTopLevelParent().SendMessage(WM_DNP_CHANGEFOCUS,(WPARAM)TRUE);		//メインウインドウへフォーカスを受け取ったことを通知
		//	bHandled = FALSE;
		//	return	0;
		//}

		//LRESULT OnKillFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		//{
		//	GetTopLevelParent().SendMessage(WM_DNP_CHANGEFOCUS,(WPARAM)FALSE);		//メインウインドウへフォーカスを失ったことを通知
		//	bHandled = FALSE;
		//	return	0;
		//}
	};

tabbrowser279.gif 最後に今回のバグの元凶となっていたSetFocusChange内の処理を変更する。
	//■変更
	//IEコントロールがフォーカスを受け取ったときbGetFocus=true、
	//IEコントロールがフォーカスを失ったときbGetFocus=falseとして呼び出す
	bool	SetFocusChange(bool bGetFocus)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;

		if(bGetFocus)
		{
		}
		else
		{
			CComPtr<IOleInPlaceObject> pIOleInPlaceObject;

			if(_pIWebBrowser2)
				_pIWebBrowser2->QueryInterface(&pIOleInPlaceObject);
			if(pIOleInPlaceObject)
				pIOleInPlaceObject->UIDeactivate();
		}

		return	true;
	}

tabbrowser280.gif
これでとりあえず、フォーカス関連のバグは修正された。しかし今回のバグ修正作業によりスクリプトの実行や画像の表示のON/OFF機能が使えなくなってしまっている。

次回はその部分を修正する。

プロジェクトファイルをダウンロード


カテゴリー「タブブラウザーを作る」 のエントリー