Mercurial and Git clients can push and pull from this alias URL to interact with this repository. You can change to which repository an alias points by going to the Aliases link on the project page.
// Copyright (C) 2011 Fog Creek Software//// This program is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program. If not, see <http://www.gnu.org/licenses/>.#include "stdafx.h"#include "THgShell_h.h"#include "TortoiseUtils.h"#include "StringUtils.h"#include "THgStatus.h"#include "Winstat.h"#include "SysInfo.h"#include "RegistryConfig.h"#include "TortoiseIconBitmap.h"#include "THgVersion.h"#include "TortoiseHgCmenu.h"#include "RunDialog.h"#include "Kiln.h"#include <msi.h>voidCTortoiseHgCmenuBase::AddMenuList(UINTidCmd,constCString&strName){ATLTRACE("AddMenuList: idCmd = %d, name = '%s'\n",idCmd,strName);m_mapMenuId[idCmd]=m_mapMenuDesc[strName];}voidCTortoiseHgCmenuBase::InitMenuMaps(constCMenuDescription*pMenuDescs,intnCount){if(m_mapMenuDesc.IsEmpty()){CStringstrLang;GetRegistryConfig("CMenuLang",strLang);for(inti=0;i<nCount;i++){CMenuDescriptionmd=pMenuDescs[i];if(md.strName.IsEmpty()){ATLTRACE("**** InitMenuMaps: ignoring entry with empty name\n");break;}ATLTRACE("InitMenuMaps: adding '%s'\n",(LPCTSTR)md.strName); // Look for translation of menu and help text
if (!strLang.IsEmpty())
+ { GetCMenuTranslation(strLang, md.strName, md.strMenuText, md.strHelpText);
+ } m_mapMenuDesc[md.strName] = md;
}
}m_mapMenuId.RemoveAll();}voidInsertMenuItemWithIcon1(HMENUhMenu,UINTindexMenu,UINTidCmd,constCStringW&strMenuText,constCString&strIconName){// MFT_STRING is obsolete and should not be used (replaced by MIIM_STRING// from Win2K onward)MENUITEMINFOWmii;memset(&mii,0,sizeof(MENUITEMINFOW));mii.cbSize=sizeof(MENUITEMINFOW);mii.fMask=MIIM_ID|MIIM_STRING;mii.dwTypeData=(LPWSTR)(LPCWSTR)strMenuText;mii.cch=strMenuText.GetLength();mii.wID=idCmd;if(!strIconName.IsEmpty()){if(CSysInfo::IsVistaOrLater()){HBITMAPhBitmap=GetTortoiseIconBitmap(strIconName);if(hBitmap){mii.fMask|=MIIM_BITMAP;mii.hbmpItem=hBitmap;}else{ATLTRACE(" ***** InsertMenuItemWithIcon1: can't find '%s'\n",(LPCTSTR)strIconName);}}else{HICONhIcon=GetTortoiseIcon(strIconName);if(hIcon){mii.fMask|=MIIM_BITMAP|MIIM_DATA;mii.dwItemData=(ULONG_PTR)hIcon;mii.hbmpItem=HBMMENU_CALLBACK;}else{ATLTRACE(" ***** InsertMenuItemWithIcon1: can't find '%s'\n",(LPCTSTR)strIconName);}}}::InsertMenuItemW(hMenu,indexMenu,TRUE,&mii);ATLTRACE(L"InsertMenuItemWithIcon1('%s') finished\n",(LPCWSTR)strMenuText);}voidInsertSubMenuItemWithIcon2(HMENUhMenu,HMENUhSubMenu,UINTindexMenu,UINTidCmd,constCStringW&strMenuText,constCString&strIconName){// MFT_STRING is obsolete and should not be used (replaced by MIIM_STRING// from Win2K onward)MENUITEMINFOWmii;memset(&mii,0,sizeof(MENUITEMINFOW));mii.cbSize=sizeof(MENUITEMINFOW);mii.fMask=MIIM_SUBMENU|MIIM_ID|MIIM_STRING;mii.dwTypeData=(LPWSTR)(LPCWSTR)strMenuText;mii.cch=strMenuText.GetLength();mii.wID=idCmd;mii.hSubMenu=hSubMenu;if(CSysInfo::IsVistaOrLater()){HBITMAPhBitmap=GetTortoiseIconBitmap(strIconName);if(hBitmap){mii.fMask|=MIIM_BITMAP;mii.hbmpItem=hBitmap;}else{ATLTRACE(" ***** InsertSubMenuItemWithIcon2: can't find '%s'\n",(LPCTSTR)strIconName);}}else{HICONhIcon=GetTortoiseIcon(strIconName);if(hIcon){mii.fMask|=MIIM_BITMAP|MIIM_DATA;mii.dwItemData=(ULONG_PTR)hIcon;mii.hbmpItem=HBMMENU_CALLBACK;}else{ATLTRACE(" ***** InsertSubMenuItemWithIcon2: can't find '%s'\n",(LPCTSTR)strIconName);}}::InsertMenuItemW(hMenu,indexMenu,TRUE,&mii);ATLTRACE(L"InsertMenuItemWithIcon2('%s') finished\n",(LPCWSTR)strMenuText);}voidCTortoiseHgCmenuBase::InsertMenuItemByName(HMENUhMenu,constCString&strName,UINTindexMenu,UINTidCmd,UINTidCmdFirst,constCStringW&strPrefix){constCMenuDescriptionMap::CPair*pPair=m_mapMenuDesc.Lookup(strName);if(pPair==NULL){ATLTRACE("***** InsertMenuItemByName: can't find menu info for '%s'\n",(LPCTSTR)strName);return;}constCMenuDescription&md=pPair->m_value;AddMenuList(idCmd-idCmdFirst,strName);InsertMenuItemWithIcon1(hMenu,indexMenu,idCmd,strPrefix+md.strMenuText,md.strIconName);}constLPCWSTRTortoiseHgMenuEntryString=L"TortoiseHg";// returns -1 on error, 0 otherwiseintHasTortoiseMenu(HMENUhMenu,bool&bHasMenu){bHasMenu=false;intnCount=::GetMenuItemCount(hMenu);if(nCount==-1){ATLTRACE("***** HasTortoiseMenu: GetMenuItemCount returned -1\n");return-1;}MENUITEMINFOWmii;for(inti=0;i<nCount;++i){memset(&mii,0,sizeof(MENUITEMINFOW));mii.cbSize=sizeof(MENUITEMINFOW);// first GetMenuItemInfoW call: get size of menu item stringmii.fMask=MIIM_STRING;BOOLbRes=::GetMenuItemInfoW(hMenu,i,true,&mii);if(bRes==0){ATLTRACE("HasTortoiseMenu: first GetMenuItemInfo returned 0\n");continue;}if(mii.dwTypeData!=MFT_STRING){// not a stringcontinue;}// allocate buffer for the stringCStringWstrMenuItemText;LPWSTRlpszBuf=strMenuItemText.GetBuffer(mii.cch+1);// second GetMenuItemInfoW call: get string into buffermii.dwTypeData=lpszBuf;++mii.cch;// size of buffer is one more than length of stringbRes=::GetMenuItemInfoW(hMenu,i,true,&mii);strMenuItemText.ReleaseBuffer();if(bRes==0){ATLTRACE("HasTortoiseMenu: second GetMenuItemInfo returned 0\n");continue;}if(strMenuItemText==TortoiseHgMenuEntryString){ATLTRACE("HasTortoiseMenu: FOUND TortoiseHg menu entry\n");bHasMenu=true;return0;}}ATLTRACE("HasTortoiseMenu: TortoiseHg menu entry NOT found\n");return0;}voidCTortoiseHgCmenuBase::TweakMenuForVista(HMENUhMenu){if(!CSysInfo::IsVistaOrLater())return;MENUINFOmi;memset(&mi,0,sizeof(MENUINFO));mi.cbSize=sizeof(MENUINFO);mi.fMask=MIM_STYLE|MIM_APPLYTOSUBMENUS;mi.dwStyle=MNS_CHECKORBMP;::SetMenuInfo(hMenu,&mi);}#define ResultFromShort(i) ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, 0, (USHORT)(i)))// IContextMenuSTDMETHODIMPCTortoiseHgCmenuBase::QueryContextMenu(HMENUhMenu,UINTindexMenu,UINTidCmdFirst,UINTidCmdLast,UINTuFlags){ATLTRACE("CTortoiseHgCmenuBase::QueryContextMenu\n");UINTidCmd=idCmdFirst;BOOLbAppendItems=TRUE;if((uFlags&0x000F)==CMF_NORMAL)bAppendItems=TRUE;elseif(uFlags&CMF_VERBSONLY)bAppendItems=TRUE;elseif(uFlags&CMF_EXPLORE)bAppendItems=TRUE;elsebAppendItems=FALSE;if(!bAppendItems)returnS_OK;boolbHasTHgMenu=false;if(HasTortoiseMenu(hMenu,bHasTHgMenu)==0&&bHasTHgMenu){ATLTRACE("CTortoiseHgCmenuBase::QueryContextMenu: ""TortoiseHg menu entry already in menu -> skipping");returnS_OK;}InitMenuMaps(MenuDescList,MenuDescListCount);CStringpromoted_string=DefaultPromotedString;// default value if key not foundGetRegistryConfig("PromotedItems",promoted_string);CAtlList<CString>listPromoted;Tokenize(promoted_string,listPromoted,",");// Select menu to showboolbFileMenu=!m_listFiles.IsEmpty();boolbIsHgRepo=false;CStringstrCwd;if(!m_strFolder.IsEmpty()){strCwd=m_strFolder;}elseif(bFileMenu){strCwd=m_listFiles.GetHead();if(!::PathIsDirectory(strCwd))strCwd=DirName(strCwd);}if(!strCwd.IsEmpty()){// check if target directory is a Mercurial repositoryCStringstrRoot=GetHgRepoRoot(strCwd);bIsHgRepo=!strRoot.IsEmpty();if(m_listFiles.GetCount()==1&&strRoot==m_listFiles.GetHead()){bFileMenu=false;m_strFolder=strCwd;m_listFiles.RemoveAll();}}if((uFlags&CMF_EXTENDEDVERBS)==0){// shift key is not downif(!bIsHgRepo){// we are not inside a repoCStringstrCval;if(GetRegistryConfig("HideMenuOutsideRepo",strCval)!=0&&strCval=="1"){returnS_OK;// don't show thg cmenu entries}}}ATLTRACE("CTortoiseHgCmenuBase::QueryContextMenu: bIsHgRepo = %d, bFileMenu = %d\n",bIsHgRepo,bFileMenu);/* We have three menu types: files-selected, no-files-selected, no-repo */constLPCTSTR*ppszEntries=NULL;intnCount;if(bIsHgRepo){if(bFileMenu){ppszEntries=RepoFilesMenu;nCount=RepoFilesMenuCount;}else{ppszEntries=RepoNoFilesMenu;nCount=RepoNoFilesMenuCount;}}else{ppszEntries=NoRepoMenu;nCount=NoRepoMenuCount;}// start building TortoiseHg menus and submenus::InsertMenu(hMenu,indexMenu++,MF_SEPARATOR|MF_BYPOSITION,0,NULL);for(inti=0;i<nCount;i++){CStringstrName=ppszEntries[i];if(listPromoted.Find(strName)!=NULL){if((strName=="kiln"||strName=="kilnfiles")&&!KilnGetUrl(strCwd)){continue;}InsertMenuItemByName(hMenu,strName,indexMenu++,idCmd++,idCmdFirst,L"Hg ");}}constHMENUhSubMenu=::CreatePopupMenu();if(hSubMenu){UINTindexSubMenu=0;boolbIsSeparator=true;for(inti=0;i<nCount;i++){if(ppszEntries[i]==NULL){if(!bIsSeparator){::InsertMenu(hSubMenu,indexSubMenu++,MF_SEPARATOR|MF_BYPOSITION,0,NULL);bIsSeparator=true;}}else{CStringstrName=ppszEntries[i];if(listPromoted.Find(strName)==NULL){if((strName=="kiln"||strName=="kilnfiles")&&!KilnGetUrl(strCwd)){continue;}InsertMenuItemByName(hSubMenu,strName,indexSubMenu++,idCmd++,idCmdFirst,L"");bIsSeparator=false;}}}if(bIsSeparator&&indexSubMenu>0)::RemoveMenu(hSubMenu,indexSubMenu-1,MF_BYPOSITION);TweakMenuForVista(hSubMenu);}ATLTRACE(" CTortoiseHgCmenuBase::QueryContextMenu: adding main THG menu\n");InsertSubMenuItemWithIcon2(hMenu,hSubMenu,indexMenu++,idCmd++,TortoiseHgMenuEntryString,"hg.ico");::InsertMenu(hMenu,indexMenu++,MF_SEPARATOR|MF_BYPOSITION,0,NULL);TweakMenuForVista(hMenu);returnResultFromShort(idCmd-idCmdFirst);}STDMETHODIMPCTortoiseHgCmenuBase::InvokeCommand(LPCMINVOKECOMMANDINFOlpcmi){ATLTRACE("CTortoiseHgCmenuBase::InvokeCommand");HRESULThr=E_INVALIDARG;if(!HIWORD(lpcmi->lpVerb)){UINTidCmd=LOWORD(lpcmi->lpVerb);ATLTRACE("CTortoiseHgCmenuBase::InvokeCommand: idCmd = %d\n",idCmd);constCMenuIdCmdMap::CPair*p=m_mapMenuId.Lookup(idCmd);if(p!=NULL){RunDialog(lpcmi->hwnd,p->m_value.strName,m_listFiles,m_strFolder);hr=S_OK;}else{ATLTRACE("***** CTortoiseHgCmenuBase::InvokeCommand: action not found ""for idCmd %d\n",idCmd);}}returnhr;}STDMETHODIMPCTortoiseHgCmenuBase::GetCommandString(UINT_PTRidCmd,UINTuFlags,UINTFAR*reserved,LPSTRpszName,UINTcchMax){// see http://msdn.microsoft.com/en-us/library/bb776094%28VS.85%29.aspxHRESULThr=S_FALSE;constchar*psz="";constwchar_t*pszw=0;#ifdef _DEBUGCStringstrFlags="?";switch(uFlags){caseGCS_HELPTEXTW:
strFlags="GCS_HELPTEXTW";break;caseGCS_HELPTEXTA:
strFlags="GCS_HELPTEXTA";break;caseGCS_VALIDATEW:
strFlags="GCS_VALIDATEW";break;caseGCS_VALIDATEA:
strFlags="GCS_VALIDATEA";break;caseGCS_VERBW:
strFlags="GCS_VERBW";break;caseGCS_VERBA:
strFlags="GCS_VERBA";break;}ATLTRACE("CTortoiseHgCmenuBase::GetCommandString: idCmd = %d, uFlags = %d (%s), ""cchMax = %d\n",idCmd,uFlags,(LPCTSTR)strFlags,cchMax);#endifconstCMenuIdCmdMap::CPair*p=m_mapMenuId.Lookup((UINT)idCmd);if(p==NULL){ATLTRACE("***** CTortoiseHgCmenuBase::GetCommandString: idCmd not found\n");}else{ATLTRACE("CTortoiseHgCmenuBase::GetCommandString: name = '%s'\n",(LPCTSTR)p->m_value.strName);if(uFlags==GCS_HELPTEXTW){pszw=p->m_value.strHelpText;hr=S_OK;intnSize=p->m_value.strHelpText.GetLength();if(nSize>=40){ATLTRACE("***** CTortoiseHgCmenuBase::GetCommandString: warning: ""length of help text is %d, which is not reasonably ""short (<40)\n",nSize);}}elseif(uFlags==GCS_HELPTEXTA){// we don't provide ansi help textspsz="";hr=S_OK;}elseif(uFlags==GCS_VERBW||uFlags==GCS_VERBA){#if 0 psz = p->m_value.name;#else// bugfix: don't provide verbs ("rename" conflicted with rename of explorer)psz="";#endifhr=S_OK;}elseif(uFlags==GCS_VALIDATEW||uFlags==GCS_VALIDATEA){hr=S_OK;}}if(cchMax<1){ATLTRACE("CTortoiseHgCmenuBase::GetCommandString: cchMax = %d (is <1)\n",cchMax);returnhr;}size_tnSize=0;if(uFlags&GCS_UNICODE){LPWSTRlpszDest=(LPWSTR)pszName;CStringWstrUnicode=psz;LPCWSTRlpszSrc=pszw?pszw:strUnicode;wcsncpy_s(lpszDest,cchMax,lpszSrc,cchMax-1);*(lpszDest+cchMax-1)=0;nSize=wcslen(lpszSrc);ATLTRACE(L"CTortoiseHgCmenuBase::GetCommandString: res = %d, "L"pszName (wide) = '%s'\n",(int)hr,lpszDest);}else{strncpy_s(pszName,cchMax,psz,cchMax-1);*(pszName+cchMax-1)=0;nSize=strlen(psz);ATLTRACE("CShellExtCMenu::GetCommandString: res = %d, pszName = '%s'\n",(int)hr,psz);}if(nSize>cchMax-1){ATLTRACE("***** CShellExtCMenu::GetCommandString: string was truncated: ""size = %d, cchMax = %d\n",nSize,cchMax);}returnhr;}STDMETHODIMPCTortoiseHgCmenuBase::HandleMenuMsg(UINTuMsg,WPARAMwParam,LPARAMlParam){LRESULTlResult;returnHandleMenuMsg2(uMsg,wParam,lParam,&lResult);}STDMETHODIMPCTortoiseHgCmenuBase::HandleMenuMsg2(UINTuMsg,WPARAMwParam,LPARAMlParam,LRESULT*pResult){// A great tutorial on owner drawn menus in shell extension can be found// here: http://www.codeproject.com/shell/shellextguide7.aspLRESULTlResult;if(!pResult)pResult=&lResult;*pResult=FALSE;switch(uMsg){caseWM_MEASUREITEM:
{MEASUREITEMSTRUCT*lpmis=(MEASUREITEMSTRUCT*)lParam;if(lpmis==NULL)break;lpmis->itemWidth+=2;if(lpmis->itemHeight<16)lpmis->itemHeight=16;*pResult=TRUE;}break;caseWM_DRAWITEM:
{DRAWITEMSTRUCT*lpdis=(DRAWITEMSTRUCT*)lParam;if(!lpdis||(lpdis->CtlType!=ODT_MENU)||!lpdis->itemData)break;::DrawIconEx(lpdis->hDC,lpdis->rcItem.left-16,lpdis->rcItem.top+(lpdis->rcItem.bottom-lpdis->rcItem.top-16)/2,(HICON)lpdis->itemData,16,16,0,0,DI_NORMAL);*pResult=TRUE;}break;default:returnS_OK;}returnS_OK;}#ifdef _DEBUGvoidCTortoiseHgCmenuBase::PrintDebugHeader(LPCITEMIDLISTpIDFolder,LPDATAOBJECTpDataObj){ATLTRACE("CTortoiseHgCmenuBase::Initialize\n");// get installed MSI product id (for debugging purposes for now)#ifdef _M_X64LPCTSTRlpszShellExId="{D5D1E532-CDAD-4FFD-9695-757B8A29B4BA}";#elseLPCTSTRlpszShellExId="{728E8840-5878-4EA7-918F-281C2697ABB1}";#endifcharszProductId[50];UINTmsires=::MsiGetProductCode(lpszShellExId,szProductId);ATLTRACE("MSI shellexid: %s\n",lpszShellExId);ATLTRACE("MSI msires: %d",msires);ATLTRACE("MSI installed product id: %s\n",szProductId);DWORDdwBufSize=300;CAutoVectorPtr<char>buf(newchar[dwBufSize]);msires=::MsiGetProductInfo(szProductId,INSTALLPROPERTY_INSTALLLOCATION,buf,&dwBufSize);if(msires==ERROR_SUCCESS){ATLTRACE("MSI install location: %s\n",&buf[0]);}else{ATLTRACE("MSI install location: error %d\n",msires);}ATLTRACE(L"---- TortoiseHg shell extension version %s ----",(LPCTSTR)THgVersion);ATLTRACE(" pIDFolder: %p\n",pIDFolder);ATLTRACE(" pDataObj: %p\n",pDataObj);}#endifSTDMETHODIMPCTortoiseHgCmenuBase::Initialize(LPCITEMIDLISTpIDFolder,LPDATAOBJECTpDataObj,HKEYhRegKey){TCHARszName[MAX_PATH+1];#ifdef _DEBUGPrintDebugHeader(pIDFolder,pDataObj);#endifm_strFolder.Empty();m_listFiles.RemoveAll();if(pDataObj){FORMATETCfmt={CF_HDROP,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL};STGMEDIUMstg={TYMED_HGLOBAL};if(SUCCEEDED(pDataObj->GetData(&fmt,&stg))&&stg.hGlobal){HDROPhDrop=(HDROP)::GlobalLock(stg.hGlobal);if(hDrop){UINTuNumFiles=::DragQueryFile(hDrop,0xFFFFFFFF,NULL,0);ATLTRACE(" hDrop uNumFiles = %d\n",uNumFiles);for(UINTi=0;i<uNumFiles;++i){if(::DragQueryFile(hDrop,i,szName,MAX_PATH)>0){ATLTRACE(" DragQueryFile [%d] = '%s'\n",i,szName);m_listFiles.AddTail(szName);}}}else{ATLTRACE(" hDrop is NULL\n");}::GlobalUnlock(stg.hGlobal);if(stg.pUnkForRelease){IUnknown*relInterface=(IUnknown*)stg.pUnkForRelease;relInterface->Release();}}else{ATLTRACE(" pDataObj->GetData failed\n");}}// if a directory backgroundif(pIDFolder){::SHGetPathFromIDList(pIDFolder,szName);ATLTRACE(" Folder '%s'\n",szName);m_strFolder=szName;}// disable context menu if neither the folder nor the files// have been foundif(m_strFolder.IsEmpty()&&m_listFiles.IsEmpty()){ATLTRACE(" shell extension not available on this object\n");returnE_FAIL;}else{returnS_OK;}}CTortoiseHgCmenuBase::CTortoiseHgCmenuBase(){}CTortoiseHgCmenu::CTortoiseHgCmenu(){}
Attach a Trello Card
Add a tag
Your session has expired
You are no longer logged in. Please log in and try your request again.
Filter RSS Feed
This RSS feed URL allows you to see the contents of your current filter using any feed reader.
This link includes a special authentication token. If you share the URL with anyone else, they can see this RSS feed's activity. You can disable these tokens when needed.
Your current filter is unsaved; changing it won't affect this RSS feed.