第10回 「お気に入り」メニューを表示する (タブブラウザーを作る)

tabbrowser40.gif
次はタブブラウザーに「お気に入り」の選択機能を実装する。一般的に出回っているタブブラウザーの中には独自形式の「お気に入り」を実装しているものもある。独自形式の方が機能追加などが柔軟にでき便利だ。
しかしここでは多機能で一般的に使えるタブブラウザーの開発を目指しているわけでもないので独自形式は避け、Internet Explorerの「お気に入り」を利用する。

前回までに作成した作成したプロジェクトで「リソース」ウインドウを開き、ウインドウフレーム用メニューに「お気に入り」とその下に「dummy」の項目を追加する。
「dummy」のIDは「ID_FAVORITE_DUMMY」とした。この項目は「お気に入り」メニューの場所を特定するために利用するだけで実際にメニューを表示するときには削除する。

tabbrowser41.gif
メニューリソースの編集が終わったら一度保存するために「ファイル」メニューの「すべてを保存」を選択する。

tabbrowser42.gif
次にソリューションウインドウにある「resource.h」をダブルクリックして開く。このときに警告が出るが、「OK」ボタンを押し警告は無視する。そしてここに2行追加する。

「お気に入り」として表示するメニューの数はユーザーによって異なるため動的にメニュー項目を追加する必要がある。ここではID_FAVORITE_FIRSTから始まるIDを「お気に入り」用として使うことにした。
#define ID_FAVORITE_FIRST               34000			//お気に入りは全部で2万アイテムまでサポート
#define ID_FAVORITE_LAST                54000			//

tabbrowser43.gif
次にソリューションウインドウの「MainFrm.ht」を開き、お気に入りメニュー用のクラスを追加する。

Internet Explorerの「お気に入り」はユーザーフォルダの中にインターネットショートカットの「ファイル」として保存されている。ここでは「フォルダー構造をそのままメニューとして表示する」で作成したCDnpFolderMenuクラス派生でお気に入りを表示する。
#include "DnpFolderMenu.h"					//■追加

#include "shlwapi.h"						//■追加
#pragma	comment(lib,"shlwapi.lib")			//■追加

//■追加
//「お気に入り」メニュークラス
class	CFavoritesMenu	: public CDnpFolderMenu
{
public:
	BEGIN_MSG_MAP(CFavoritesMenu)
		CHAIN_MSG_MAP(CDnpFolderMenu)
	END_MSG_MAP()


protected:

	///
	///\brief
	///	メニューに表示する文字列の設定
	///
	///ファイル名とパス(フォルダー名とパス)を参考にメニューにする文字列を決める。
	///
	///falseを返すとそのアイテムは表示しない
	///
	virtual	bool	TranslateMenuString(LPCTSTR pszPath,LPCTSTR pszFileName,CAtlString& strMenuString,bool bFolder)
	{
		//フォルダはフォルダ名をそのまま表示名として使う
		if(bFolder)
		{
			strMenuString = pszFileName;
			return	true;
		}

		//ファイルはインターネットショートカット(拡張子が.url)なら拡張子を除いたファイル名を表示
		//それ以外の拡張子のファイルはfalseを返して非表示にする

		LPTSTR	pszExt;

		strMenuString = pszFileName;
		pszExt = ::PathFindExtension(strMenuString.GetBuffer(0));		//拡張子の開始ポインタ取得
		if(pszExt == NULL)
			return	false;

		if(::_tcsicmp(pszExt,_T(".url")) != 0)
			return	false;		//拡張子が.urlではなかった

		*pszExt = NULL;			//拡張子を消す

		return	true;
	}
};

tabbrowser44.gif
さらにメッセージハンドラの処理などを追加する。

メニューが開いたことを知らせる「WM_MENUSELECT」を検出して開いたメニュー項目の最初に「ID_FAVORITE_DUMMY」があれば「お気に入り」用のメニューと判断して処理している。

ユーザーによって異なる「お気に入り」フォルダの取得には「OSに関係なく特殊フォルダのパスを取得する」で作成したSafeSHGetSpecialFolderPathを利用した。
	BEGIN_MSG_MAP(CMainFrame)
		MESSAGE_HANDLER(WM_DNP_CHANGEFOCUS, OnDnpChangeFocus)
		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)
		NOTIFY_CODE_HANDLER(TBVN_CONTEXTMENU, OnTabContextMenu)
		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()


	CFavoritesMenu	_cFovMenu;			//■追加 「お気に入り」メニュー用クラス

	//■追加
	//メニューを開いたときに呼ばれる処理
	//「お気に入り」が最初に開いたときだけ、ウインドウメニューと_cFovMenuを関連付ける
	LRESULT OnMenuSelect(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		int		nMenuIndex = LOWORD(wParam);
		int		nFlag = HIWORD(wParam);
		BOOL	ret;
		HMENU	hMenu = (HMENU)lParam;
		HMENU	hSubMenu;
		TCHAR	pszPath[MAX_PATH + 100];

		bHandled = FALSE;
		if(!(nFlag & MF_POPUP))
			return	0;

		hSubMenu = ::GetSubMenu(hMenu,nMenuIndex);
		if(hSubMenu == NULL)
			return	0;

		//「お気に入り」のメニューが開いたかどうかをチェック
		//「お気に入り」は一つ目のメニューをID_FAVORITE_DUMMYとした
		if(::GetMenuItemCount(hSubMenu) == 0 || ::GetMenuItemID(hSubMenu,0) != ID_FAVORITE_DUMMY)
			return	0;

		//「お気に入り」フォルダ取得
		ret = SafeSHGetSpecialFolderPath(m_hWnd,pszPath,CSIDL_FAVORITES,FALSE);
		if(ret == FALSE)
			return	0;

		//もうダミー項目は必要ないから削除
		::RemoveMenu(hSubMenu,ID_FAVORITE_DUMMY,MF_BYCOMMAND);

		//ウインドウメニューへ「お気に入り」を関連付ける
		_cFovMenu.ShowMenu(pszPath,hSubMenu,ID_FAVORITE_FIRST);

		return	0;
	}

