OSDN Git Service

Merge with stable
[winmerge-jp/winmerge-jp.git] / ShellExtension / WinMergeShell.cpp
index 5e1dccf..7648b98 100644 (file)
@@ -4,7 +4,7 @@
 //    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
@@ -16,7 +16,7 @@
 /////////////////////////////////////////////////////////////////////////////
 // Look at http://www.codeproject.com/shell/ for excellent guide
 // to Windows Shell programming by Michael Dunn.
-// 
+//
 // This extension needs two registry values to be defined:
 //  HKEY_CURRENT_USER\Software\Thingamahoochie\WinMerge\ContextMenuEnabled
 //   defines if context menu is shown (extension enabled) and if
 //   overwrites 'Executable' if defined. Useful to overwrite
 //   option set from UI when debugging/testing.
 /////////////////////////////////////////////////////////////////////////////
-/** 
+/**
  * @file  WinMergeShell.cpp
  *
  * @brief Implementation of the ShellExtension class
  */
 // ID line follows -- this is updated by SVN
-// $Id$
+// $Id: WinMergeShell.cpp 6933 2009-07-26 14:07:03Z kimmov $
 
 #include "stdafx.h"
 #include "ShellExtension.h"
 #include "WinMergeShell.h"
 #include "UnicodeString.h"
 #include "RegKey.h"
-#include "coretools.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 
-/** 
+/**
  * @brief Flags for enabling and other settings of context menu.
  */
 enum ExtensionFlags
 {
        EXT_ENABLED = 0x01, /**< ShellExtension enabled/disabled. */
        EXT_ADVANCED = 0x02, /**< Advanced menuitems enabled/disabled. */
-       EXT_SUBFOLDERS = 0x04, /**< Subfolders included by default? */
 };
 
 /// Max. filecount to select
-static const int MaxFileCount = 2;
-/// Registry path to WinMerge 
+static const int MaxFileCount = 3;
+/// Registry path to WinMerge
 #define REGDIR _T("Software\\Thingamahoochie\\WinMerge")
 static const TCHAR f_RegDir[] = REGDIR;
 static const TCHAR f_RegLocaleDir[] = REGDIR _T("\\Locale");
+static const TCHAR f_RegSettingsDir[] = REGDIR _T("\\Settings");
 
 /**
  * @name Registry valuenames.
  */
-/*@{*/ 
+/*@{*/
 /** Shell context menuitem enabled/disabled */
 static const TCHAR f_RegValueEnabled[] = _T("ContextMenuEnabled");
 /** 'Saved' path in advanced mode */
@@ -79,25 +78,35 @@ static const TCHAR f_RegValuePath[] = _T("Executable");
 static const TCHAR f_RegValuePriPath[] = _T("PriExecutable");
 /** LanguageId */
 static const TCHAR f_LanguageId[] = _T("LanguageId");
+/** Recurse */
+static const TCHAR f_Recurse[] = _T("Recurse");
 /*@}*/
 
-/// Shown menustate
+/**
+ * @brief The states in which the menu can be.
+ * These states define what items are added to the menu and how those
+ * items work.
+ */
 enum
 {
-       MENU_SIMPLE = 0,
-       MENU_ONESEL_NOPREV,
-       MENU_ONESEL_PREV,
-       MENU_TWOSEL,
+       MENU_SIMPLE = 0,  /**< Simple menu, only "Compare item" is shown. */
+       MENU_ONESEL_NOPREV,  /**< One item selected, no previous selections. */
+       MENU_ONESEL_PREV,  /**< One item selected, previous selection exists. */
+       MENU_TWOSEL,  /**< Two items are selected. */
+       MENU_THREESEL
 };
 
 #define USES_WINMERGELOCALE CWinMergeTempLocale __wmtl__
 
