// 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,HICON*pIconOpt)
{
ITEM_TYPES itemType = IT_UNKNOWN;
IXMLDOMElement *pElm = m_id2ElmMap[menuID];
if (pElm!=NULL)
{
itemType = GetItemSpecifics(pElm,pTextBuffer);
if (pIconOpt!=NULL)
{
*pIconOpt = GetItemIconOpt(pElm,m_menuIconSize);
}
}
else
{
if (menuID==MENUID_CONFIG)
{
if (pTextBuffer!=NULL)
strcpy(pTextBuffer,"Config...");
itemType = IT_CONFIG;
}
else if (menuID==MENUID_MORE)
{
if (pTextBuffer!=NULL)
strcpy(pTextBuffer,"More...");
itemType = IT_FOLDER;
}
}
return itemType;
}
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]="";
HICON hicon=NULL;
const int itype = (int)m_pThis->LookupMenuItemSpecifics(menuID,text,&hicon);
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 (hicon!=NULL)
{
DrawIconEx(hdc,pRect->left,pRect->top,hicon,
m_pThis->m_menuIconSize.cx,m_pThis->m_menuIconSize.cx,
0,NULL,DI_NORMAL);
DestroyIcon(hicon);
}
else
{
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();
}