EternalWindows
WebBrowser コントロール / ステータスバーの実装

今回は、ステータスバーの実装について説明します。 ステータスバーは、ブラウザのこの部分に相当します。

ステータスバーには、3つのパートが用意されています。 1つ目のパートにはページのロード状況やリンク先のURLが表示され、 2つ目のパートには現在のセキュリティゾーンが表示されます。 ローカルコンピュータ上のファイルにアクセスしている場合は、 「インターネット」ではなく「コンピュータ」と表示されるでしょう。 3つ目のパートには、現在のページのズーム率が表示されます。 このパートで右クリックを行うと、コンテキストメニューからズーム率を変更できます。

ステータスバーはCStatusbarで識別され、CWebBrowserContainerによって使用されます。 CWebBrowserContainerはステータスバーを作成したくなった段階で、CStatusbar::Createを呼び出します。

void CStatusbar::Create(HWND hwndParent)
{
	m_hwndStatus = CreateWindowEx(0, STATUSCLASSNAME, TEXT(""), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndParent, (HMENU)ID_STATUS, NULL, NULL);

	m_hmenuZoom = CreatePopupMenu();
	InitializeMenuItem(m_hmenuZoom, TEXT("125%"), 125, NULL);
	InitializeMenuItem(m_hmenuZoom, TEXT("100%"), 100, NULL);
	InitializeMenuItem(m_hmenuZoom, TEXT("75%"), 75, NULL);
}

ステータスバーのウインドウを作成すると共に、ズーム用のメニューも作成しておきます。 各メニュー項目のIDは、ズーム率と一致させておくと何かと都合がよいため、 そのようにしておきます。

ステータスバーで右クリックが行われた場合は、親ウインドウにWM_NOTIFYが送られます。 これを検出したCWebBrowserContainerは、CStatusbar::OnNotifyを呼び出します。

void CStatusbar::OnNotify(WPARAM wParam, LPARAM lParam)
{
	int             i;
	int             nId;
	int             nZoom;
	int             nCount;
	POINT           pt;
	VARIANT         varIn, varOut;
	CWebBrowserHost *pWebBrowserHost = g_pWebBrowserContainer->GetActiveBrowser();
	int             a[] = {125, 100, 75};

	if (((LPNMHDR)lParam)->idFrom != ID_STATUS)
		return;

	if (((LPNMHDR)lParam)->code != NM_RCLICK || ((LPNMMOUSE)lParam)->dwItemSpec != 2)
		return;

	if (pWebBrowserHost == NULL)
		return;
	
	VariantInit(&varOut);
	pWebBrowserHost->Exec(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DODEFAULT, NULL, &varOut);

	nCount = GetMenuItemCount(m_hmenuZoom);
	for (i = 0; i < nCount; i++) {
		nId = a[i];
		SetMenuItem(m_hmenuZoom, nId, TRUE, nId == varOut.lVal);
	}

	GetCursorPos(&pt);
	nId = TrackPopupMenu(m_hmenuZoom, TPM_RETURNCMD | TPM_NONOTIFY, pt.x, pt.y, 0, m_hwndStatus, NULL);
	if (nId == 0)
		return;

	nZoom = nId;

	varIn.vt = VT_I4;
	varIn.lVal = nZoom;
	VariantInit(&varOut);
	pWebBrowserHost->Exec(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DODEFAULT, &varIn, &varOut);

	SetZoomText();
}

通知コードが右クリックに関するものかを確認し、さらに3番目のパートが対象になっているかを確認します。 また、アクティブなブラウザが存在するかも確認します。 条件に問題なければ、OLECMDID_OPTICAL_ZOOMを指定して現在のブラウザのズーム率を取得します。 そして、メニューを表示する前に、現在のズーム率と一致する項目にチェックを付けるようにします。 TrackPopupMenuの呼び出しでメニューが表示されることになりますが、 ことのきにはTPM_RETURNCMDを指定しているため、選択された項目のIDが戻り値として返ります。 このIDはズーム率の値であるため、そのままOLECMDID_OPTICAL_ZOOMで新しいズーム率に設定できます。 SetZoomTextを呼び出しているのは、パートに新しいズーム率を表示するためです。

ステータスバーの内容は、イベントの状況によって異なるといえます。 前々節では、CEventSinkというイベントを受信するオブジェクトを実装しましたが、 ここで通知されるイベントには、ステータスバーのテキストの変更を示すDISPID_STATUSTEXTCHANGEが含まれています。 これを検出したCEventSinkはCWebBrowserContainer::OnStatusTextChangeを呼び出し、 さらにこのメソッドはCStatusbar::OnStatusTextChangeを呼び出します。

void CStatusbar::OnStatusTextChange(BSTR Text)
{
	SetStatusText(PANE_NAVIGATION, Text, NULL);
}