+static String GetResourceString(UINT resourceID);
+
 class CWinMergeTempLocale
 {
 private:
        LCID m_lcidOld;
 public:
-       CWinMergeTempLocale() {
+       CWinMergeTempLocale()
+       {
                CRegKeyEx reg;
                if (reg.Open(HKEY_CURRENT_USER, f_RegLocaleDir) != ERROR_SUCCESS)
                        return;
@@ -106,13 +115,73 @@ public:
 
                int iLangId = reg.ReadDword(f_LanguageId, (DWORD)-1);
                if (iLangId != -1)
+               {
                        SetThreadLocale(MAKELCID(iLangId, SORT_DEFAULT));
+                       SetThreadUILanguage(iLangId);
+               }
        }
-       ~CWinMergeTempLocale() {
+       ~CWinMergeTempLocale()
+       {
                SetThreadLocale(m_lcidOld);
+               SetThreadUILanguage(LANGIDFROMLCID(m_lcidOld));
        }
 };
 
+/**
+ * @brief Load a string from resource.
+ * @param [in] Resource string ID.
+ * @return String loaded from resource.
+ */
+static String GetResourceString(UINT resourceID)
+{
+       TCHAR resStr[1024] = {0};
+       int res = LoadString(_Module.GetModuleInstance(), resourceID, resStr, 1024);
+       ATLASSERT(res != 0);
+       String strResource = resStr;
+       return strResource;
+}
+
+static HBITMAP MakeBitmapBackColorTransparent(HBITMAP hbmSrc)
+{
+       HDC hdcSrc = CreateCompatibleDC(NULL);
+       BITMAP bm;
+       GetObject(hbmSrc, sizeof(bm), &bm);
+       HBITMAP hbmSrcOld = (HBITMAP)SelectObject(hdcSrc, hbmSrc);
+       BITMAPINFO bi = {0};
+       bi.bmiHeader.biSize = sizeof BITMAPINFOHEADER;
+       bi.bmiHeader.biPlanes = 1;
+       bi.bmiHeader.biBitCount = 32;
+       bi.bmiHeader.biCompression = BI_RGB;
+       bi.bmiHeader.biWidth = bm.bmWidth;
+       bi.bmiHeader.biHeight = -bm.bmHeight;
+       DWORD *pBits = NULL;
+       HBITMAP hbmNew = CreateDIBSection(NULL, &bi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0);
+       if (pBits)
+       {
+               COLORREF clrTP = GetPixel(hdcSrc, 0, 0);
+               int bR = GetRValue(clrTP), bG = GetGValue(clrTP), bB = GetBValue(clrTP);
+       
+               for (int y = 0; y < bm.bmHeight; ++y)
+               {
+                       for (int x = 0; x < bm.bmWidth; ++x)
+                       {
+                               COLORREF clrCur = GetPixel(hdcSrc, x, y);
+                               int cR = GetRValue(clrCur), cG = GetGValue(clrCur), cB = GetBValue(clrCur);
+                               if (!(abs(cR - bR) <= 1 && abs(cG - bG) <= 1 && abs(cB - bB) <= 1))
+                               {
+                                       pBits[y * bm.bmWidth + x] = cB | (cG << 8) | (cR << 16) | 0xFF000000;
+                               }
+                       }
+               }
+       }
+
+       SelectObject(hdcSrc, hbmSrcOld);
+       DeleteDC(hdcSrc);
+
+       return hbmNew;
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 // CWinMergeShell
 
@@ -120,9 +189,40 @@ public:
 CWinMergeShell::CWinMergeShell()
 {
        m_dwMenuState = 0;
-       HBITMAP hMergeBmp = LoadBitmap(_Module.GetModuleInstance(),
-                       MAKEINTRESOURCE(IDB_WINMERGE));
-       m_MergeBmp.Attach(hMergeBmp);
+       int cx = GetSystemMetrics(SM_CXMENUCHECK);
+       int cy = GetSystemMetrics(SM_CYMENUCHECK);
+       int id_fileicon = cx > 16 ? IDB_WINMERGE32 : IDB_WINMERGE;
+       int id_diricon = cx > 16 ? IDB_WINMERGEDIR32 : IDB_WINMERGEDIR;
+
+       // compress or stretch icon bitmap according to menu item height
+       HBITMAP hMergeBmp = (HBITMAP)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(id_fileicon), IMAGE_BITMAP,
+                       cx, cy, LR_DEFAULTCOLOR);
+       HBITMAP hMergeDirBmp = (HBITMAP)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(id_diricon), IMAGE_BITMAP,
+                       cx, cy, LR_DEFAULTCOLOR);
+
+       OSVERSIONINFO osvi;
+       osvi.dwOSVersionInfoSize = sizeof OSVERSIONINFO;
+       GetVersionEx(&osvi);
+       if (osvi.dwMajorVersion >= 6)
+       {
+               m_MergeBmp = MakeBitmapBackColorTransparent(hMergeBmp);
+               DeleteObject(hMergeBmp);
+
+               m_MergeDirBmp = MakeBitmapBackColorTransparent(hMergeDirBmp);
+               DeleteObject(hMergeDirBmp);
+       }
+       else
+       {
+               m_MergeBmp = hMergeBmp;
+               m_MergeDirBmp = hMergeDirBmp;
+       }
+}
+
+/// Default destructor, unloads bitmap
+CWinMergeShell::~CWinMergeShell()
+{
+       DeleteObject(m_MergeDirBmp);
+       DeleteObject(m_MergeBmp);
 }
 
 /// Reads selected paths
@@ -152,7 +252,7 @@ HRESULT CWinMergeShell::Initialize(LPCITEMIDLIST pidlFolder,
                        return E_INVALIDARG;
 
                // Sanity check & make sure there is at least one filename.
-               UINT uNumFilesDropped = DragQueryFile (hDropInfo, 0xFFFFFFFF, NULL, 0);
+               UINT uNumFilesDropped = DragQueryFile(hDropInfo, 0xFFFFFFFF, NULL, 0);
                m_nSelectedItems = uNumFilesDropped;
 
                if (uNumFilesDropped == 0)
@@ -189,20 +289,26 @@ HRESULT CWinMergeShell::Initialize(LPCITEMIDLIST pidlFolder,
                GlobalUnlock(stg.hGlobal);
                ReleaseStgMedium(&stg);
        }
+       else
+       {
+               m_nSelectedItems = 0;
+       }
 
-       // No item selected - selection is the folder background
+               // No item selected - selection is the folder background
        if (pidlFolder)
        {
                TCHAR szPath[MAX_PATH] = {0};
 
                if (SHGetPathFromIDList(pidlFolder, szPath))
                {
-                       m_strPaths[0] = szPath;
-                       m_nSelectedItems = 1;
+                       if (m_nSelectedItems < MaxFileCount)
+                               m_strPaths[m_nSelectedItems++] = szPath;
                        hr = S_OK;
                }
                else
+               {
                        hr = E_INVALIDARG;
+               }
        }
        return hr;
 }
@@ -211,7 +317,6 @@ HRESULT CWinMergeShell::Initialize(LPCITEMIDLIST pidlFolder,
 HRESULT CWinMergeShell::QueryContextMenu(HMENU hmenu, UINT uMenuIndex,
                UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags)
 {
-       AFX_MANAGE_STATE(AfxGetStaticModuleState())
        int nItemsAdded = 0;
        USES_WINMERGELOCALE;
 
@@ -237,12 +342,14 @@ HRESULT CWinMergeShell::QueryContextMenu(HMENU hmenu, UINT uMenuIndex,
                }
                else
                {
-                       if (m_nSelectedItems == 1 && m_strPreviousPath.IsEmpty())
+                       if (m_nSelectedItems == 1 && m_strPreviousPath.empty())
                                m_dwMenuState = MENU_ONESEL_NOPREV;
-                       else if (m_nSelectedItems == 1 && !m_strPreviousPath.IsEmpty())
+                       else if (m_nSelectedItems == 1 && !m_strPreviousPath.empty())
                                m_dwMenuState = MENU_ONESEL_PREV;
                        else if (m_nSelectedItems == 2)
                                m_dwMenuState = MENU_TWOSEL;
+                       else if (m_nSelectedItems == 3)
+                               m_dwMenuState = MENU_THREESEL;
 
                        nItemsAdded = DrawAdvancedMenu(hmenu, uMenuIndex, uidFirstCmd);
                }
@@ -256,7 +363,6 @@ HRESULT CWinMergeShell::QueryContextMenu(HMENU hmenu, UINT uMenuIndex,
 HRESULT CWinMergeShell::GetCommandString(UINT_PTR idCmd, UINT uFlags,
                UINT* pwReserved, LPSTR pszName, UINT  cchMax)
 {
-       AFX_MANAGE_STATE(AfxGetStaticModuleState())
        USES_CONVERSION;
        USES_WINMERGELOCALE;
 
@@ -268,7 +374,7 @@ HRESULT CWinMergeShell::GetCommandString(UINT_PTR idCmd, UINT uFlags,
        }
        else
        {
-               if (idCmd > 1)
+               if (idCmd > 2)
                        return E_INVALIDARG;
        }
 
@@ -276,17 +382,17 @@ HRESULT CWinMergeShell::GetCommandString(UINT_PTR idCmd, UINT uFlags,
        // supplied buffer.
        if (uFlags & GCS_HELPTEXT)
        {
-               CString strHelp;
+               String strHelp;
 
                strHelp = GetHelpText(idCmd);
 
                if (uFlags & GCS_UNICODE)
                        // We need to cast pszName to a Unicode string, and then use the
                        // Unicode string copy API.
-                       lstrcpynW((LPWSTR) pszName, T2CW(strHelp), cchMax);
+                       lstrcpynW((LPWSTR) pszName, T2CW(strHelp.c_str()), cchMax);
                else
                        // Use the ANSI string copy API to return the help string.
-                       lstrcpynA(pszName, T2CA(strHelp), cchMax);
+                       lstrcpynA(pszName, T2CA(strHelp.c_str()), cchMax);
 
                return S_OK;
        }
@@ -296,9 +402,8 @@ HRESULT CWinMergeShell::GetCommandString(UINT_PTR idCmd, UINT uFlags,
 /// Runs WinMerge with given paths
 HRESULT CWinMergeShell::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
 {
-       AFX_MANAGE_STATE(AfxGetStaticModuleState())
        CRegKeyEx reg;
-       CString strWinMergePath;
+       String strWinMergePath;
        BOOL bCompare = FALSE;
        BOOL bAlterSubFolders = FALSE;
        USES_WINMERGELOCALE;
@@ -311,8 +416,8 @@ HRESULT CWinMergeShell::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
        if (!GetWinMergeDir(strWinMergePath))
                return S_FALSE;
 
-       // Check that file we are trying to execute exists and is executable
-       if (!CheckExecutable(strWinMergePath))
+       // Check that file we are trying to execute exists
+       if (!PathFileExists(strWinMergePath.c_str()))
                return S_FALSE;
 
        if (LOWORD(pCmdInfo->lpVerb) == 0)
@@ -326,23 +431,24 @@ HRESULT CWinMergeShell::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
                case MENU_ONESEL_NOPREV:
                        m_strPreviousPath = m_strPaths[0];
                        if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
-                               reg.WriteString(f_FirstSelection, m_strPreviousPath);
+                               reg.WriteString(f_FirstSelection, m_strPreviousPath.c_str());
                        break;
 
                case MENU_ONESEL_PREV:
                        m_strPaths[1] = m_strPaths[0];
                        m_strPaths[0] = m_strPreviousPath;
                        bCompare = TRUE;
-                       
+
                        // Forget previous selection
                        if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
                                reg.WriteString(f_FirstSelection, _T(""));
                        break;
 
                case MENU_TWOSEL:
+               case MENU_THREESEL:
                        // "Compare" - compare paths
                        bCompare = TRUE;
-                       m_strPreviousPath.Empty();
+                       m_strPreviousPath.erase();
                        break;
                }
        }
@@ -353,12 +459,12 @@ HRESULT CWinMergeShell::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
                case MENU_ONESEL_PREV:
                        m_strPreviousPath = m_strPaths[0];
                        if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
-                               reg.WriteString(f_FirstSelection, m_strPreviousPath);
+                               reg.WriteString(f_FirstSelection, m_strPreviousPath.c_str());
                        bCompare = FALSE;
                        break;
                default:
                        // "Compare..." - user wants to compare this single item and open WinMerge
-                       m_strPaths[1].Empty();
+                       m_strPaths[1].erase();
                        bCompare = TRUE;
                        break;
                }
@@ -372,18 +478,21 @@ HRESULT CWinMergeShell::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
        if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0)
                bAlterSubFolders = TRUE;
 
-       CString strCommandLine = FormatCmdLine(strWinMergePath, m_strPaths[0],
-               m_strPaths[1], bAlterSubFolders);
+       String strCommandLine = FormatCmdLine(strWinMergePath, m_strPaths[0],
+                       m_strPaths[1], bAlterSubFolders);
+
+       if (!m_strPaths[2].empty())
+               strCommandLine += _T(" \"") + m_strPaths[2] + _T("\"");
 
        // Finally start a new WinMerge process
        BOOL retVal = FALSE;
        STARTUPINFO stInfo = {0};
        stInfo.cb = sizeof(STARTUPINFO);
        PROCESS_INFORMATION processInfo = {0};
-       
-       retVal = CreateProcess(NULL, (LPTSTR)(LPCTSTR)strCommandLine,
-               NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
-               &stInfo, &processInfo);
+
+       retVal = CreateProcess(NULL, (LPTSTR)strCommandLine.c_str(),
+                       NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
+                       &stInfo, &processInfo);
 
        if (!retVal)
                return S_FALSE;
@@ -394,61 +503,40 @@ HRESULT CWinMergeShell::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo)
 }
 
 /// Reads WinMerge path from registry
-BOOL CWinMergeShell::GetWinMergeDir(CString &strDir)
+BOOL CWinMergeShell::GetWinMergeDir(String &strDir)
 {
        CRegKeyEx reg;
        if (!reg.QueryRegUser(f_RegDir))
                return FALSE;
-       
+
        // Try first reading debug/test value
-       strDir = reg.ReadString(f_RegValuePriPath, _T("")).c_str();
-       if (strDir.IsEmpty())
+       strDir = reg.ReadString(f_RegValuePriPath, _T(""));
+       if (strDir.empty())
        {
-               strDir = reg.ReadString(f_RegValuePath, _T("")).c_str();
-               if (strDir.IsEmpty())
+               strDir = reg.ReadString(f_RegValuePath, _T(""));
+               if (strDir.empty())
                        return FALSE;
-       }       
+       }
 
        return TRUE;
 }
 
-/// Checks if given file exists and is executable
-BOOL CWinMergeShell::CheckExecutable(CString path)
-{
-       String sExt;
-       SplitFilename(path, NULL, NULL, &sExt);
-       CString ext(sExt.c_str());
-
-       // Check extension
-       ext.MakeLower();
-       if (ext == _T("exe") || ext == _T("cmd") || ext == ("bat"))
-       {
-               // Check if file exists
-               struct _stati64 statBuffer;
-               int nRetVal = _tstati64(path, &statBuffer);
-               if (nRetVal > -1)
-                       return TRUE;
-       }
-       return FALSE;
-}
-
 /// Create menu for simple mode
 int CWinMergeShell::DrawSimpleMenu(HMENU hmenu, UINT uMenuIndex,
                UINT uidFirstCmd)
 {
-       CString strMenu;
-       VERIFY(strMenu.LoadString(IDS_CONTEXT_MENU));
+       String strMenu = GetResourceString(IDS_CONTEXT_MENU);
+       InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strMenu.c_str());
 
-       InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strMenu);
-       
        // Add bitmap
