« 2009年03月 | メイン | 2010年05月 »

前の10件 1  2  3  4  5  6  7

2009年04月 記事一覧

2009年04月04日

第19回 タブを切り替えたときにアドレスバーのURL表示を更新する (タブブラウザーを作る)

tabbrowser70.gif
前回はアドレスバーにアクティブなタブで読み込まれたホームページのURLが自動で表示されるようにした。しかしタブを切り替えたときにアドレスバーのURL表示が変わらないという問題があった。今回はその点を修正する。

WTLのCTabViewではタブが切り替わったとき、親ウインドウにTBVN_PAGEACTIVATEDという通知が届く。これを処理してアドエスバーを更新する。MainFrm.h内にTBVN_PAGEACTIVATEDのハンドラと処理を実装する。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)		//■追加
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()



	//■追加
	//タブが選択されたときの処理
	LRESULT OnTabPageActivated(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

		bool		ret;
		CAtlString	strURL;
		CTabBrowser100View*	pView;

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
			return	0;

		ret = pView->GetURL(strURL);		//URL取得
		if(ret == false)
			return	0;

		//アドレスバーのURL変更
		_wndAddressBar.SetWindowText(strURL);

		return	0;
	}

tabbrowser71.gif 次にIEUtility.hに現在のURLを取得するための関数を追加する。
	//■追加
	bool	GetURL(CAtlString& strURL)
	{
		HRESULT		hr;
		CComBSTR	bstr;

		strURL = _T("");
		if(_pIWebBrowser2 == NULL)
			return	false;

		hr = _pIWebBrowser2->get_LocationURL(&bstr);
		if(FAILED(hr))
			return	false;

		strURL = bstr;

		return	true;
	}

tabbrowser72.gif
これでビルド/実行するとタブを切り替えたときにもきちんとアドレスバーにそのタブで開いているページのURLが表示されるようになった。

まだまだアドレスバーで修正しなければいけない点が残っているがとりあえず保留して、次回はタブブラウザーが起動するときに前回閉じた位置で開くように設定する。

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

第20回 ウインドウの位置やサイズを保存/復元する (タブブラウザーを作る)

tabbrowser73.gif
今回は終了時にウインドウの位置やサイズを保存し、起動時にその位置やサイズをみ込むことで前回閉じた位置に前回のままの大きさで開く処理を実装する。情報の保存はレジストリに行う。

ウインドウの位置やサイズの取得/復元は以前に「ウインドウの位置情報を保存/復元するクラス」で作成したCDnpWindowPlacementクラスを利用する。

まずこのクラスを利用するために「DnpWindowPlacement.h」ファイルをプロジェクトに追加する。Visual Studio 2008で「プロジェクト」メニューから「既存の項目を追加」を選択する。このメニュー項目がない場合はソリューションウインドウを開いてからもういちどメニューを選択する。

tabbrowser74.gif
そして現われたウインドウで「DnpWindowPlacement.h」を追加する。

※このファイルは今回のサンプルプロジェクトもしくは「ウインドウの位置情報を保存/復元するクラス」からダウンロードしてください

tabbrowser75.gif 次に情報を保存するためのレジストリ位置を簡単に変えれるように「stdafx.h」にレジストリ位置用の定義を追加する。
#define	DNP_REGKEY		_T("software\\dinop\\tabbrowser")	//■追加

tabbrowser76.gif そして「TabBrowser100.cpp」のメインウインドウ生成後にレジストリから位置情報を読み込み、復元する処理を追加する。
int Run(LPTSTR /*lpstrCmdLine*/ = NULL, int nCmdShow = SW_SHOWDEFAULT)
{
	CMessageLoop theLoop;
	_Module.AddMessageLoop(&theLoop);

	CMainFrame wndMain;

	if(wndMain.CreateEx() == NULL)
	{
		ATLTRACE(_T("メイン ウィンドウの作成に失敗しました!\n"));
		return 0;
	}

	//wndMain.ShowWindow(nCmdShow);		//■削除

	//■追加
	//ウインドウサイズ/位置をレジストリから復元
	{
		bool	ret;
		CDnpWindowPlacement	cPlacement;

		ret = cPlacement.LoadFromReg(HKEY_CURRENT_USER,DNP_REGKEY,_T("WindowPlacement"));
		if(ret)
			ret = cPlacement.Restore(wndMain);
		if(ret == false)
			wndMain.ShowWindow(nCmdShow);
	}

	int nRet = theLoop.Run();

	_Module.RemoveMessageLoop();
	return nRet;
}

tabbrowser77.gif さらに「MainFrm.h」内にCDnpWindowPlacementを利用するためのincludeを追加する。
#include "DnpWindowPlacement.h"		//■追加

tabbrowser78.gif そして、OnDestroy()が呼ばれたときにレジストリへウインドウの位置/サイズを保存するようにする。
	LRESULT OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		//■追加
		//現在のウインドウサイズ/位置をレジストリに保存
		{
			CDnpWindowPlacement	cPlacement;

			cPlacement.GetCurrentPos(m_hWnd);
			cPlacement.SaveToReg(HKEY_CURRENT_USER,DNP_REGKEY,_T("WindowPlacement"));
		}

		_cTabImageList.Destroy();

		// メッセージ フィルターおよび画面更新用のオブジェクト登録解除
		CMessageLoop* pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->RemoveMessageFilter(this);
		pLoop->RemoveIdleHandler(this);

		bHandled = FALSE;
		return 1;
	}

tabbrowser79.gif
これでビルド/実行すると前回の位置に同じ大きさでタブブラウザが開くようになった。

次回はステータスバーにIEコントロールからのメッセージを表示する機能を追加する。

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

第21回 IEコントロールからのメッセージをステータスバーに表示する (タブブラウザーを作る)

tabbrowser80.gif
Internet Explorerではステータスバーに「ページが表示されました」などのメッセージが表示される。この文字列はIEコントロールのイベントDISPID_STATUSTEXTCHANGEで受信できる。今回はこれを利用してステータスバーにメッセージを表示する。

ステータスバーメッセージ表示機能の実装はアドレスバーに開かれているページのURLを表示したのと基本的に同じ流れだ。

まず「stdafx.h」にメインウインドウへメッセージが変更されたことを知らせる独自メッセージを定義する。
#define	WM_DNP_CHANGESTATUSTEXT	(WM_APP + 4)			//■追加

tabbrowser81.gif 次にビュークラスの中にIEコントロールからのイベントを受信したときにメインウインドウへ通知する処理を実装する。またユーザーによって「タブ」が切りかえられたときに、そのページ用のステータスバーメッセージがきちんと表示されるように取得用の関数GetLastStatusText()も実装しておく。
	CAtlString	_strLastStatusText;		//■追加 最後に送られてきたステータステキストを保管

	void __stdcall OnStatusTextChange(BSTR bstrText)
	{
		//■処理を実装

		_strLastStatusText = bstrText;		//いつでも参照できるようにクラスのメンバーに保存しておく

		if(_pTabView == NULL || _pTabView->GetActivePage() != _pTabView->PageIndexFromHwnd(m_hWnd))
			return;			//このビューがアクティブでなければこれ以降処理しない

		//メインウインドウに変更を通知する
		GetTopLevelWindow().SendMessage(WM_DNP_CHANGESTATUSTEXT,(WPARAM)(LPCTSTR)_strLastStatusText);
	}

	//■追加
	//最後にIEコントロールから受け取ったステータスバー用メッセージを返す
	void	GetLastStatusText(CAtlString& strText)
	{
		strText = _strLastStatusText;
	}

