[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: Native menu rendering with icons patch (W2k, XP, Vista)

From: Adam Strzelecki <ono_at_java.pl>
Date: 2007-06-11 13:28:56 CEST

Hi Stefan,

As I'm feeling bit responsible for missing icons on Windows 2000 I've
make another but this time complete patch that renders icons on Vista
natively too without disrupting the visual style and fixes W2K problems.

If it using hints from:
http://shellrevealed.com/blogs/shellblog/archive/2007/02/06/Vista-Style-Menus_2C00_-Part-1-_2D00_-Adding-icons-to-standard-menus.aspx

However instead WIC it is using GdiPlus (faster) as a PARGB32 conversion
library.

Please add "gdiplus.lib" to link libraries and "gdiplus.dll" to delayed
DLLs so it is only loaded on Vista (when needed).

Note that the issue on Windows 2000 with missing icons only exists when
uFlags of QueryContextMenu is 0 (this is bug in Windows 2000). So I
tricked the function and I use hbmp(Un)checked only in this case, in
other cases we may use HBMMENU_CALLBACK as it works okay (i.e. you
right-click on the folder), and you got full sized icons.

Also note that this method should allow you to use 32bit icons on XP and
Vista for context menus.

So the most important changes are GdiPlus initialization on Vista and
IconToBitmapPARGB32 function that creates PARGB32 bitmap from any icon
for Vista, so it can be used as a handle on popup menu in hbmpItem directly.

I removed also IconToBitmap transparentColor parameter, because actually
all Windows are treating RGB(255, 255, 255) as transparent color anyway
for hbmp(Un)checked, so specifing anything other makes no sense.

Best regards,

-- 
: nanoANT Adam Strzelecki :
:       nanoant.com       :

Index: src/TortoiseShell/ContextMenu.cpp
===================================================================
--- src/TortoiseShell/ContextMenu.cpp (wersja 9717)
+++ src/TortoiseShell/ContextMenu.cpp (kopia robocza)
@@ -180,12 +180,6 @@
                                    LPDATAOBJECT pDataObj,
                                    HKEY /* hRegKey */)
 {
- OSVERSIONINFOEX inf;
- ZeroMemory(&inf, sizeof(OSVERSIONINFOEX));
- inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
- GetVersionEx((OSVERSIONINFO *)&inf);
- fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);
-
         ATLTRACE("Shell :: Initialize\n");
         PreserveChdir preserveChdir;
         files_.clear();
@@ -540,7 +534,7 @@
         return NOERROR;
 }
 
-void CShellExt::InsertSVNMenu(BOOL istop, HMENU menu, UINT pos, UINT_PTR id, UINT stringid, UINT icon, UINT idCmdFirst, SVNCommands com)
+void CShellExt::InsertSVNMenu(BOOL istop, HMENU menu, UINT pos, UINT_PTR id, UINT stringid, UINT icon, UINT idCmdFirst, SVNCommands com, UINT uFlags)
 {
         TCHAR menutextbuffer[255] = {0};
         TCHAR verbsbuffer[255] = {0};
@@ -553,10 +547,10 @@
                 _tcscpy_s(menutextbuffer, 255, _T("SVN "));
         }
         _tcscat_s(menutextbuffer, 255, stringtablebuffer);
- if ((fullver >= 0x600)||(fullver <= 0x500))
+ if ((fullver < 0x500)||(fullver == 0x500 && !uFlags))
         {
                 InsertMenu(menu, pos, MF_BYPOSITION | MF_STRING , id, menutextbuffer);
- HBITMAP bmp = IconToBitmap(icon, (COLORREF)GetSysColor(COLOR_MENU));
+ HBITMAP bmp = IconToBitmap(icon);
                 SetMenuItemBitmaps(menu, pos, MF_BYPOSITION, bmp, bmp);
         }
         else
@@ -566,7 +560,7 @@
                 menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_BITMAP | MIIM_STRING;
                 menuiteminfo.fType = MFT_STRING;
                 menuiteminfo.dwTypeData = menutextbuffer;