-       if ((HBITMAP)m_MergeBmp != NULL)
-               SetMenuItemBitmaps(hmenu, uMenuIndex, MF_BYPOSITION, m_MergeBmp, NULL);
-       
+       HBITMAP hBitmap = PathIsDirectory(m_strPaths[0].c_str()) ? m_MergeDirBmp : m_MergeBmp;
+       if (hBitmap != NULL)
+               SetMenuItemBitmaps(hmenu, uMenuIndex, MF_BYPOSITION, hBitmap, NULL);
+
        // Show menu item as grayed if more than two items selected
        if (m_nSelectedItems > MaxFileCount)
                EnableMenuItem(hmenu, uMenuIndex, MF_BYPOSITION | MF_GRAYED);
-       
+
        return 1;
 }
 
@@ -456,60 +544,63 @@ int CWinMergeShell::DrawSimpleMenu(HMENU hmenu, UINT uMenuIndex,
 int CWinMergeShell::DrawAdvancedMenu(HMENU hmenu, UINT uMenuIndex,
                UINT uidFirstCmd)
 {
-       CString strCompare;
-       CString strCompareEllipsis;
-       CString strCompareTo;
-       CString strReselect;
+       String strCompare = GetResourceString(IDS_COMPARE);
+       String strCompareEllipsis = GetResourceString(IDS_COMPARE_ELLIPSIS);
+       String strCompareTo = GetResourceString(IDS_COMPARE_TO);
+       String strReselect = GetResourceString(IDS_RESELECT_FIRST);
        int nItemsAdded = 0;
 
-       VERIFY(strCompare.LoadString(IDS_COMPARE));
-       VERIFY(strCompareEllipsis.LoadString(IDS_COMPARE_ELLIPSIS));
-       VERIFY(strCompareTo.LoadString(IDS_COMPARE_TO));
-       VERIFY(strReselect.LoadString(IDS_RESELECT_FIRST));
-
        switch (m_dwMenuState)
        {
-       // No items selected earlier
-       // Select item as first item to compare
+               // No items selected earlier
+               // Select item as first item to compare
        case MENU_ONESEL_NOPREV:
-               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strCompareTo);
+               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
+                               strCompareTo.c_str());
                uMenuIndex++;
                uidFirstCmd++;
-               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strCompareEllipsis);
+               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
+                               strCompareEllipsis.c_str());
                nItemsAdded = 2;
                break;
 