tabbrowser82.gif そして「MainFrm.h」内に独自メッセージWM_DNP_CHANGESTATUSTEXTを受け取ったときや、タブが切り替わったときにステータスバーの表示を更新する処理を追加する。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)	//■追加
		NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()

	//■追加
	//ステータスバーの表示テキストの変更処理
	//アクティブなタブのIEでステータスバーのテキストが変わったときにメッセージが届く
	LRESULT OnDnpChangeStatusText(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		LPCTSTR		pszText = (LPCTSTR)wParam;

		if(pszText == NULL)
			return	0;

		if(::IsWindow(m_hWndStatusBar))
			::SetWindowText(m_hWndStatusBar,pszText);

		return	0;
	}


	//■処理を変更/追加
	//タブが選択されたときの処理
	LRESULT OnTabPageActivated(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

		bool		ret;
		CTabBrowser100View*	pView;

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
			return	0;

		//アドレスバーのURL変更
		{
			CAtlString	strURL;

			ret = pView->GetURL(strURL);		//URL取得
			if(ret)
				_wndAddressBar.SetWindowText(strURL);
		}

		//ステータスバーのテキスト変更
		{
			CAtlString	strText;

			pView->GetLastStatusText(strText);
			if(::IsWindow(m_hWndStatusBar))
				::SetWindowText(m_hWndStatusBar,strText);
		}

		return	0;
	}

tabbrowser83.gif
これでビルド/実行するとステータスバーにIEコントロールからのメッセージが表示されるようになった。

次回はダウンロード状況などがわかるようにステータスバーにプログレスバーを配置する。

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

第22回 ステータスバーにプログレスコントロールを配置する (タブブラウザーを作る)

tabbrowser84.gif
Internet Explorerはステータスバーにプログレスバーが表示され、ページの読み込み状態がひと目でわかるようになっている。

今回はInternet Explorerのように進行状況を表示するためステータスバーにプログレスバーコントロールを配置する。

CStatusBarCtrl::SetParts() を利用してペインを1つ作り、そのうちの1つにプログレスバーコントロールが重なるように配置した。表示するタイミングはWM_SIZEを利用し、ステータスバーががメインウインドウのサイズ変更されたときもきちんと更新されるようにした。 また、今回はプログレスバーを表示するにとどめ、進行状況の表示はしていない。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)
		NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		MESSAGE_HANDLER(WM_SIZE, OnSize)				//■追加
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()


	CProgressBarCtrl	_wndProgressBar;			//■追加 ステータスバーに配置するプログレスバーコントロール

	//■追加
	//bVisible=falseならプログレスバーを消す
	//nProgress < 0、nProgressMax < 0ならプログレス設定はしない
	//nProgressMax < nProgressなら強制的に非表示にする
	bool	RefreshProgressBar(int nProgress=-1,int nProgressMax=-1,bool bVisible=true)
	{
		if(::IsWindow(m_hWndStatusBar) == FALSE)
			return	false;				//ステータスバーがない

		int		nProgressWidth = 100;			//■プログレスバーの幅は100px固定とする
		CRect	rect;
		CStatusBarCtrl	wndStatusBar = m_hWndStatusBar;

		if(nProgressMax < nProgress)
			bVisible = false;

		if(bVisible == false)
			nProgressWidth = 0;

		//ステータスバーのペインの作成/更新
		{
			int		pnParts[2];						//2つペインを作成(1つはデフォルト、もう1つはプログレスバー用)

			wndStatusBar.GetClientRect(&rect);
			pnParts[0] = rect.Width() - nProgressWidth;
			pnParts[1] = rect.Width();

			wndStatusBar.SetParts(sizeof(pnParts) / sizeof(pnParts[0]),pnParts);
		}


		//プログレスバーの生成/更新
		{
			wndStatusBar.GetRect(1,&rect);			//2つめのペインをプログレスバーに利用する

			if(_wndProgressBar.IsWindow() == FALSE)
				_wndProgressBar.Create(m_hWndStatusBar,rect,0,WS_CHILD);	//プログレスバーを生成
			else
				_wndProgressBar.MoveWindow(&rect);			//位置を移動

			if(bVisible)
			{
				if(_wndProgressBar.IsWindowVisible() == FALSE)
					_wndProgressBar.ShowWindow(SW_NORMAL);		//可視に
			}
			else
			{
				if(_wndProgressBar.IsWindowVisible())
					_wndProgressBar.ShowWindow(SW_HIDE);		//不可視に
			}

			//プログレスの設定
			if(nProgressMax >= 0)
				_wndProgressBar.SetRange(0,nProgressMax);
			if(nProgress >= 0)
				_wndProgressBar.SetPos(nProgress);
		}

		return	true;
	}


	//■追加
	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		RefreshProgressBar(50,100);		//プログレスバーを50%で表示

		return	0;
	}


tabbrowser85.gif
これでビルド/実行するとステータスバーにプログレスバーコントロールが表示された。

次回はこのプログレスバーコントロールにIEコントロールから受け取った進行状況を表示する。

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

第23回 プログレスバーでページの読み込み状況を表示する (タブブラウザーを作る)

tabbrowser86.gif
前回はステータスバーにプログレスコントロールを配置した。今回はプログレスコントロールにIEコントロールから受けとった進行状況を表示する。

基本的な流れはステータスバーにIEコントロールからのメッセージを表示したときと同じだ。

「stdafx.h」にIEコントロールからの進行状況を知らせる独自メッセージを定義する。
#define	WM_DNP_CHANGEPROGRESS	(WM_APP + 5)		//■追加

tabbrowser88.gif 次にビュークラスでIEコントロールからのDISPID_PROGRESSCHANGEイベントを受信してメインウインドウへ通知する処理を実装する。
	//■2つ変数を追加
	long	_nLastProgress;			//最後に受け取ったプログレス情報
	long	_nLastProgressMax;		//最後に受け取ったプログレス最大値

	//■処理を実装
	void __stdcall OnProgressChange(long lProgress,long lProgressMax)
	{
		if(lProgress < 0 || (lProgressMax == 0 && lProgress == 0))	//MSDNには記述がないが(0,0)で終わることが多いため(0,0)でも非表示
			lProgressMax = lProgress - 1;		//lProgressMax < lProgress にしておく -> 自動的にプログレスバーが消える

		//状態を保存しておく
		_nLastProgress		= lProgress;
		_nLastProgressMax	= lProgressMax;

		if(_pTabView == NULL || _pTabView->GetActivePage() != _pTabView->PageIndexFromHwnd(m_hWnd))
			return;			//このビューがアクティブでなければこれ以降処理しない

		//メインウインドウに変更を通知する
		GetTopLevelWindow().SendMessage(WM_DNP_CHANGEPROGRESS,(WPARAM)_nLastProgress,(LPARAM)_nLastProgressMax);
	}

	//■追加
	//最後にIEコントロールから受け取ったプログレス情報を返す
	void	GetLastProgress(long& nProgress,long& nProgressMax)
	{
		nProgress		= _nLastProgress;
		nProgressMax	= _nLastProgressMax;
	}

