Click here to Skip to main content
15,891,851 members
Articles / Desktop Programming / ATL

Visual Studio Favorites

Rate me:
Please Sign up or sign in to vote.
2.00/5 (3 votes)
16 Mar 20033 min read 52.7K   710   14  
Add shortcuts, favorites, and more to Visual Studio.
// FavoritesMenu.cpp: implementation of the CFavoritesMenu class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "FavoritesMenu.h"
#include "EditFavoritesDlg.h"
#include "resource.h"

#include <algorithm>
//For some reason it's not working unless I include this here also... weird...
#include <initguid.h>
#include <ObjModel\appguid.h>

#include <shlobj.h>

#define	MENUID_CONFIG	(0x1000)
#define	MENUID_MORE		(0x1001)
#define	MENUID_BASE		(0x1100)

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CFavoritesMenu* CFavoritesMenu::m_pThis=NULL;

CFavoritesMenu::CFavoritesMenu() : m_hTopLevelMenu(NULL),m_hMenu(NULL),m_nextMenuID(MENUID_BASE),m_hImageList(NULL),m_imageListHeight(0),m_imageListWidth(0)
{
	m_pThis = this;
	::GetCursorPos(&m_menuDropMousePoint);

	HBITMAP tempbmp=(HBITMAP)LoadImage(_Module.m_hInstResource,
													MAKEINTRESOURCE(IDB_MENUICONS) ,
													IMAGE_BITMAP,
													0, 0,
													LR_CREATEDIBSECTION | LR_DEFAULTSIZE);
	BITMAP	bm={0};
	GetObject(tempbmp,sizeof(bm), &bm);

	m_imageListWidth = bm.bmWidth/TOTAL_NUM_ICONS;
	m_imageListHeight= bm.bmHeight;

	// Create the full-color image list
	// cx, cy = your icon width & height
	m_hImageList = ImageList_Create(m_imageListWidth, 
												m_imageListHeight,
												ILC_MASK | ILC_COLOR32,
												TOTAL_NUM_ICONS,
												0);

	ImageList_AddMasked(m_hImageList, tempbmp, 0xFF00FF);

	DeleteObject(tempbmp);

	// Set up the correct sizes for spacing and whatnot so we don't have to do it over
	// all the bloody time.
	m_menuIconSize.cx = std::_MAX(m_imageListWidth , ::GetSystemMetrics(SM_CXMENUCHECK));
	m_menuIconSize.cy = std::_MAX(m_imageListHeight, ::GetSystemMetrics(SM_CYMENUCHECK));
}

CFavoritesMenu::~CFavoritesMenu()
{
	ImageList_Destroy(m_hImageList);
	ReleaseAllMappedElms();
	m_pThis=NULL;
}

void CFavoritesMenu::Load(IApplication *pIApp)
{
	ReleaseAllMappedElms();

	// This will load and recurse it all
	CConfigurationFile::Load();

	if (m_hTopLevelMenu==NULL)
	{	// need to have at least the top level menu
		m_hTopLevelMenu = ::CreatePopupMenu();
		m_hMenu = m_hTopLevelMenu;
		if (m_hTopLevelMenu!=NULL)
			AddItem(NULL,IT_CONFIG);
	}

	if (m_hTopLevelMenu!=NULL)
	{
		// Now make the window to handle popup messages and handle it
		LPCSTR pszWndClass="CFavoritesMenu_Handler";
		HINSTANCE hinst=HINSTANCE(::GetModuleHandle(0));

		WNDCLASS wndclass={CS_PARENTDC,st_PopupMenuWndProc,0,0,hinst,
									 NULL,NULL,NULL,"",pszWndClass};

		::RegisterClass(&wndclass);

		HWND hWnd = ::CreateWindowEx(0L,pszWndClass,"",0,0,0,0,0,
											 NULL,NULL,hinst,(LPVOID)this);

		// track
		long v=::TrackPopupMenu(m_hTopLevelMenu,TPM_NONOTIFY|TPM_RETURNCMD|TPM_LEFTALIGN,
										m_menuDropMousePoint.x, m_menuDropMousePoint.y, 0 , hWnd, NULL);

		if (v>0)
			DoMenuCommand(v,pIApp);

		::DestroyWindow(hWnd);
		::DestroyMenu(m_hTopLevelMenu);
		m_hTopLevelMenu=m_hMenu=NULL;	
	}

	ReleaseAllMappedElms();
}

ITEM_TYPES 	CFavoritesMenu::LookupMenuItemSpecifics(const int menuID,char *pTextBuffer)
{
	IXMLDOMElement *pElm = m_id2ElmMap[menuID];
	if (pElm!=NULL)
	{
		return GetItemSpecifics(pElm,pTextBuffer);
	}
	else
	{
		if (menuID==MENUID_CONFIG)
		{
			if (pTextBuffer!=NULL)
				strcpy(pTextBuffer,"Config...");
			return IT_CONFIG;
		}
		if (menuID==MENUID_MORE)
		{
			if (pTextBuffer!=NULL)
				strcpy(pTextBuffer,"More...");
			return IT_FOLDER;
		}
	}
	return IT_UNKNOWN;
}