-       // One item selected earlier:
-       // Allow re-selecting first item or selecting second item
+               // One item selected earlier:
+               // Allow re-selecting first item or selecting second item
        case MENU_ONESEL_PREV:
-               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strCompare);
+               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
+                               strCompare.c_str());
                uMenuIndex++;
                uidFirstCmd++;
-               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strReselect);
+               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
+                               strReselect.c_str());
                nItemsAdded = 2;
                break;
 
-       // Two items selected
-       // Select both items for compare
+               // Two items selected
+               // Select both items for compare
        case MENU_TWOSEL:
-               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strCompare);
+       case MENU_THREESEL:
+               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
+                               strCompare.c_str());
                nItemsAdded = 1;
                break;
 
        default:
-               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strCompare);
+               InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
+                               strCompare.c_str());
                nItemsAdded = 1;
                break;
        }
-       
+
        // Add bitmap
-       if ((HBITMAP)m_MergeBmp != NULL)
+       HBITMAP hBitmap = PathIsDirectory(m_strPaths[0].c_str()) ? m_MergeDirBmp : m_MergeBmp;
+       if (hBitmap != NULL)
        {
                if (nItemsAdded == 2)
-                       SetMenuItemBitmaps(hmenu, uMenuIndex - 1, MF_BYPOSITION, m_MergeBmp, NULL);
-               SetMenuItemBitmaps(hmenu, uMenuIndex, MF_BYPOSITION, m_MergeBmp, NULL);
+                       SetMenuItemBitmaps(hmenu, uMenuIndex - 1, MF_BYPOSITION, hBitmap, NULL);
+               SetMenuItemBitmaps(hmenu, uMenuIndex, MF_BYPOSITION, hBitmap, NULL);
        }