tabbrowser87.gif long型のメンバー変数_nLastProgressと_nLastProgressMaxをコンストラクタで初期化する処理を忘れずに追加しておく。
	CTabBrowser100View(CTabView* pTabView)
	{
		_pTabView = pTabView;
		_nLastProgress		= 0;		//■追加
		_nLastProgressMax	= -1;		//■追加
	}

tabbrowser89.gif
そして「MainFrm.h」側で独自メッセージを受信したときにそれをプログレスバーに表示する処理を追加する。

また、OnSize内でタブブラウザー起動時にプログレスバーが表示されないようにIsWindow()でチェックする処理を入れた。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)
		MESSAGE_HANDLER(WM_DNP_CHANGEPROGRESS, OnDnpChangeProgress)		//■追加
		NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()

	//■追加
	LRESULT OnDnpChangeProgress(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		RefreshProgressBar((long)wParam,(long)lParam);
		return	0;
	}


	LRESULT OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled = FALSE;

		//■変更
		if(_wndProgressBar.IsWindow())
			RefreshProgressBar();		//プログレスバー表示更新

		return	0;
	}

tabbrowser90.gif 最後に、タブが切り替わったときに、新しいタブでの進行状況が表示されるように、OnTabPageActivated()の中にプログレスバーの更新処理を入れておく。
	//タブが選択されたときの処理
	LRESULT OnTabPageActivated(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

		bool		ret;
		CTabBrowser100View*	pView;

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
			return	0;

		//アドレスバーのURL変更
		{
			CAtlString	strURL;

			ret = pView->GetURL(strURL);		//URL取得
			if(ret)
				_wndAddressBar.SetWindowText(strURL);
		}

		//ステータスバーのテキスト変更
		{
			CAtlString	strText;

			pView->GetLastStatusText(strText);
			if(::IsWindow(m_hWndStatusBar))
				::SetWindowText(m_hWndStatusBar,strText);
		}

		//■追加
		//プログレス変更
		{
			long	nProgress;
			long	nProgressMax;

			pView->GetLastProgress(nProgress,nProgressMax);
			RefreshProgressBar(nProgress,nProgressMax);
		}

		return	0;
	}

tabbrowser91.gif
これでビルド/実行するとステータスバーにあるプログレスバーコントロールにページの読み込み状況が表示されるようになった。

次回はちょっと趣向を変えて「Googleツールバー」を利用する。実現方法がまだわからないので挫折したらほかの話題に変わるかもしれません。

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

第24回 「Googleツールバー」をホストし検索バーとして利用する (タブブラウザーを作る)

tabbrowser92.gif
これまでの作業でタブブラウザーとしてまだ「お気に入り」機能を中心に致命的な機能不足があるものの、普段使いにも耐えれるレベルになってきた。しかし最近のブラウザーに必ず付いている検索バーがないため、検索しづらいという使いにくさもあった。

ゼロから検索バーを実装するのが正攻法だが、せっかくなので今回は「Googleツールバー」を利用できるようにする。当然のことながらパソコンに「Googleツールバー」がインストールされている必要がある。

まず下準備としてすべてのビューに対してタブの切り替えが通知されるようにする。「MainFrm.hのOntTabPageActivatedに処理を追加する。
	//タブが選択されたときの処理
	LRESULT OnTabPageActivated(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
	{
		if(pnmh == NULL)
			return	0;

		//■追加
		//全てのビューに対してタブ変更があったことを通知
		{
			int		i;
			int		nCount;
			CTabBrowser100View*	pView;

			int kk=m_view.GetActivePage();

			nCount = m_view.GetPageCount();
			for(i = 0; i < nCount; i++)
			{
				pView = GetPageView(i);
				if(pView == NULL)
					continue;

				if(pnmh->idFrom == i)
					pView->OnTabPageChange(true);
				else
					pView->OnTabPageChange(false);
			}
		}

		bool		ret;
		CTabBrowser100View*	pView;

		pView = GetActivePageView();		//アクティブビュー取得
		if(pView == NULL)
			return	0;

		//アドレスバーのURL変更
		{
			CAtlString	strURL;

			ret = pView->GetURL(strURL);		//URL取得
			if(ret)
				_wndAddressBar.SetWindowText(strURL);
		}

		//ステータスバーのテキスト変更
		{
			CAtlString	strText;

			pView->GetLastStatusText(strText);
			if(::IsWindow(m_hWndStatusBar))
				::SetWindowText(m_hWndStatusBar,strText);
		}

		//プログレス変更
		{
			long	nProgress;
			long	nProgressMax;

			pView->GetLastProgress(nProgress,nProgressMax);
			RefreshProgressBar(nProgress,nProgressMax);
		}

		return	0;
	}

tabbrowser93.gif 次に「Googleツールバー」を表示用の位置を確保するためにツールバー領域にリバーを作る。ここではCDummyWndというダミーウインドウを用意してそれにたいしてリバーを作成した。
	//■追加
	class	CDummyWnd	: public CWindowImpl<CDummyWnd>
	{
	public:
		BEGIN_MSG_MAP(CDummyWnd)
		END_MSG_MAP()
	};

	CDummyWnd			_wndDummy;		//■追加


	LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		// コマンドバー ウィンドウの作成
		HWND hWndCmdBar = m_CmdBar.Create(m_hWnd, rcDefault, NULL, ATL_SIMPLE_CMDBAR_PANE_STYLE);
		// メニューのアタッチ
		m_CmdBar.AttachMenu(GetMenu());
		// コマンドバー画像の読み込み
		m_CmdBar.LoadImages(IDR_MAINFRAME);
		// 以前のメニューの削除
		SetMenu(NULL);

		HWND hWndToolBar = CreateSimpleToolBarCtrl(m_hWnd, IDR_MAINFRAME, FALSE, ATL_SIMPLE_TOOLBAR_PANE_STYLE);

		CreateSimpleReBar(ATL_SIMPLE_REBAR_NOBORDER_STYLE);
		AddSimpleReBarBand(hWndCmdBar);
		AddSimpleReBarBand(hWndToolBar, NULL, TRUE);

		_wndAddressBar.Create(m_hWnd,CRect(0,0,200,200),0,WS_CHILD | WS_VISIBLE | CBS_DROPDOWN);
		AddSimpleReBarBand(_wndAddressBar);

		//■2行追加
		//ダミーウインドウを生成し、リバーに割り当てる。これはIEツールバーに利用
		//ここで生成してしまうと何もないリバーができてしまうが気にしない
		_wndDummy.Create(m_hWnd,CRect(0,0,500,24),0,WS_CHILD);
		AddSimpleReBarBand(_wndDummy,0,TRUE);						//1行表示

		CreateSimpleStatusBar();

		m_hWndClient = m_view.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE);

		UIAddToolBar(hWndToolBar);
		UISetCheck(ID_VIEW_TOOLBAR, 1);
		UISetCheck(ID_VIEW_STATUS_BAR, 1);

		// メッセージ フィルターおよび画面更新用のオブジェクト登録
		CMessageLoop* pLoop = _Module.GetMessageLoop();
		ATLASSERT(pLoop != NULL);
		pLoop->AddMessageFilter(this);
		pLoop->AddIdleHandler(this);

		CMenuHandle menuMain = m_CmdBar.GetMenu();
		m_view.SetWindowMenu(menuMain.GetSubMenu(WINDOW_MENU_POSITION));

		_cTabImageList.Create(16,16,ILC_COLOR | ILC_MASK,1,10);
		m_view.SetImageList(_cTabImageList);

		return 0;
	}


