第64回 「タブ」をバックグラウンドで開けるようにする (タブブラウザーを作る)

tabbrowser288.gif
今回は新しいページをバックグラウンドのタブで開けるようにする。

現状では「ファイルメニュー」の「新規作成」でも、リンクを右クリックして現れるメニューから「新しいウインドウで開く」を選択したときも、新しく開いたタブにフォーカスがあたり、それまで開いていたタブから新しいタブに切り替わってしまう。

それを
・「ファイルメニュー」の「新規作成」で開いたタブはアクティブで
・リンクを右クリックして現れるメニューから「新しいウインドウで開く」から開いたタブはバックグラウンドで
開くようにする。

「タブ」コントロールはWTLのCTabViewを利用している。このクラスには標準ではタブをバックグラウンドで開くオプションがなく、常にアクティブにしてタブが作られてしまう。 CTabViewに新しいタブをバックグラウンドで開くオプションを追加するために、派生クラスを作り、AddPageとInsertPageを上書きする。
//■追加
class	CTabViewEx : public CTabView
{

public:

	//WTLのCTabView::InsertPageからソースをコピー&改変
	//
	//bOpenInBackground=trueなら新しいタブにフォーカスを与えない(バックグラウンドで開く)
	//
	bool AddPage(HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL,bool bOpenInBackground=false)
	{
		return InsertPage(GetPageCount(), hWndView, lpstrTitle, nImage, pData, bOpenInBackground);
	}

	//WTLのCTabView::InsertPageからソースをコピー&改変
	//
	//bOpenInBackground=trueなら新しいタブにフォーカスを与えない(バックグラウンドで開く)
	//
	bool InsertPage(int nPage, HWND hWndView, LPCTSTR lpstrTitle, int nImage = -1, LPVOID pData = NULL,bool bOpenInBackground=false)
	{
		ATLASSERT(::IsWindow(m_hWnd));
		ATLASSERT(nPage == GetPageCount() || IsValidPageIndex(nPage));

		CTabViewEx* pT = static_cast<CTabViewEx*>(this);		//■変更 CTabViewExでキャスト

		int cchBuff = lstrlen(lpstrTitle) + 1;
		LPTSTR lpstrBuff = NULL;
		ATLTRY(lpstrBuff = new TCHAR[cchBuff]);
		if(lpstrBuff == NULL)
			return false;

		SecureHelper::strcpy_x(lpstrBuff, cchBuff, lpstrTitle);

		CTempBuffer<TCHAR, _WTL_STACK_ALLOC_THRESHOLD> buff;
		LPTSTR lpstrTabText = buff.Allocate(m_cchTabTextLength + 1);
		if(lpstrTabText == NULL)
			return false;

		pT->ShortenTitle(lpstrTitle, lpstrTabText, m_cchTabTextLength + 1);

		SetRedraw(FALSE);

		TCITEMEXTRA tcix = { 0 };
		tcix.tciheader.mask = TCIF_TEXT | TCIF_IMAGE | TCIF_PARAM;
		tcix.tciheader.pszText = lpstrTabText;
		tcix.tciheader.iImage = nImage;
		tcix.tvpage.hWnd = hWndView;
		tcix.tvpage.lpstrTitle = lpstrBuff;
		tcix.tvpage.pData = pData;
		int nItem = m_tab.InsertItem(nPage, tcix);
		if(nItem == -1)
		{
			delete [] lpstrBuff;
			SetRedraw(TRUE);
			return false;
		}

		if(bOpenInBackground == false || GetPageCount() == 1)		//■条件を追加
		{
			//新しいタブをアクティブにする
			SetActivePage(nItem);
			pT->OnPageActivated(m_nActivePage);
		}

		if(GetPageCount() == 1)
			pT->ShowTabControl(true);

		pT->UpdateLayout();

		SetRedraw(TRUE);
		RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);

		return true;
	}
};