-       
+
        // Show menu item as grayed if more than two items selected
        if (m_nSelectedItems > MaxFileCount)
        {
@@ -522,14 +613,14 @@ int CWinMergeShell::DrawAdvancedMenu(HMENU hmenu, UINT uMenuIndex,
 }
 
 /// Determine help text shown in explorer's statusbar
-CString CWinMergeShell::GetHelpText(UINT_PTR idCmd)
+String CWinMergeShell::GetHelpText(UINT_PTR idCmd)
 {
-       CString strHelp;
+       String strHelp;
 
        // More than two items selected, advice user
        if (m_nSelectedItems > MaxFileCount)
        {
-               VERIFY(strHelp.LoadString(IDS_CONTEXT_HELP_MANYITEMS));
+               strHelp = GetResourceString(IDS_CONTEXT_HELP_MANYITEMS);
                return strHelp;
        }
 
@@ -538,19 +629,21 @@ CString CWinMergeShell::GetHelpText(UINT_PTR idCmd)
                switch (m_dwMenuState)
                {
                case MENU_SIMPLE:
-                       VERIFY(strHelp.LoadString(IDS_CONTEXT_HELP));
+                       strHelp = GetResourceString(IDS_CONTEXT_HELP);;
                        break;
 
                case MENU_ONESEL_NOPREV:
-                       VERIFY(strHelp.LoadString(IDS_HELP_SAVETHIS));
+                       strHelp = GetResourceString(IDS_HELP_SAVETHIS);
                        break;
-               
+
                case MENU_ONESEL_PREV:
-                       AfxFormatString1(strHelp, IDS_HELP_COMPARESAVED, m_strPreviousPath);
+                       strHelp = GetResourceString(IDS_HELP_COMPARESAVED);
+                       string_replace(strHelp, _T("%1"), m_strPreviousPath);
                        break;
-               
+
                case MENU_TWOSEL:
-                       VERIFY(strHelp.LoadString(IDS_CONTEXT_HELP));
+               case MENU_THREESEL:
+                       strHelp = GetResourceString(IDS_CONTEXT_HELP);
                        break;
                }
        }
@@ -559,10 +652,10 @@ CString CWinMergeShell::GetHelpText(UINT_PTR idCmd)
                switch (m_dwMenuState)
                {
                case MENU_ONESEL_PREV:
-                       VERIFY(strHelp.LoadString(IDS_HELP_SAVETHIS));
+                       strHelp = GetResourceString(IDS_HELP_SAVETHIS);
                        break;
                default:
-                       VERIFY(strHelp.LoadString(IDS_CONTEXT_HELP));
+                       strHelp = GetResourceString(IDS_CONTEXT_HELP);
                        break;
                }
        }