tabbrowser94.gif
そしてタブが作成されるときに、「Googleツールバー」を配置するためのダミーウインドウがビューに渡すようにする。

本来であればこんな実装はするべきではないのだが、、、ソースコードが汚くなるが今回は(も)気にせずにどんどん変更していく。
	//タブの新規作成
	//nPosはタブを追加する場所。nPos<0(もしくはnPos=m_view.GetPageCount())で一番後ろ、
	//そのほかの数値はCTabView::InsertPageにそのまま渡す
	//
	//戻ったポインタは自動削除されるため、deleteの必要なし
	//
	CTabBrowser100View*	CreateNewTab(LPCTSTR pszURL,LPCTSTR pszTitle,int nPos=-1,int nImage=-1)
	{
		bool	ret;
		CTabBrowser100View*	pView;

		pView = new CTabBrowser100View(&m_view,_wndDummy);	//■変更 タブビューのポインタとIEツールバー用のダミーウインドウを渡しておく
		if(pView == NULL)
			return	NULL;

		//ビューウインドウ生成
		if(pszURL == NULL || *pszURL == NULL)
			pView->Create(m_view, rcDefault, _T("about:blank"), WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		else
			pView->Create(m_view, rcDefault, pszURL, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_HSCROLL | WS_VSCROLL, 0);
		if(pView->IsWindow() == FALSE)
		{
			delete	pView;
			return	NULL;		//ビューウインドウ生成失敗
		}

		//タブにビューを割り当てる
		if(nPos < 0)
			nPos = m_view.GetPageCount();
		if(pszTitle)
			ret = m_view.InsertPage(nPos,pView->m_hWnd,pszTitle,nImage,pView);
		else
			ret = m_view.InsertPage(nPos,pView->m_hWnd,_T(""),nImage,pView);

		if(ret == false)
		{
			delete	pView;
			return	NULL;		//タブへの追加失敗
		}

		return	pView;
	}

tabbrowser95.gif 「Googleツールバー」のようなInternet Explorer用ツールバーはIDeskBandとして実装されている。その内部では親ウインドウを取得するためにIOleWindow、IWebBrowser2を取得するためにIServiceProvider、フォーカス変更を知らせるためにIInputObjectSiteなどが利用されている。 それらを用意してIDeskBandをホストするためのインターフェースを作成する。
//■追加
//IEツールバーをホストするためのインターフェース
MIDL_INTERFACE("28997A04-86A4-436d-BF58-133F7BD82E8A")
IIEToolbar : public IUnknown
{
public:
	STDMETHOD (put_hwnd)(HWND hWnd) = 0;
	STDMETHOD (put_IWebBrowser2)(IDispatch* pIDispatch) = 0;
};



//■追加
//IEツールバーをホストするためのクラス
class ATL_NO_VTABLE CIEToolbar : 
	public CComObjectRootEx<CComSingleThreadModel>,
	public IOleCommandTarget,
	public IOleWindow,
	public IServiceProvider,
	public IInputObjectSite,
	public IIEToolbar
{
public:
	CIEToolbar()
	{
	}

	BEGIN_COM_MAP(CIEToolbar)
		COM_INTERFACE_ENTRY(IIEToolbar)
		COM_INTERFACE_ENTRY(IOleCommandTarget)
		COM_INTERFACE_ENTRY(IOleWindow)
		COM_INTERFACE_ENTRY(IServiceProvider)
		COM_INTERFACE_ENTRY(IInputObjectSite)
	END_COM_MAP()

	DECLARE_PROTECT_FINAL_CONSTRUCT()


	HWND	m_hWnd;			//ツールバーの親となるウインドウハンドルを保持 IOleWindow::GetWindow() でツールバーに渡す

	CComPtr<IDispatch>		_pIDispatch;		//ツールバーに渡すIWebBrowser2のIDispatch


	//IIEToolbar
	STDMETHOD (put_hwnd)(HWND hWnd)
	{
		m_hWnd = hWnd;
		return	S_OK;
	}
	STDMETHOD (put_IWebBrowser2)(IDispatch* pIDispatch)
	{
		_pIDispatch = NULL;

		if(pIDispatch == NULL)
			return	E_POINTER;

		return	pIDispatch->QueryInterface(IID_IDispatch,(void**)&_pIDispatch);
	}



	//IOleCommandTarget
	STDMETHOD(Exec)(const GUID *pguidCmdGroup,DWORD nCmdID,DWORD nCmdExecOpt,VARIANTARG *pvaIn,VARIANTARG *pvaOut)
	{
		return	S_OK;
	}
	STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup,ULONG cCmds,OLECMD *prgCmds,OLECMDTEXT *pCmdText)
	{
		return	S_OK;
	}


	//IOleWindow
	STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode)
	{
		return	E_NOTIMPL;
	}
	STDMETHOD(GetWindow)(HWND* phwnd)
	{
		if(phwnd == NULL)
			return	E_FAIL;

		*phwnd = m_hWnd;
		return	S_OK;
	}


	//IServiceProvider
	STDMETHOD(QueryService)(REFGUID guidService,REFIID riid,void **ppv)
	{
		if(_pIDispatch)
		{
			*ppv = _pIDispatch;
			return	S_OK;
		}

		{
			//IEを起動して渡す

			HRESULT	hr;
			CComPtr<IWebBrowserApp>	pIWebBrowserApp;

			pIWebBrowserApp = NULL;
			hr = ::CoCreateInstance(CLSID_InternetExplorer,NULL,CLSCTX_SERVER,IID_IWebBrowserApp,(void**)&pIWebBrowserApp);
			if(pIWebBrowserApp)
				pIWebBrowserApp->put_Visible(VARIANT_TRUE);
			if(pIWebBrowserApp)
				pIWebBrowserApp->Navigate(L"about:blank",NULL,NULL,NULL,NULL);
			::Sleep(1000);		//■■1秒固定で待つ
			if(pIWebBrowserApp)
			{
				IWebBrowser2*	pIWebBrowser2;

				hr = pIWebBrowserApp->QueryInterface(IID_IWebBrowser2,(void**)&pIWebBrowser2);

				*ppv = pIWebBrowser2;
				return	S_OK;
			}
		}

		return	E_FAIL;
	}

	//IInputObjectSite
	STDMETHOD(OnFocusChangeIS)(IUnknown *punkObj,BOOL fSetFocus)
	{
		if(fSetFocus)
		{
			HRESULT	hr = E_FAIL;
			CComPtr<IOleInPlaceObject> pIOleInPlaceObject;

			if(_pIDispatch)
				_pIDispatch->QueryInterface(&pIOleInPlaceObject);
			if(pIOleInPlaceObject)
				hr = pIOleInPlaceObject->UIDeactivate();		//IEのUIを無効化
		}

		return	S_OK;
	}
};