- menuiteminfo.hbmpItem = HBMMENU_CALLBACK;
+ menuiteminfo.hbmpItem = (fullver >= 0x500) ? IconToBitmapPARGB32(icon) : HBMMENU_CALLBACK;
                 menuiteminfo.wID = id;
                 InsertMenuItem(menu, pos, TRUE, &menuiteminfo);
         }
@@ -596,7 +590,7 @@
                 mySubMenuMap[pos] = com;
 }
 
-HBITMAP CShellExt::IconToBitmap(UINT uIcon, COLORREF transparentColor)
+HBITMAP CShellExt::IconToBitmap(UINT uIcon)
 {
         std::map<UINT, HBITMAP>::iterator bitmap_it = bitmaps.lower_bound(uIcon);
         if (bitmap_it != bitmaps.end() && bitmap_it->first == uIcon)
@@ -606,22 +600,21 @@
         if (!hIcon)
                 return NULL;
 
- RECT rect;
+ RECT rect;
 
         rect.right = ::GetSystemMetrics(SM_CXMENUCHECK);
         rect.bottom = ::GetSystemMetrics(SM_CYMENUCHECK);
 
- rect.left =
- rect.top = 0;
+ rect.left = rect.top = 0;
 
- HWND desktop = ::GetDesktopWindow();
+ HWND desktop = ::GetDesktopWindow();
         if (desktop == NULL)
         {
                 DestroyIcon(hIcon);
                 return NULL;
         }
 
- HDC screen_dev = ::GetDC(desktop);
+ HDC screen_dev = ::GetDC(desktop);
         if (screen_dev == NULL)
         {
                 DestroyIcon(hIcon);
@@ -655,8 +648,9 @@
                 return NULL;
         }
 
- // Fill the background of the compatible DC with the given colour
- ::SetBkColor(dst_hdc, transparentColor);
+ // Fill the background of the compatible DC with the white colour
+ // that is taken by menu routines as transparent
+ ::SetBkColor(dst_hdc, RGB(255, 255, 255));
         ::ExtTextOut(dst_hdc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
 
         // Draw the icon into the compatible DC
@@ -726,7 +720,7 @@
         if (((uFlags & 0x000f)!=CMF_NORMAL)&&(!(uFlags & CMF_EXPLORE))&&(!(uFlags & CMF_VERBSONLY)))
                 return NOERROR;
 
- if (folder_.size() == 0)
+ if (folder_.size() == 0)
                 return NOERROR; // no target? this should never happen, but we shouldn't crash anyway just because we can't handle this.
 
         //the drop handler only has eight commands, but not all are visible at the same time:
@@ -737,21 +731,21 @@
         UINT idCmd = idCmdFirst;
 
         if ((itemStates & ITEMIS_FOLDERINSVN)&&((itemStates & ITEMIS_INSVN)&&((~itemStates) & ITEMIS_ADDED)))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVEMENU, 0, idCmdFirst, ShellMenuDropMove);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVEMENU, 0, idCmdFirst, ShellMenuDropMove, uFlags);
         if ((itemStates & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_ONLYONE)&&((~itemStates) & ITEMIS_ADDED))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVERENAMEMENU, 0, idCmdFirst, ShellMenuDropMoveRename);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPMOVERENAMEMENU, 0, idCmdFirst, ShellMenuDropMoveRename, uFlags);
         if ((itemStates & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&((~itemStates) & ITEMIS_ADDED))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYMENU, 0, idCmdFirst, ShellMenuDropCopy);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYMENU, 0, idCmdFirst, ShellMenuDropCopy, uFlags);
         if ((itemStates & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_INSVN)&&(itemStates & ITEMIS_ONLYONE)&&((~itemStates) & ITEMIS_ADDED))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYRENAMEMENU, 0, idCmdFirst, ShellMenuDropCopyRename);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYRENAMEMENU, 0, idCmdFirst, ShellMenuDropCopyRename, uFlags);
         if ((itemStates & ITEMIS_FOLDERINSVN)&&((~itemStates) & ITEMIS_INSVN))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYADDMENU, 0, idCmdFirst, ShellMenuDropCopyAdd);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPCOPYADDMENU, 0, idCmdFirst, ShellMenuDropCopyAdd, uFlags);
         if (((itemStates & ITEMIS_FOLDERINSVN)==0)&&(itemStates & ITEMIS_INSVN))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTMENU, 0, idCmdFirst, ShellMenuDropExport);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTMENU, 0, idCmdFirst, ShellMenuDropExport, uFlags);
         if (((itemStates & ITEMIS_FOLDERINSVN)==0)&&(itemStates & ITEMIS_INSVN))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTEXTENDEDMENU, 0, idCmdFirst, ShellMenuDropExportExtended);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_DROPEXPORTEXTENDEDMENU, 0, idCmdFirst, ShellMenuDropExportExtended, uFlags);
         if ((itemStates & ITEMIS_FOLDERINSVN)&&(itemStates & ITEMIS_PATCHFILE))
- InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_MENUAPPLYPATCH, 0, idCmdFirst, ShellMenuApplyPatch);
+ InsertSVNMenu(FALSE, hMenu, indexMenu++, idCmd++, IDS_MENUAPPLYPATCH, 0, idCmdFirst, ShellMenuApplyPatch, uFlags);
         if (idCmd != idCmdFirst)
                 InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL);
 
@@ -965,7 +959,8 @@
                                                                                 menuInfo[menuIndex].menuTextID,
                                                                                 menuInfo[menuIndex].iconID,
                                                                                 idCmdFirst,
- menuInfo[menuIndex].command);
+ menuInfo[menuIndex].command,
+ uFlags);
                                                 if (!bIsTop)
                                                         bMenuEntryAdded = true;
                                         }
@@ -981,48 +976,51 @@
         MENUITEMINFO menuiteminfo;
         ZeroMemory(&menuiteminfo, sizeof(menuiteminfo));
         menuiteminfo.cbSize = sizeof(menuiteminfo);
- if ((fullver >= 0x600)||(fullver <= 0x500))
- menuiteminfo.fMask = MIIM_STRING | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_DATA;
- else
- {
- menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_BITMAP | MIIM_STRING;
- menuiteminfo.hbmpItem = HBMMENU_CALLBACK;
- }
         menuiteminfo.fType = MFT_STRING;
          menuiteminfo.dwTypeData = _T("TortoiseSVN\0\0");
 
- HBITMAP bmp = NULL;
+ UINT uIcon = IDI_APP;
         if (folder_.size())
         {
- bmp = IconToBitmap(IDI_MENUFOLDER, (COLORREF)GetSysColor(COLOR_MENU));
+ uIcon = IDI_MENUFOLDER;
                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuFolder;
                 myIDMap[idCmd] = ShellSubMenuFolder;
                 menuiteminfo.dwItemData = (ULONG_PTR)g_MenuIDString;
         }
         else if (((~itemStates) & ITEMIS_SHORTCUT) && (files_.size()==1))
         {
- bmp = IconToBitmap(IDI_MENUFILE, (COLORREF)GetSysColor(COLOR_MENU));
+ uIcon = IDI_MENUFILE;
                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuFile;
                 myIDMap[idCmd] = ShellSubMenuFile;
         }
         else if ((itemStates & ITEMIS_SHORTCUT) && (files_.size()==1))
         {
- bmp = IconToBitmap(IDI_MENULINK, (COLORREF)GetSysColor(COLOR_MENU));
+ uIcon = IDI_MENULINK;
                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuLink;
                 myIDMap[idCmd] = ShellSubMenuLink;
         }
         else if (files_.size() > 1)
         {
- bmp = IconToBitmap(IDI_MENUMULTIPLE, (COLORREF)GetSysColor(COLOR_MENU));
+ uIcon = IDI_MENUMULTIPLE;
                 myIDMap[idCmd - idCmdFirst] = ShellSubMenuMultiple;
                 myIDMap[idCmd] = ShellSubMenuMultiple;
         }
         else
         {
- bmp = IconToBitmap(IDI_APP, (COLORREF)GetSysColor(COLOR_MENU));
                 myIDMap[idCmd - idCmdFirst] = ShellSubMenu;
                 myIDMap[idCmd] = ShellSubMenu;
         }