tabbrowser45.gif 次に「お気に入り」の中の項目が選択されたときの処理などを実装する。 本来であれば選択された「お気に入り」をタブの中で開く処理を行うが、ここでは選択された「お気に入り」をメッセージボックスに表示するのみにしている。
	//■追加
	//「お気に入り」が選択されたときの処理
	LRESULT OnFavorite(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
	{
		CAtlString	strFolder;
		CAtlString	strFile;

		_cFovMenu.GetPathFromID(wID,strFolder,strFile);

		//選択されたお気に入りのファイル名をメッセージボックスで表示
		MessageBox(strFile);

		return	0;
	}

	//■追加
	//	SHGetSpecialFolderPathの全プラットフォーム対応バージョン
	//
	//「OSに関係なく特殊フォルダのパスを取得する」
	//http://www.usefullcode.net/2006/12/os.html
	//
	BOOL	SafeSHGetSpecialFolderPath(HWND hwndOwner,LPTSTR lpszPath,int nFolder,BOOL fCreate)
	{
		BOOL	(CALLBACK* g_pfnSHGetSpecialFolderPath)(HWND,LPTSTR,int,BOOL);
		HRESULT	(CALLBACK* g_pfnSHGetFolderPath)(HWND,int,HANDLE,DWORD,LPTSTR);

		int			i;
		BOOL		ret;
		HRESULT		hr;
		HMODULE		hDLL;
		TCHAR		pszDllFile[][15] = {_T("shfolder.dll"),_T("shell32.dll")};

		if(lpszPath == NULL)
			return	FALSE;
		*lpszPath = NULL;

		ret = FALSE;
		for(i = 0; i < 2; i++)
		{
			hDLL = ::LoadLibrary(pszDllFile[i]);
			if(hDLL == NULL)
				continue;

			#ifdef UNICODE
				(*(FARPROC*)&g_pfnSHGetSpecialFolderPath = ::GetProcAddress(hDLL,"SHGetSpecialFolderPathW"));
				(*(FARPROC*)&g_pfnSHGetFolderPath = ::GetProcAddress(hDLL,"SHGetFolderPathW"));
			#else
				(*(FARPROC*)&g_pfnSHGetSpecialFolderPath = ::GetProcAddress(hDLL,"SHGetSpecialFolderPathA"));
				(*(FARPROC*)&g_pfnSHGetFolderPath = ::GetProcAddress(hDLL,"SHGetFolderPathA"));
			#endif

			if(g_pfnSHGetSpecialFolderPath)
				ret = g_pfnSHGetSpecialFolderPath(hwndOwner,lpszPath,nFolder,fCreate);
			if(ret == FALSE && g_pfnSHGetFolderPath)
			{
				hr = g_pfnSHGetFolderPath(hwndOwner,nFolder | (fCreate ? CSIDL_FLAG_CREATE : 0),NULL,SHGFP_TYPE_DEFAULT,lpszPath);
				ret = (SUCCEEDED(hr)) ? TRUE : FALSE;
			}

			::FreeLibrary(hDLL);

			if(ret)
				return	TRUE;
		}

		return	FALSE;
	}

tabbrowser46.gif
さらにプロジェクトに「DnpFolderMenu.h」を追加する。

「プロジェクト」メニューから「既存項目に追加」を選択する。



※「DnpFolderMenu.h」はこのホームページの中の「フォルダー構造をそのままメニューとして表示する」からダウンロードできます。またダウンロードできるプロジェクト内にも同梱しています

tabbrowser47.gif
そして「DnpFolderMenu.h」を選択して「追加」ボタンを押す。

tabbrowser48.gif
これでビルド/実行するとInternet Explorerで登録した「お気に入り」がメニューとして表示された。

この「お気に入り」は表示順序がInternet Explorerとは異なり、アイコンも表示されないし、追加や整理する機能もない。それらは今後少しづつ実装する。


次回は選択された「お気に入り」を実際にタブとして開く動作を追加する。

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


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