tabbrowser96.gif ビュークラス内のコンストラクタ周りでリバー用のHWNDを保存するように修正する。
class CTabBrowser100View : public CWindowImpl<CTabBrowser100View, CAxWindow>
	,public IDispEventImpl<SINKID_EVENTS, CTabBrowser100View, &DIID_DWebBrowserEvents2>
	,public CIEUtility
{
	CTabView*	_pTabView;		//タブビューを保持
	HWND		_hWndRebarIE;	//■追加 IEツールバー用のリバー
public:
	DECLARE_WND_SUPERCLASS(NULL, CAxWindow::GetWndClassName())


	CTabBrowser100View(CTabView* pTabView,HWND hWndRebarIE)		//■変更
	{
		_pTabView = pTabView;
		_nLastProgress		= 0;
		_nLastProgressMax	= -1;
		_hWndRebarIE = hWndRebarIE;		//■追加
	}

tabbrowser97.gif
タブが切り替えられたときにIDeskBandの表示/非表示を切り替える動作を実装する。

ShowWindowで表示/非表示した方が効率がいいのだが、ここではIDeskBand::ShowDWを利用した。


Internet Explorer用のツールバーは1つのツールバーに対して1つのIEコントロールを割り当てる仕組みで作られている。これはIDeskBandがタブブラウザが一般的でなかった頃に設計されたインターフェースであり、当時は1つのInternet Explorerの中で複数のページを切り替えて表示することが想定されていなかったためだ。

スタンドアロン型のIE6からタブブラウザ型のIE7にバージョンアップするときに、このIDeskBandも併せてタブブラウザーに即した形に大きく変わるのではないか?と期待半分、大きく変わったらまたIEツールバーを作りなおさなければいけないという悪寒半分で昔はIE7の登場を待っていた。

しかし蓋を開けてみればIE6からIE7でIDeskBandはまったく同じままに残った。最初は不思議だったのだが、SPY++でのぞいてみるとすぐにその理由がわかった。Internet Explorer 7は1つのタブに対して1つのIEツールバーを生成する。。。そしてタブを切り替えたときにそれらを表示/非表示して切り換えていた。平たく言えば、IE7は内部でタブの数だけIE6を開くような造りになっていた。

今回実装しているタブブラウザー内「Googleツールバー」の表示でも同じ方法を使っている。ビュークラス内でIDeskBandを生成し、タブが切り替わったときにアクティブになったタブのIDeskBandを表示、それ以外を非表示にしている。
	CComPtr<IDeskBand>	_pIDeskBand;	//■追加


	//■追加
	//タブの選択変更があったときメインウインドウから呼ばれる
	bool	OnTabPageChange(bool bActivated)
	{
		if(_pIDeskBand == NULL)
			return	false;

		if(bActivated)
			_pIDeskBand->ShowDW(TRUE);		//IEツールバーを表示
		else
			_pIDeskBand->ShowDW(FALSE);		//IEツールバーを消す

		return	true;
	}

tabbrowser98.gif
最後にCreate()の中で「Googleツールバー」のIDeskBandを生成する。

「Googleツールバー」のGUIDは数年前にメモしていたものを流用したが、2009年4月4日現在の最新版のものでも同じGUIDが使われているようだ。 このGUIDを使ってCOMインターフェースをCoCreateInstanceし、ホストするためのIIEToolbarインターフェースに割り当てている。
		//■追加
		//Googleツールバーを生成する
		{
			HRESULT	hr;
			GUID	rclsid;
			WCHAR	pszGUID[] = {L"{2318C2B1-4965-11d4-9B18-009027A5CD4F}"};		//GoogleツールバーのGUID

			::CLSIDFromString(pszGUID,&rclsid);

			_pIDeskBand = NULL;
			hr = ::CoCreateInstance(rclsid,NULL,CLSCTX_INPROC_SERVER,IID_IDeskBand,(void**)&_pIDeskBand);
			if(_pIDeskBand)
			{
				CComPtr<IObjectWithSite>	pIObjectWithSite;

				_pIDeskBand->QueryInterface(IID_IObjectWithSite,(void**)&pIObjectWithSite);

				DESKBANDINFO	sDeskBandInfo;

				//別に必要ないが情報をたくさん取得
				::ZeroMemory(&sDeskBandInfo,sizeof(DESKBANDINFO));
				sDeskBandInfo.dwMask = DBIM_MINSIZE | DBIM_MAXSIZE | DBIM_INTEGRAL | DBIM_ACTUAL | DBIM_TITLE | DBIM_MODEFLAGS | DBIM_BKCOLOR;
				hr = _pIDeskBand->GetBandInfo(0,DBIF_VIEWMODE_NORMAL,&sDeskBandInfo);

				//■■本当はきちんと調整しないとだめ!!
				CRect	rect(0,0,2000,sDeskBandInfo.ptMinSize.y);		//横サイズは十分大きいサイズを確保(//■■2000px固定)

				//デスクバンドをホスト用クラスに割り当てる
				{
					CComPtr<IIEToolbar>	pIIEToolbar;

					pIIEToolbar = new CComObject<CIEToolbar>;

					pIIEToolbar->put_hwnd(_hWndRebarIE);
					if(_pIWebBrowser2)
						pIIEToolbar->put_IWebBrowser2(_pIWebBrowser2);
					if(pIObjectWithSite)
						hr = pIObjectWithSite->SetSite(pIIEToolbar);

					if(SUCCEEDED(hr))
						hr = _pIDeskBand->ShowDW(TRUE);
				}

				//位置調整
				{
					HWND	hWnd;

					_pIDeskBand->GetWindow(&hWnd);
					if(::IsWindow(hWnd))
						::MoveWindow(hWnd,0,0,rect.Width(),rect.Height(),TRUE);
				}
			}
		}

		return	hWnd;
	}

tabbrowser99.gif
これでビルド/実行してタブを開くと「Googleツールバー」が表示され、検索などができるようになった。もちろんページランクの表示機能なども動作した。

しかしホスト方法、特に親ウインドウを共通にしているなどだいぶ実装を省略しているため、「Googleツールバー」ではドロップダウンリストからアクセスできる検索履歴を利用するときにうまく動かないことがあるようだ。