tabbrowser289.gif そしてCMainFrame::m_viewをCTabViewから新しい派生クラスのCTabViewExに変更する。
class CMainFrame : public CFrameWindowImpl<CMainFrame>, public CUpdateUI<CMainFrame>,
		public CMessageFilter, public CIdleHandler
		,protected CIShellMenuPopup					//「お気に入り」メニュー用クラス。publicの必要なし
		,protected CIEToolbarManager				//IEツールバー管理クラス。publicの必要なし
{
	CAddressBarCtrl	_wndAddressBar;			// アドレスバー用コンボボックス

	CTabViewEx			m_view;			//■変更
	CCommandBarCtrl2 m_CmdBar;

public:

tabbrowser290.gif 新しいタブを作るための関数CreateNewTabでも新しいタブをアクティブにするかバックグラウンドにするかの選択ができるようにする。
	//■変更(bOpenInBackgroundへの対応)
	//タブの新規作成
	//nPosはタブを追加する場所。nPos<0(もしくはnPos=m_view.GetPageCount())で一番後ろ、
	//そのほかの数値はCTabView::InsertPageにそのまま渡す
	//
	//戻ったポインタは自動削除されるため、deleteの必要なし
	//
	//bOpenInBackground=trueならバックグラウンドでタブを開く
	//
	CTabBrowser100View*	CreateNewTab(LPCTSTR pszURL,LPCTSTR pszTitle,int nPos=-1,int nImage=-1,bool bOpenInBackground=false)
	{
		bool	ret;
		CTabBrowser100View*	pView;

		//■追加
		//bOpenInBackgroundが指定されていても現在タブが1つもない状態ならbOpenInBackgroundをfalseに強制設定する
		if(bOpenInBackground && m_view.GetPageCount() == 0)
			bOpenInBackground = false;

		pView = new CTabBrowser100View(&m_view);	//タブビューのポインタを渡す
		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,bOpenInBackground);	//■変更
		else
			ret = m_view.InsertPage(nPos,pView->m_hWnd,_T(""),nImage,pView,bOpenInBackground);		//■変更

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

		//IEツールバーの生成
		{
			size_t	i;
			size_t	nSize;

			nSize = _acAvailableIEToolbar.GetCount();
			for(i = 0; i < nSize; i++)
			{
				if(_acAvailableIEToolbar[i].pwndDummy)
					pView->AddIEToolbar(*_acAvailableIEToolbar[i].pwndDummy,bOpenInBackground);		//IEツールバー生成
			}
		}

		return	pView;
	}

tabbrowser292.gif 独自メッセージ用のクラスに新しいタブをどう開くかのオプション用変数を追加する。
#pragma	once

class	CTabBrowser100View;

// CMainFrame::nDnpCreateNewTab() 用の情報クラス
class	CDnpCreateNewTabInfo
{
public:
	CAtlString	strURL;
	CAtlString	strTitle;
	int		nImage;
	int		nPos;
	bool	bOpenInBackground;		//■追加

	CTabBrowser100View*	pView;

	CDnpCreateNewTabInfo()
	{
		strURL	= _T("about:blank");
		pView	= NULL;
		nImage	= -1;
		nPos	= -1;
		bOpenInBackground = false;	//■追加
	}
};

tabbrowser291.gif そして独自メッセーによるタブ生成処理時にも指定ができるようにする。
	//WM_DNP_CREATENEWTABによりタブの作成
	//WPARAMはCDnpCreateNewTabInfoへのポインタを渡す
	LRESULT OnDnpCreateNewTab(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CDnpCreateNewTabInfo*	pInfo = (CDnpCreateNewTabInfo*)wParam;

		if(pInfo == NULL)
			return	0;

		//■変更 pInfo->bOpenInBackgroundを追加
		pInfo->pView = CreateNewTab(pInfo->strURL,pInfo->strTitle,pInfo->nPos,pInfo->nImage,pInfo->bOpenInBackground);

		return	0;
	}

tabbrowser293.gif 最後にリンクを右クリックして「新しいウインドウで開く」を選択したときに、バックグラウンドでタブを開くように指定すれば終わりだ。
	//新しいウインドウで開く前に呼ばれる処理
	//新しいタブで開く
	void	__stdcall OnNewWindow2(IDispatch **ppDisp,VARIANT_BOOL *Cancel)
	{
		CDnpCreateNewTabInfo	cInfo;

		cInfo.bOpenInBackground = true;		//■追加 リンクを新しいタブで開くときはバックグラウンドで開く

		//新しいタブを作る
		GetTopLevelWindow().SendMessage(WM_DNP_CREATENEWTAB,(WPARAM)&cInfo);
		if(cInfo.pView == NULL || cInfo.pView->_pIWebBrowser2 == NULL)
			return;

		//新しいタブで開くようにIEコントロールにポインタを渡す
		cInfo.pView->_pIWebBrowser2->put_RegisterAsBrowser(VARIANT_TRUE);	//これしなくても開くようになっているのはなぜ?IE8から?一応実行しておく
		cInfo.pView->_pIWebBrowser2->get_Application(ppDisp);
	}

tabbrowser294.gif
これで新しいタブをバックグラウンドで開けるようになった。

しかし今回の修正によりリンクを新しいタブで開いたときに「Googleツールバー」の下端が欠けてしまい、きちんと表示されなくなった。次回はその問題を修正する。

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


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