+ HBITMAP bmp = NULL;
+ if ((fullver < 0x500)||(fullver == 0x500 && !uFlags))
+ {
+ bmp = IconToBitmap(uIcon);
+ menuiteminfo.fMask = MIIM_STRING | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_DATA;
+ }
+ else
+ {
+ menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_BITMAP | MIIM_STRING;
+ menuiteminfo.hbmpItem = (fullver >= 0x600) ? IconToBitmapPARGB32(uIcon) : HBMMENU_CALLBACK;
+ }
         menuiteminfo.hbmpChecked = bmp;
         menuiteminfo.hbmpUnchecked = bmp;
         menuiteminfo.hSubMenu = subMenu;
@@ -1567,7 +1565,7 @@
                                          UINT FAR * /*reserved*/,
                                          LPSTR pszName,
                                          UINT cchMax)
-{
+{
         //do we know the id?
         std::map<UINT_PTR, UINT_PTR>::const_iterator id_it = myIDMap.lower_bound(idCmd);
         if (id_it == myIDMap.end() || id_it->first != idCmd)
@@ -1636,8 +1634,8 @@
 
 STDMETHODIMP CShellExt::HandleMenuMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
- LRESULT res;
- return HandleMenuMsg2(uMsg, wParam, lParam, &res);
+ LRESULT res;
+ return HandleMenuMsg2(uMsg, wParam, lParam, &res);
 }
 
 STDMETHODIMP CShellExt::HandleMenuMsg2(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *pResult)
@@ -1857,7 +1855,7 @@
         {
                 // check if the item name is ignored or the mask
                 size_t p = 0;
- while ( (p=ignoredprops.find( ignorepath,p )) != -1 )
+ while ( (p=ignoredprops.find( ignorepath,p )) != -1 )
                 {
                         if ( (p==0 || ignoredprops[p-1]==TCHAR('\n'))
                                 && (p+_tcslen(ignorepath)==ignoredprops.length() || ignoredprops[p+_tcslen(ignorepath)+1]==TCHAR('\n')) )
@@ -1948,8 +1946,8 @@
                 menuiteminfo.cbSize = sizeof(menuiteminfo);
                 menuiteminfo.fMask = MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU | MIIM_DATA | MIIM_BITMAP | MIIM_STRING;
                 menuiteminfo.fType = MFT_STRING;
- menuiteminfo.hbmpItem = HBMMENU_CALLBACK;
- HBITMAP bmp = IconToBitmap(IDI_IGNORE, (COLORREF)GetSysColor(COLOR_MENU));
+ HBITMAP bmp = (fullver >= 0x600) ? IconToBitmapPARGB32(IDI_IGNORE) : IconToBitmap(IDI_IGNORE);
+ menuiteminfo.hbmpItem = (fullver >= 0x600) ? bmp : HBMMENU_CALLBACK;
                 menuiteminfo.hbmpChecked = bmp;
                 menuiteminfo.hbmpUnchecked = bmp;
                 menuiteminfo.hSubMenu = ignoresubmenu;
@@ -1976,4 +1974,25 @@
         }
 }
 
+HBITMAP CShellExt::IconToBitmapPARGB32(UINT uIcon)
+{
+ std::map<UINT, HBITMAP>::iterator bitmap_it = bitmaps.lower_bound(uIcon);
+ if (bitmap_it != bitmaps.end() && bitmap_it->first == uIcon)
+ return bitmap_it->second;
+ if (!m_gdipToken)
+ return NULL;
+ HICON hIcon = (HICON)LoadImage(g_hResInst, MAKEINTRESOURCE(uIcon), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
+ if (!hIcon)
+ return NULL;
+ Bitmap icon(hIcon);
+ Bitmap bmp(16, 16, PixelFormat32bppPARGB);
+ Graphics g(&bmp);
+ g.DrawImage(&icon, 0, 0, 16, 16);
 
+ HBITMAP hBmp = NULL;
+ bmp.GetHBITMAP(Color(255, 0, 0, 0), &hBmp);
+
+ if(hBmp)
+ bitmaps.insert(bitmap_it, std::make_pair(uIcon, hBmp));
+ return hBmp;
+}
Index: src/TortoiseShell/ShellExt.cpp
===================================================================
--- src/TortoiseShell/ShellExt.cpp (wersja 9717)
+++ src/TortoiseShell/ShellExt.cpp (kopia robocza)
@@ -31,9 +31,16 @@
 
 std::set<CShellExt *> g_exts;
 
+
 // *********************** CShellExt *************************
 CShellExt::CShellExt(FileState state)
 {
+ OSVERSIONINFOEX inf;
+ ZeroMemory(&inf, sizeof(OSVERSIONINFOEX));
+ inf.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ GetVersionEx((OSVERSIONINFO *)&inf);
+ fullver = MAKEWORD(inf.dwMinorVersion, inf.dwMajorVersion);
+
     m_State = state;
 
     m_cRef = 0L;
@@ -47,6 +54,13 @@
     };
     InitCommonControlsEx(&used);
         LoadLangDll();
+
+ m_gdipToken = NULL;
+ if(fullver >= 0x600)
+ {
+ GdiplusStartupInput gdiplusStartupInput;
+ GdiplusStartup(&m_gdipToken, &gdiplusStartupInput, NULL);
+ }
 }
 
 CShellExt::~CShellExt()
@@ -59,6 +73,9 @@
         bitmaps.clear();
         g_cRefThisDll--;
         g_exts.erase(this);
+
+ if(m_gdipToken)
+ GdiplusShutdown(m_gdipToken);
 }
 
 void LoadLangDll()