次回は「印刷」機能を実装する。

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

第25回 印刷関連機能が使えるようにする (タブブラウザーを作る)

tabbrowser100.gif
前回はツールバーとして「Googleツールバー」を利用するというかなりヘビーな実装をした。今回は軽く印刷関連機能の実装を行う。

IEコントロールはIWebBrowser2::ExecWB (IOleCommandTarget::Exec)により各種操作ができるように作られている。ここにOLECMDID_PRINTなどを与えることで印刷機能を利用する。

実際にIWebBrowser2を操作する処理をCIEUtilityに追加する。

ちなみに私はIOleCommandTarget関連は後から機能を拡張できる利点がある反面、外側から何が実装されているのかが分かりにくくCOMインターフェースの嫌な部分だと思う。なんとかならないのかな。。。
	//■追加
	//bPrintOrPreview=trueなら印刷、falseなら印刷プレビュー
	bool	PrintOrPreview(bool bPrintOrPreview)
	{
		HRESULT	hr;

		if(_pIWebBrowser2 == NULL)
			return	false;

		if(bPrintOrPreview)
			hr = _pIWebBrowser2->ExecWB(OLECMDID_PRINT,OLECMDEXECOPT_PROMPTUSER,NULL,NULL);
		else
			hr = _pIWebBrowser2->ExecWB(OLECMDID_PRINTPREVIEW,OLECMDEXECOPT_PROMPTUSER,NULL,NULL);

		return	SUCCEEDED(hr) ? true : false;
	}

	//■追加
	bool	PrintSetup(void)
	{
		HRESULT	hr = E_FAIL;

		if(_pIWebBrowser2)
			hr = _pIWebBrowser2->ExecWB(OLECMDID_PAGESETUP,OLECMDEXECOPT_PROMPTUSER,NULL,NULL);

		return	SUCCEEDED(hr) ? true : false;
	}

tabbrowser101.gif そして「MainFrm.h」側でメニュー操作により飛んで来た印刷関連メッセージを処理するメッセージハンドラを追加する。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)
		MESSAGE_HANDLER(WM_DNP_CHANGEPROGRESS, OnDnpChangeProgress)
		NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_ID_HANDLER(ID_FILE_PRINT, OnFileCommand)					//■追加
		COMMAND_ID_HANDLER(ID_FILE_PRINT_PREVIEW, OnFileCommand)			//■追加
		COMMAND_ID_HANDLER(ID_FILE_PRINT_SETUP, OnFileCommand)				//■追加
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()

	//■追加
	LRESULT OnFileCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
	{
		CTabBrowser100View*	pView;

		pView = GetActivePageView();
		if(pView == NULL)
		{
			bHandled = FALSE;
			return	0;
		}

		bHandled = TRUE;
		switch(wID)
		{
		case	ID_FILE_PRINT:				//印刷
			pView->PrintOrPreview(true);
			break;

		case	ID_FILE_PRINT_PREVIEW:		//印刷プレビュー
			pView->PrintOrPreview(false);
			break;

		case	ID_FILE_PRINT_SETUP:		//印刷セットアップ
			pView->PrintSetup();
			break;

		default:
			bHandled = FALSE;
			break;
		}

		return	0;
	}

tabbrowser102.gif
これで「印刷」「印刷プレビュー」「プリンタの設定」の各項目が利用できるようになった。

次回は「切り取り」「コピー」「貼り付け」というクリップボード関連機能を実装する。

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

第26回 「編集」メニューから「コピー」などできるようにする (タブブラウザーを作る)

tabbrowser103.gif
前回は「印刷」「印刷プレビュー」などの機能が使えるようにした。

今回は前回とまったく同じ方法で「コピー」や「切り取り」などのクリップボード関連操作をメニューからできるようにする。

CIEUtilityにCopy()などを実装する。今回は利用しないがSelectAll()も用意した。
	//■追加
	bool	Copy(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_COPY,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

	//■追加
	bool	Cut(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_CUT,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

	//■追加
	bool	Paste(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_PASTE,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

	//■追加
	bool	SelectAll(void)
	{
		if(_pIWebBrowser2 == NULL)
			return	false;
		return	SUCCEEDED(_pIWebBrowser2->ExecWB(OLECMDID_SELECTALL,OLECMDEXECOPT_PROMPTUSER,NULL,NULL)) ? true : false;
	}

tabbrowser104.gif そして「MainFrm.h」の中でメニューが選択されたときに上で作成した関数が呼ばれるようにする。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CREATENEWTAB, OnDnpCreateNewTab)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		MESSAGE_HANDLER(WM_DNP_CHANGEADDRESS, OnDnpChangeAddress)
		MESSAGE_HANDLER(WM_DNP_CHANGESTATUSTEXT, OnDnpChangeStatusText)
		MESSAGE_HANDLER(WM_DNP_CHANGEPROGRESS, OnDnpChangeProgress)
		NOTIFY_CODE_HANDLER(CBEN_ENDEDIT, OnAddressbarEnter)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit)
		COMMAND_ID_HANDLER(ID_FILE_NEW, OnFileNew)
		COMMAND_ID_HANDLER(ID_VIEW_TOOLBAR, OnViewToolBar)
		COMMAND_ID_HANDLER(ID_VIEW_STATUS_BAR, OnViewStatusBar)
		COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE, OnWindowClose)
		COMMAND_ID_HANDLER(ID_WINDOW_CLOSE_ALL, OnWindowCloseAll)
		COMMAND_ID_HANDLER(ID_FILE_PRINT, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PRINT_PREVIEW, OnFileCommand)
		COMMAND_ID_HANDLER(ID_FILE_PRINT_SETUP, OnFileCommand)
		COMMAND_ID_HANDLER(ID_EDIT_COPY, OnFileCommand)				//■追加
		COMMAND_ID_HANDLER(ID_EDIT_CUT, OnFileCommand)				//■追加
		COMMAND_ID_HANDLER(ID_EDIT_PASTE, OnFileCommand)			//■追加
		COMMAND_ID_HANDLER(ID_EDIT_SELECT_ALL, OnFileCommand)		//■追加
		COMMAND_RANGE_HANDLER(ID_WINDOW_TABFIRST, ID_WINDOW_TABLAST, OnWindowActivate)
		COMMAND_ID_HANDLER(ID_IE_BACK, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_NEXT, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_STOP, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_REFRESH, OnIECommand)
		COMMAND_ID_HANDLER(ID_IE_ADDFAVORITE, OnIEAddFavorite)
		COMMAND_ID_HANDLER(ID_IE_ORGANIZE_FAVORITE, OnIEOrganizeFavorite)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		NOTIFY_CODE_HANDLER(TBVN_PAGEACTIVATED, OnTabPageActivated)
		MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
		COMMAND_RANGE_HANDLER(ID_FAVORITE_FIRST,ID_FAVORITE_LAST,OnFavorite)
		CHAIN_MSG_MAP_MEMBER(_cFovMenu)
		CHAIN_MSG_MAP(CUpdateUI<CMainFrame>)
		CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>)
	END_MSG_MAP()

	LRESULT OnFileCommand(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& bHandled)
	{
		CTabBrowser100View*	pView;

		pView = GetActivePageView();
		if(pView == NULL)
		{
			bHandled = FALSE;
			return	0;
		}

		bHandled = TRUE;
		switch(wID)
		{
		case	ID_EDIT_COPY:				//■追加 コピー
			pView->Copy();
			break;

		case	ID_EDIT_CUT:				//■追加 切り取り
			pView->Cut();
			break;

		case	ID_EDIT_PASTE:				//■追加 貼り付け
			pView->Paste();
			break;

		case	ID_EDIT_SELECT_ALL:			//■追加 全てを選択
			pView->SelectAll();
			break;

		case	ID_FILE_PRINT:				//印刷
			pView->PrintOrPreview(true);
			break;

		case	ID_FILE_PRINT_PREVIEW:		//印刷プレビュー
			pView->PrintOrPreview(false);
			break;

		case	ID_FILE_PRINT_SETUP:		//印刷セットアップ
			pView->PrintSetup();
			break;

		default:
			bHandled = FALSE;
			break;
		}

		return	0;
	}