SetStatusTextという自作メソッドは、第1引数の定数で識別されるペインに第2引数のテキストを設定します。 パートのインデックスではなく、PANE_NAVIGATIONのような定数を使用しているのは、 インデックスという位置情報をなるべく隠蔽するためです。 SetStatusTextの内部は次のようになっています。

void CStatusbar::SetStatusText(DWORD dwPane, LPWSTR lpszText, HICON hicon)
{
	int nPart = 0;

	if (dwPane == PANE_NAVIGATION)
		nPart = 0;
	else if (dwPane == PANE_ZONE)
		nPart = 1;
	else
		return;
	
	SendMessageW(m_hwndStatus, SB_SETTEXT, nPart, (LPARAM)lpszText);

	if (hicon != NULL)
		SendMessage(m_hwndStatus, SB_SETICON, nPart, (LPARAM)hicon);
}

ペインの定数をパートのインデックスに変換し、該当パートにSB_SETTEXTでテキストを設定します。 また、アイコンのハンドルが渡されている場合は、SB_SETICONでアイコンを設定します。

新しいページにアクセスした場合やタブを切り替えた場合は、 ステータスバーの内容を現在のURLのものに更新する必要があります。 このような場合には、CStatusbar::UpdateStatusTextが呼ばれます。

void CStatusbar::UpdateStatusText(BSTR URL)
{
	DWORD                    dwZone;
	IInternetSecurityManager *pInternetSecurityManager;
	IInternetZoneManager     *pInternetZoneManager;
	ZONEATTRIBUTES           zoneAttributes;
	LPWSTR                   lpsz;
	HMODULE                  hmod;
	HICON                    hicon;
	int                      cxDesired = GetSystemMetrics(SM_CXSMICON);
	int                      cyDesired = GetSystemMetrics(SM_CYSMICON);

	CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pInternetSecurityManager));
	pInternetSecurityManager->MapUrlToZone(URL, &dwZone, 0);
	pInternetSecurityManager->Release();

	CoCreateInstance(CLSID_InternetZoneManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pInternetZoneManager));
	zoneAttributes.cbSize = sizeof(ZONEATTRIBUTES);
	pInternetZoneManager->GetZoneAttributes(dwZone, &zoneAttributes);
	pInternetZoneManager->Release();
	
	lpsz = StrChrW(zoneAttributes.szIconPath, '#');
	lpsz[0] = '\0';
	
	hmod = LoadLibrary(zoneAttributes.szIconPath);
	hicon = (HICON)LoadImageW(hmod, MAKEINTRESOURCE(StrToIntW(lpsz + 1)), IMAGE_ICON, cxDesired, cyDesired, LR_SHARED);
	SetStatusText(PANE_ZONE, zoneAttributes.szDisplayName, hicon);
	FreeLibrary(hmod);

	SetZoomText();
}

セキュリティゾーンのペインを更新するために、現在のセキュリティゾーンを取得する必要があります。 InternetSecurityManager::MapUrlToZoneは、指定したURLに関連するゾーンを返すことができるため、 これを呼び出すようにします。 ゾーンに関する具体的な情報を取得するには、IInternetZoneManagerという別のインターフェースを使用します。 GetZoneAttributesを呼び出すとZONEATTRIBUTES構造体を初期化でき、 これにはゾーンの表示名とアイコンを格納したファイルパスが含まれています。 ただし、アイコンのパスについてはinetcpl.cpl#001313のようになっており、 #の左側にファイル名が、右側にはリソースIDが含まれています。 よって、これを分割して考えるために、StrChrで#の位置を示すアドレスを取得し、それをNULL文字に置き換えます。 これにより、zoneAttributes.szIconPathがファイル名を表すようになり、 lpsz + 1がリソースIDを表すことになります。 LoadLibraryでファイルをロードしたら、アイコンを取得するためにLoadImageを呼び出します。 第2引数はリソースIDを数値として指定しなければならないため、StrToIntで文字列を数値に変換しています。 ゾーンを設定する場合はSetStatusTextにPANE_ZONEを指定し、 テキストはzoneAttributes.szDisplayNameを指定します。 また、取得したアイコンのハンドルを第3引数に指定します。 ズーム率が変更している可能性もあるため、SetZoomTextを呼び出しておきます。

全てのタブが破棄されて何もページが存在しない状態では、 ステータスバーに何らかのテキストが表示されるべきではありません。 このように、ステータスバーの内容をクリアしたい際にはClearStatusTextが呼ばれます。

void CStatusbar::ClearStatusText()
{
	int i, n;
	
	n = (int)SendMessage(m_hwndStatus, SB_GETPARTS, 0, 0);

	for (i = 0; i < n; i++) {
		SendMessage(m_hwndStatus, SB_SETICON, i, 0);
		SendMessage(m_hwndStatus, SB_SETTEXT, i, 0);
	}
}

SB_GETPARTSを送信すれば、ステータスバーに存在するパートの数を取得できます。 後は、この数だけテキストとアイコンに0を指定すればよいことになります。


戻る