LRESULT CALLBACK CFavoritesMenu::st_PopupMenuWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (uMsg==WM_MEASUREITEM)
	{
		MEASUREITEMSTRUCT *pMS = (MEASUREITEMSTRUCT *)lParam;
		const int menuID = pMS->itemData;

		char text[256];
		m_pThis->LookupMenuItemSpecifics(menuID,text);

		HDC hdc= ::CreateCompatibleDC(NULL);
		HGDIOBJ oldObj = ::SelectObject(hdc,::GetStockObject(DEFAULT_GUI_FONT));
		RECT r={0};
		::DrawText(hdc,text, -1, &r, DT_LEFT|DT_SINGLELINE|DT_EXPANDTABS|DT_CALCRECT);

		// Handle the icon here

		pMS->itemWidth=m_pThis->m_menuIconSize.cx + 2 + (r.right - r.left);

		pMS->itemHeight=2 + std::_MAX( (r.bottom - r.top) , m_pThis->m_menuIconSize.cy );

		::SelectObject(hdc,oldObj);
		::DeleteDC(hdc);
		return TRUE;
	}
	else
	if (uMsg==WM_DRAWITEM)
	{
		DRAWITEMSTRUCT *pDS = (DRAWITEMSTRUCT*)lParam;
		const int menuID = pDS->itemData;

		char text[256];
		const int itype = (int)m_pThis->LookupMenuItemSpecifics(menuID,text);

		const bool isSelected = ((pDS->itemState & ODS_SELECTED)!=0);
		HDC &hdc = pDS->hDC;
		RECT *pRect = &pDS->rcItem;
		HBRUSH hbrush = NULL;
		COLORREF oldColor=0;
		if (isSelected)
		{
			hbrush = ::CreateSolidBrush(::GetSysColor(COLOR_HIGHLIGHT));
			oldColor = ::SetTextColor(hdc,::GetSysColor(COLOR_HIGHLIGHTTEXT));
		}
		else
		{
			hbrush = ::CreateSolidBrush(::GetSysColor(COLOR_MENU));
			oldColor = ::SetTextColor(hdc,::GetSysColor(COLOR_MENUTEXT));
		}

		::FillRect(hdc,pRect,hbrush);

		// Handle the icon here
		if (itype>=0 && itype < TOTAL_NUM_ICONS)
		{
			ImageList_DrawEx(m_pThis->m_hImageList,itype,hdc,pRect->left,pRect->top,0,0,CLR_NONE,CLR_NONE,ILD_TRANSPARENT);
		}

		pRect->left += m_pThis->m_menuIconSize.cx + 2;
		
		const int oldMode = ::SetBkMode(hdc,TRANSPARENT);
		HGDIOBJ oldObj = ::SelectObject(hdc,::GetStockObject(DEFAULT_GUI_FONT));

		DrawText(hdc,text, -1, pRect, DT_LEFT|DT_SINGLELINE|DT_VCENTER|DT_EXPANDTABS);

		::SelectObject(hdc,oldObj);
		::SetBkMode(hdc,oldMode);
		::SetTextColor(hdc,oldColor);

		::DeleteObject(hbrush);
		return TRUE;
	}
	return DefWindowProc(hwnd,uMsg,wParam,lParam);
}

//////////////////////////////////////////////////////////////////////

bool CFavoritesMenu::AddItem(IXMLDOMElement *pElm,const ITEM_TYPES itemType)
{
	if (itemType==IT_CONFIG)
	{
		::AppendMenu(m_hMenu,MF_STRING|MF_OWNERDRAW,MENUID_CONFIG,(LPCSTR)MENUID_CONFIG);
		return true;
	}
	else
	if (itemType==IT_BREAK)
	{
		::AppendMenu(m_hMenu,MF_SEPARATOR,0,NULL);	// seperators are not owner drawn to make it easier
		return true;
	}
	else
	if (itemType==IT_FOLDER)
	{
		// Folders are added and then the RecursiveLoad function checks the menu handles and
		// previous item to make sub stuff
		HMENU subMenu = ::CreatePopupMenu();
		::AppendMenu(m_hMenu,MF_POPUP|MF_OWNERDRAW,(UINT)subMenu,(LPCSTR)m_nextMenuID);
	}
	else
	{
		::AppendMenu(m_hMenu,MF_STRING|MF_OWNERDRAW,m_nextMenuID,(LPCSTR)m_nextMenuID);
	}

	m_id2ElmMap[m_nextMenuID] = pElm;
	if (pElm!=NULL)
		pElm->AddRef();	// make sure it's kept around.

	++m_nextMenuID;
	return true;
}