tabbrowser105.gif
これでビルド/実行すると「編集」メニューから「コピー」などの操作ができるようになった。

本来であればIEコントロール上で選択されている部分がないばあいは「コピー」メニューを無効にするべきだ。しかし今回の実装では常にメニューが有効になっている。

次回はIEコントロールの状態に合わせてメニューの有効/無効が自動で変わるようにする。

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

第27回 「コピー」「切り取り」などをIEコントロールの状態に合わせて有効にする (タブブラウザーを作る)

tabbrowser106.gif
前回は「編集」メニューの「コピー」などを使えるようにした。しかしメニュー項目が常に有効なままだった。

今回はIEコントロール内の状況に合わせて、メニュー項目の有効と無効が切り替わるようにする。

IWebBrowser2からIHTMLDocumentを取り出し、選択されているHTMLエレメントがあるときは「コピー」メニューを有効に、、、という方法も考えられるが非常に煩雑だ。今回はIOleCommandtarget::QueryStatusを利用して状態を取得する。

状態を取得するための関数をCIEUtilityに追加する。
	//■追加
	//「コピー」「切り取り」「貼り付け」「全てを選択」メニューが使える状態かどうかの取得
	bool	IsEnable_EditCommand(bool& bCopy,bool& bCut,bool& bPaste,bool& bSelectAll)
	{
		HRESULT	hr;
		OLECMD	pOleCmd[] = 
			{
				{ OLECMDID_COPY, 0 },
				{ OLECMDID_CUT, 0 },
				{ OLECMDID_PASTE, 0 },
				{ OLECMDID_SELECTALL, 0 }
			};

		CComPtr<IOleCommandTarget>	pIOleCommandTarget;

		bCopy		= false;
		bCut		= false;
		bPaste		= false;
		bSelectAll	= false;
		if(_pIWebBrowser2)
			_pIWebBrowser2->QueryInterface(&pIOleCommandTarget);
		if(pIOleCommandTarget == NULL)
			return	true;				//trueで返す

		hr = pIOleCommandTarget->QueryStatus(NULL,sizeof(pOleCmd)/sizeof(OLECMD),pOleCmd,NULL);
		if(FAILED(hr))
			return	true;				//trueで返す

		bCopy		= (pOleCmd[0].cmdf & OLECMDF_ENABLED) ? true : false;
		bCut		= (pOleCmd[1].cmdf & OLECMDF_ENABLED) ? true : false;
		bPaste		= (pOleCmd[2].cmdf & OLECMDF_ENABLED) ? true : false;
		bSelectAll	= (pOleCmd[3].cmdf & OLECMDF_ENABLED) ? true : false;

		return	true;
	}

tabbrowser107.gif そして「MainFrm.h」のBEGIN_UPDATE_UI_MAPの中にメニュー項目用のIDを追加し、ちょっと行儀が悪いがOnIdle処理で状態を更新する。
	//■内部処理変更
	virtual BOOL OnIdle()
	{
		CTabBrowser100View*	pView;

		pView = GetActivePageView();

		//「戻る」「進む」ボタンの状態設定
		{
			bool	bBack = false;
			bool	bNext = false;

			if(pView)
				pView->IsButtonEnable(bBack,bNext);

			UIEnable(ID_IE_BACK,bBack ? TRUE : FALSE);
			UIEnable(ID_IE_NEXT,bNext ? TRUE : FALSE);
		}

		//「編集」メニュー項目更新
		{
			bool	bCopy		= false;
			bool	bCut		= false;
			bool	bPaste		= false;
			bool	bSelectAll	= false;

			if(pView)
				pView->IsEnable_EditCommand(bCopy,bCut,bPaste,bSelectAll);

			UIEnable(ID_EDIT_COPY		,bCopy ? TRUE : FALSE);
			UIEnable(ID_EDIT_CUT		,bCut ? TRUE : FALSE);
			UIEnable(ID_EDIT_PASTE		,bPaste ? TRUE : FALSE);
			UIEnable(ID_EDIT_SELECT_ALL	,bSelectAll ? TRUE : FALSE);
		}

		UIUpdateToolBar();
		return FALSE;
	}

	BEGIN_UPDATE_UI_MAP(CMainFrame)
		UPDATE_ELEMENT(ID_VIEW_TOOLBAR, UPDUI_MENUPOPUP)
		UPDATE_ELEMENT(ID_VIEW_STATUS_BAR, UPDUI_MENUPOPUP)
		UPDATE_ELEMENT(ID_IE_BACK, UPDUI_TOOLBAR)
		UPDATE_ELEMENT(ID_IE_NEXT, UPDUI_TOOLBAR)
		UPDATE_ELEMENT(ID_EDIT_COPY, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)			//■追加
		UPDATE_ELEMENT(ID_EDIT_CUT, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)			//■追加
		UPDATE_ELEMENT(ID_EDIT_PASTE, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)			//■追加
		UPDATE_ELEMENT(ID_EDIT_SELECT_ALL, UPDUI_TOOLBAR | UPDUI_MENUPOPUP)		//■追加
	END_UPDATE_UI_MAP()

tabbrowser108.gif
これでビルド/実行すると「編集」メニューにある「コピー」「切り取り」「貼り付け」の各項目がIEコントロールの状態に合わせて有効になるようになった。

次回は開いているページに含まれるJavascriptなどにエラーがあってもエラーダイアログが開かないようにする。

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

2009年04月05日

第28回 スクリプトエラーダイアログが表示されないようにする (タブブラウザーを作る)

tabbrowser109.gif
今回は前に紹介した「IEコントロールのスクリプトエラー表示を消す」の方法を用いて、javascriptなどにエラーがあってもエラーダイアログが表示されないようにする。

まず、前に作成したクラスをタブブラウザーのプロジェクトに取り込む。「プロジェクト」メニューから「既存項目の追加」を選択する。

tabbrowser110.gif
そして「DnpDocHostUIHandlerImpl.h」をプロジェクトに追加する。