@@ -570,24 +663,25 @@ CString CWinMergeShell::GetHelpText(UINT_PTR idCmd)
 }
 
 /// Format commandline used to start WinMerge
-CString CWinMergeShell::FormatCmdLine(const CString &winmergePath,
-       const CString &path1, const CString &path2, BOOL bAlterSubFolders)
+String CWinMergeShell::FormatCmdLine(const String &winmergePath,
+               const String &path1, const String &path2, BOOL bAlterSubFolders)
 {
-       CString strCommandline = winmergePath;
+       String strCommandline = _T("\"") + winmergePath + _T("\"");
 
        // Check if user wants to use context menu
        BOOL bSubfoldersByDefault = FALSE;
-       if (m_dwContextMenuEnabled & EXT_SUBFOLDERS) // User wants subfolders by def
-               bSubfoldersByDefault = TRUE;
+       CRegKeyEx reg;
+       if (reg.Open(HKEY_CURRENT_USER, f_RegSettingsDir) == ERROR_SUCCESS)
+               bSubfoldersByDefault = reg.ReadBool(f_Recurse, FALSE);
 
        if (bAlterSubFolders && !bSubfoldersByDefault)
                strCommandline += _T(" /r");
        else if (!bAlterSubFolders && bSubfoldersByDefault)
                strCommandline += _T(" /r");
-       
+
        strCommandline += _T(" \"") + path1 + _T("\"");
-       
-       if (!m_strPaths[1].IsEmpty())
+
+       if (!m_strPaths[1].empty())
                strCommandline += _T(" \"") + path2 + _T("\"");
 
        return strCommandline;