void	CFavoritesMenu::ReleaseAllMappedElms()
{
	T_menuElmMap_Itor cur = m_id2ElmMap.begin();
	while (cur!=m_id2ElmMap.end())
	{
		IXMLDOMElement *pElm = cur->second;
		if (pElm!=NULL)
			pElm->Release();
		++cur;
	}
	m_id2ElmMap.clear();
}


// This is called to start processing each menu level.
void CFavoritesMenu::RecursiveLoad(IXMLDOMNode *pChildNode)
{
	if (m_hTopLevelMenu==NULL)
	{	// need to make the first one
		m_hTopLevelMenu = ::CreatePopupMenu();
		m_hMenu = m_hTopLevelMenu;
		AddItem(NULL,IT_CONFIG);
		CConfigurationFile::RecursiveLoad(pChildNode);
	}
	else
	{	// we're making a sub menu. Check the last item added; if it is a popup menu,
		// then switch over to it.
		HMENU lastSubMenu=NULL;

		int cnt = ::GetMenuItemCount(m_hMenu);
		if (cnt>0)
			lastSubMenu = ::GetSubMenu(m_hMenu,cnt-1);

		if (lastSubMenu==NULL)
		{
			lastSubMenu = ::CreatePopupMenu();
			::AppendMenu(m_hMenu,MF_POPUP|MF_OWNERDRAW,(UINT)lastSubMenu,(LPCSTR)MENUID_MORE);	// something generic
		}

		HMENU prevHmenu = m_hMenu;

		m_hMenu = lastSubMenu;

		CConfigurationFile::RecursiveLoad(pChildNode);

		m_hMenu = prevHmenu;
	}
}

void CFavoritesMenu::DoMenuCommand(const long menuID,IApplication *pIApp)
{
	ITEM_TYPES itemType = LookupMenuItemSpecifics(menuID,NULL);

	CComVariant	pathVar;
	char pszPath[MAX_PATH]="";

	IXMLDOMElement *pElm = m_id2ElmMap[menuID];
	if (pElm!=NULL)
	{
		pElm->getAttribute(_bstr_t("path"),&pathVar);
		AtlW2AHelper(pszPath,pathVar);
	}

	switch (itemType)
	{
		case IT_CONFIG:
			HandleConfigCommand();
			break;
		case IT_EXE:
		case IT_LINK:
			// Links and exe's are able to directly shell open
			if (pszPath[0]!=0)
			{
label_openviashellex:
				char pszDir[MAX_PATH];
				strcpy(pszDir,pszPath);
				if (!PathIsDirectory(pszDir))
					PathRemoveFileSpec(pszDir);
				// Note: best to not use open, since this blows up with .url files and anything else
				// that does not default to strict dde "open" for the default action.
				ShellExecute(NULL, NULL /*"open"*/, pszPath, NULL, pszDir, SW_SHOWNORMAL);
			}
			break;
		case IT_FILE:
			if (pszPath[0]!=0)
			{	// when opening a file, we need to check if what will open it Visual Studio
				// If so, then we have to have the current app server open the file, instead
				// of telling the shell to do so, since otherwise, nothing happens (the shell
				// is trying to ivoke a command on an already blocked com thread)
				TCHAR szExe[MAX_PATH]="";
				FindExecutable(pszPath, _T(""), szExe); 
 
				LPTSTR pszApp = PathFindFileName(szExe);
				if (strcmpi(pszApp,"msdev.exe")==0)
				{	// have to tell visual studio to open the file
					goto label_openviamsdevcom;
				}
				else
				{	// otherwise, we should be able to open this.
					goto label_openviashellex;
				}
			}
			break;
		case IT_BSC:
			{
label_openviamsdevcom:
				CComPtr<IDispatch> pDispDoc;
				pIApp->get_Documents(&pDispDoc);
				CComQIPtr<IDocuments, &IID_IDocuments> pDocs(pDispDoc);
				if (pDocs!=NULL && pszPath[0]!=0)
				{
					CComPtr<IDispatch> pOpenDoc;	// we ignore this value
					pDocs->Open(pathVar.bstrVal,
									CComVariant("Auto"),
									CComVariant(VARIANT_FALSE),
									&pOpenDoc);
				}
			}
			break;
	}
}

void CFavoritesMenu::HandleConfigCommand()
{
	CEditFavoritesDlg	dlg;
	dlg.DoModal();
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I work when you don't. Sleep is for wussies.

The answer is no, whatever the question is. You can't have it, you don't need it, and you'd just break it in five minutes if I give it to you anyways.

If you're interested, my web site is at NOPcode.com and has lots of cool stuff with programming and a one-of-a-kind Audio Server.

Comments and Discussions