tabbrowser112.gif 前はWTLのアプリケーションウイザードが生成したビュークラスにIDocHostUIHandlerなどを追加することでスクリプトエラーを消した。しかし今回は将来的な拡張のことも考慮して、独立したクラスCHideScriptError を作りそこでエラーダイアログを非表示にすることにした。
#include "DnpDocHostUIHandlerImpl.h"		//■追加

//■追加
//スクリプトエラーを消すためのUIHandler
class CHideScriptError :
	public CComObjectRootEx<CComSingleThreadModel>
	,public IDnpDocHostUIHandlerImpl
	,public	IDispatchImpl<IDispatch>
	,public IOleCommandTarget
{
public:

	BEGIN_COM_MAP(CHideScriptError)
		COM_INTERFACE_ENTRY(IDispatch)
		COM_INTERFACE_ENTRY(IDocHostUIHandler)
		COM_INTERFACE_ENTRY(IOleCommandTarget)
	END_COM_MAP()


    STDMETHODIMP QueryStatus( const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText ) 
	{
		return E_NOTIMPL; 
    }

    STDMETHODIMP Exec( const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
    {
		//コマンドがスクリプトエラーを示すものなら、エラー表示を無効にする
		if(nCmdID == OLECMDID_SHOWSCRIPTERROR)
		{
			(*pvaOut).vt		= VT_BOOL;
			(*pvaOut).boolVal	= VARIANT_TRUE;
			return S_OK;
		}

		return E_NOTIMPL;
    }
};

tabbrowser113.gif そしてビュークラス内でIEコントロールのUIHandlerとしてCHideScriptError を渡す。
	CComPtr<IDocHostUIHandler>		_pHideScriptError;		//■追加


	//■追加
	//UIHandlerの変更。本当はもともと入っていたUIhandlerを取得/保持してそれも併せて使うようにすべき
	bool	ChangeUIHandler(void)
	{
		HRESULT	hr = E_FAIL;

		CComPtr<IDispatch>	pIDispatch;
		CComPtr<ICustomDoc>	pICustomDoc;

		if(_pHideScriptError == NULL)
			_pHideScriptError = new CComObject<CHideScriptError>;

		if(_pIWebBrowser2)
			hr = _pIWebBrowser2->get_Document(&pIDispatch);
		if(pIDispatch)
			hr = pIDispatch->QueryInterface(IID_ICustomDoc,(void **)&pICustomDoc);
		if(pICustomDoc)
			hr = pICustomDoc->SetUIHandler(_pHideScriptError);

		return	SUCCEEDED(hr) ? true : false;
	}

	//HTMLページが読み終わったときに呼ばれる処理
	void	_stdcall OnDocumentComplete(IDispatch* pDisp, VARIANT* vURL)
	{
		if(pDisp != _pIWebBrowser2)
			return;			//フレームなどの読み込み終了は無視

		ChangeUIHandler();			//■追加 UIHandlerを変更する

		HICON		hIcon;
		CAtlString	strFile;

		//テンポラリファイルパス取得
		{
			DWORD	dwRet;
			TCHAR	pszPath[MAX_PATH + 1024];
			TCHAR	pszFile[MAX_PATH + 1024];

			dwRet = ::GetTempPath(MAX_PATH + 1024,pszPath);				//テンポラリフォルダ取得
			if(dwRet)
				dwRet = ::GetTempFileName(pszPath,NULL,0,pszFile);		//テンポラリファイル名自動生成
			if(dwRet)
				strFile = pszFile;
			if(strFile == _T(""))
				return;
		}

		//インターネットショートカットファイル作成
		{
			bool	ret;

			strFile += _T(".url");
			ret = CreateInternetShortcut((CAtlString)vURL->bstrVal,strFile);
			if(ret == false)
				return;

			//インターネットショートカットにアイコンが適用されるのを待つ(//■■これだとダメ?要調査)
			::SHChangeNotify(SHCNE_CREATE,SHCNF_PATH | SHCNF_FLUSH,(LPCTSTR)strFile,NULL);
		}

		//ファイルからHICONを取得
		{
			SHFILEINFO	sShFileInfo;
			HIMAGELIST	hImageList;

			//パスからアイコン取得
			hImageList = (HIMAGELIST)::SHGetFileInfo(strFile,0,&sShFileInfo,sizeof(SHFILEINFO),SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
			hIcon = ::ImageList_GetIcon(hImageList,sShFileInfo.iIcon,ILD_NORMAL);
			::ImageList_Destroy(hImageList);
		}
		::remove((CAtlStringA)strFile);		//ファイル削除
		if(hIcon == NULL)
			return;

		//タブにHICONを割り当てる
		{
			int			nPage;
			int			nImage;
			CImageList	cImageList;

			nPage = _pTabView->PageIndexFromHwnd(m_hWnd);
			cImageList = _pTabView->GetImageList();			//タブのイメージリスト取得

			nImage = _pTabView->GetPageImage(nPage);		//現在のイメージ番号取得
			if(nImage < 0)
			{
				//タブに画像がなければ新しく追加して使う
				nImage = cImageList.AddIcon(hIcon);
			}
			else
			{
				//タブに画像があればそれと置き換える
				cImageList.ReplaceIcon(nImage,hIcon);
			}
			_pTabView->SetPageImage(nPage,nImage);		//nImageが変わらなくても再描画のためにセットしなおす
		}
		::DestroyIcon(hIcon);
	}

tabbrowser114.gif 今回作成しているタブブラウザーでは「Googleツールバー」の機能を実装している。このツールバーが独自にUIHandlerを横取りしているようなので、タブが切り替わったときにUIhandlerを上書きしておく。
	//タブの選択変更があったときメインウインドウから呼ばれる
	bool	OnTabPageChange(bool bActivated)
	{
		if(_pIDeskBand == NULL)
			return	false;

		if(bActivated)
			_pIDeskBand->ShowDW(TRUE);		//IEツールバーを表示
		else
			_pIDeskBand->ShowDW(FALSE);		//IEツールバーを消す

		//■追加
		//GoogleツールバーはShowDWで?UIハンドラーを変更するのでここで上書き。
		//ShowWindowで表示/非表示を切り替えれば解決するのかな?要調査
		if(bActivated)
			ChangeUIHandler();			//UIHandlerを変更する

		return	true;
	}

tabbrowser111.gif
これでビルド/実行すると、そのページにエラーがあってもエラーダイアログがでなくなった。

しかし逆にエラーがあってもユーザーにはエラーがあったことが分からなくなってしまった。次回はユーザーが分かるようにエラーがあった場合には通知する処理を追加する。


※ちなみに「このリンク」をクリックするとスクリプトエラーが発生する。

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

前の10件 1  2  3  4  5  6  7





usefullcode@gmail.com

About 2009年04月

2009年04月にブログ「UsefullCode.net」に投稿されたすべてのエントリーです。

前の記事は2009年03月です。

次の記事は2010年05月です。

他にも多くのエントリーがあります。メインページ記事一覧も見てください。