Index: src/TortoiseShell/ShellExt.h
===================================================================
--- src/TortoiseShell/ShellExt.h (wersja 9717)
+++ src/TortoiseShell/ShellExt.h (kopia robocza)
@@ -25,6 +25,10 @@
 #include "RemoteCacheLink.h"
 #include "SVNFolderStatus.h"
 
+// GdiPlus includes
+#include <GdiPlus.h>
+using namespace Gdiplus;
+
 extern UINT g_cRefThisDll; // Reference count of this DLL.
 extern HINSTANCE g_hmodThisDll; // Instance handle for this DLL
 extern SVNFolderStatus * g_pCachedStatus; // status cache
@@ -162,6 +166,7 @@
 
         static MenuInfo menuInfo[];
         WORD fullver;
+ ULONG_PTR m_gdipToken;
         FileState m_State;
         ULONG m_cRef;
         //std::map<int,std::string> verbMap;
@@ -185,12 +190,13 @@
         std::map<UINT, HBITMAP> bitmaps;
 #define MAKESTRING(ID) LoadStringEx(g_hResInst, ID, stringtablebuffer, sizeof(stringtablebuffer)/sizeof(TCHAR), (WORD)CRegStdWORD(_T("Software\\TortoiseSVN\\LanguageID"), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)))
 private:
- void InsertSVNMenu(BOOL istop, HMENU menu, UINT pos, UINT_PTR id, UINT stringid, UINT icon, UINT idCmdFirst, SVNCommands com);
+ void InsertSVNMenu(BOOL istop, HMENU menu, UINT pos, UINT_PTR id, UINT stringid, UINT icon, UINT idCmdFirst, SVNCommands com, UINT uFlags);
         void InsertIgnoreSubmenus(UINT &idCmd, UINT idCmdFirst, HMENU hMenu, HMENU subMenu, UINT &indexMenu, int &indexSubMenu, unsigned __int64 topmenu);
         stdstring WriteFileListToTempFile();
         LPCTSTR GetMenuTextFromResource(int id);
         void GetColumnStatus(const TCHAR * path, BOOL bIsDir);
- HBITMAP IconToBitmap(UINT uIcon, COLORREF transparentColor);
+ HBITMAP IconToBitmap(UINT uIcon);
+ HBITMAP IconToBitmapPARGB32(UINT uIcon);
         int GetInstalledOverlays(); ///< returns the maximum number of overlays TSVN shall use
         STDMETHODIMP QueryDropContext(UINT uFlags, UINT idCmdFirst, HMENU hMenu, UINT &indexMenu);
         bool IsIllegalFolder(std::wstring folder, int * cslidarray);

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tortoisesvn.tigris.org
For additional commands, e-mail: dev-help@tortoisesvn.tigris.org
Received on Mon Jun 11 13:29:18 2007

This is an archived mail posted to the TortoiseSVN Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.