// 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
/////////////////////////////////////////////////////////////////////////////
// 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>
-/**
+OBJECT_ENTRY_AUTO(CLSID_WinMergeShell, CWinMergeShell)
+
+/**
* @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? */
+};
+
+enum
+{
+ CMD_COMPARE = 0,
+ CMD_COMPARE_ELLIPSE,
+ CMD_SELECT_LEFT,
+ CMD_SELECT_MIDDLE,
+ CMD_RESELECT_LEFT,
+ CMD_LAST = CMD_RESELECT_LEFT,
};
/// 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 */
static const TCHAR f_FirstSelection[] = _T("FirstSelection");
-/** Path to WinMerge[U].exe */
+/** 'Saved' path in advanced mode */
+static const TCHAR f_SecondSelection[] = _T("SecondSelection");
+/** Path to WinMergeU.exe */
static const TCHAR f_RegValuePath[] = _T("Executable");
-/** Path to WinMerge[U].exe, overwrites f_RegValuePath if present. */
+/** Path to WinMergeU.exe, overwrites f_RegValuePath if present. */
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_ONESEL_TWO_PREV, /**< One item selected, two previous selections exist. */
+ MENU_TWOSEL, /**< Two items are selected. */
+ MENU_THREESEL
};
-#define USES_WINMERGELOCALE CWinMergeTempLocale __wmtl__
+// GreyMerlin (03 Sept 2017) - The following Version Info checking code is a
+// short extract from the Microsoft <versionhelpers.h> file. Unfortunatly,
+// that file is not available for WinXP-compatible Platform Toolsets (e.g.
+// v141_xp for VS2017). Fortunatly, all the actual API interfaces do exist
+// in WinXP (actually, in all Windows products since Win2000). Use of this
+// <versionhelpers.h> code avoids the unpleasant deprecation of the GetVersionEx()
+// API begining with Win 8.1. This Version Info checking code is also fully
+// compatible with all non-XP-compatible Toolsets as well (e.g. v141).
+
+#ifndef _WIN32_WINNT_VISTA
+#define _WIN32_WINNT_VISTA 0x0600
+#endif
+#ifndef _WIN32_WINNT_WIN8
+#define _WIN32_WINNT_WIN8 0x0602
+#endif
+
+#ifndef VERSIONHELPERAPI
+#define VERSIONHELPERAPI inline bool
+
+VERSIONHELPERAPI
+IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
+{
+ OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
+ DWORDLONG const dwlConditionMask = VerSetConditionMask(
+ VerSetConditionMask(
+ VerSetConditionMask(
+ 0, VER_MAJORVERSION, VER_GREATER_EQUAL),
+ VER_MINORVERSION, VER_GREATER_EQUAL),
+ VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+
+ osvi.dwMajorVersion = wMajorVersion;
+ osvi.dwMinorVersion = wMinorVersion;
+ osvi.wServicePackMajor = wServicePackMajor;
+
+ return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
+}
-static String GetResourceString(UINT resourceID);
-class CWinMergeTempLocale
+VERSIONHELPERAPI
+IsWindows8OrGreater()
{
-private:
- LCID m_lcidOld;
-public:
- CWinMergeTempLocale() {
- CRegKeyEx reg;
- if (reg.Open(HKEY_CURRENT_USER, f_RegLocaleDir) != ERROR_SUCCESS)
- return;
-
- m_lcidOld = GetThreadLocale();
-
- int iLangId = reg.ReadDword(f_LanguageId, (DWORD)-1);
- if (iLangId != -1)
- SetThreadLocale(MAKELCID(iLangId, SORT_DEFAULT));
- }
- ~CWinMergeTempLocale() {
- SetThreadLocale(m_lcidOld);
- }
-};
+ return IsWindowsVersionOrGreater(HIBYTE(_WIN32_WINNT_WIN8), LOBYTE(_WIN32_WINNT_WIN8), 0);
+}
+#endif // VERSIONHELPERAPI
-/**
- * @brief Load a string from resource.
- * @param [in] Resource string ID.
- * @return String loaded from resource.
- */
-static String GetResourceString(UINT resourceID)
+HBITMAP ConvertHICONtoHBITMAP(HICON hIcon, int cx, int cy)
{
- TCHAR resStr[1024] = {0};
- int res = LoadString(_Module.GetModuleInstance(), resourceID, resStr, 1024);
- ATLASSERT(res!= 0);
- String strResource = resStr;
- return strResource;
+ LPVOID lpBits;
+ BITMAPINFO bmi = { { sizeof(BITMAPINFOHEADER), cx, cy, 1, IsWindows8OrGreater() ? 32u : 24u } };
+ HDC hdcMem = CreateCompatibleDC(NULL);
+ HBITMAP hbmp = CreateDIBSection(NULL, (BITMAPINFO*)&bmi, DIB_RGB_COLORS, &lpBits, NULL, 0);
+ if (hbmp)
+ {
+ HBITMAP hbmpPrev = (HBITMAP)SelectObject(hdcMem, hbmp);
+ RECT rc = { 0, 0, cx, cy };
+ if (bmi.bmiHeader.biBitCount <= 24)
+ {
+ SetBkColor(hdcMem, GetSysColor(COLOR_MENU));
+ ExtTextOut(hdcMem, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+ }
+ DrawIconEx(hdcMem, 0, 0, hIcon, cx, cy, 0, NULL, DI_NORMAL);
+ SelectObject(hdcMem, hbmpPrev);
+ }
+ DeleteDC(hdcMem);
+ return hbmp;
}
/////////////////////////////////////////////////////////////////////////////
/// Default constructor, loads icon bitmap
CWinMergeShell::CWinMergeShell()
+ : m_dwContextMenuEnabled(false)
+ , m_nSelectedItems(0)
+ , m_dwMenuState(0)
+ , m_langID(GetUserDefaultUILanguage())
{
- m_dwMenuState = 0;
+ int cx = GetSystemMetrics(SM_CXMENUCHECK);
+ int cy = GetSystemMetrics(SM_CYMENUCHECK);
// compress or stretch icon bitmap according to menu item height
- m_MergeBmp = (HBITMAP)LoadImage(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDB_WINMERGE), IMAGE_BITMAP,
- GetSystemMetrics(SM_CXMENUCHECK), GetSystemMetrics(SM_CYMENUCHECK), LR_DEFAULTCOLOR);
+ HICON hMergeIcon = (HICON)LoadImage(_AtlComModule.m_hInstTypeLib, MAKEINTRESOURCE(IDI_WINMERGE), IMAGE_ICON,
+ cx, cy, LR_DEFAULTCOLOR);
+ HICON hMergeDirIcon = (HICON)LoadImage(_AtlComModule.m_hInstTypeLib, MAKEINTRESOURCE(IDI_WINMERGEDIR), IMAGE_ICON,
+ cx, cy, LR_DEFAULTCOLOR);
+
+ m_MergeBmp = ConvertHICONtoHBITMAP(hMergeIcon, cx, cy);
+ m_MergeDirBmp = ConvertHICONtoHBITMAP(hMergeDirIcon, cx, cy);
+
+ DestroyIcon(hMergeIcon);
+ DestroyIcon(hMergeDirIcon);
+
+ CRegKeyEx reg;
+ if (reg.Open(HKEY_CURRENT_USER, f_RegLocaleDir) == ERROR_SUCCESS)
+ m_langID = static_cast<LANGID>(reg.ReadDword(f_LanguageId, m_langID));
+
}
/// Default destructor, unloads bitmap
CWinMergeShell::~CWinMergeShell()
{
+ DeleteObject(m_MergeDirBmp);
DeleteObject(m_MergeBmp);
}
HRESULT CWinMergeShell::Initialize(LPCITEMIDLIST pidlFolder,
LPDATAOBJECT pDataObj, HKEY hProgID)
{
- USES_WINMERGELOCALE;
HRESULT hr = E_INVALIDARG;
+ for (auto& path: m_strPaths)
+ path.erase();
+
// Files/folders selected normally from the explorer
if (pDataObj)
{
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)
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;
}
HRESULT CWinMergeShell::QueryContextMenu(HMENU hmenu, UINT uMenuIndex,
UINT uidFirstCmd, UINT uidLastCmd, UINT uFlags)
{
- int nItemsAdded = 0;
- USES_WINMERGELOCALE;
+ // check whether menu items are already added
+ if (hmenu == s_hMenuLastAdded)
+ {
+ MENUITEMINFO mii{ sizeof mii };
+ mii.fMask = MIIM_DATA;
+ if (GetMenuItemInfo(hmenu, s_uidCmdLastAdded, FALSE, &mii))
+ {
+ if (mii.dwItemData >= IDS_COMPARE && mii.dwItemData <= IDS_RESELECT_LEFT)
+ return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
+ }
+ }
+
+ s_hMenuLastAdded = hmenu;
+ s_uidCmdLastAdded = uidFirstCmd;
+
+ int uidUserLastCmd = 0;
// If the flags include CMF_DEFAULTONLY then we shouldn't do anything.
if (uFlags & CMF_DEFAULTONLY)
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
m_dwContextMenuEnabled = reg.ReadDword(f_RegValueEnabled, 0);
- m_strPreviousPath = reg.ReadString(f_FirstSelection, _T("")).c_str();
+ m_strPreviousPath[0] = reg.ReadString(f_FirstSelection, _T("")).c_str();
+ m_strPreviousPath[1] = reg.ReadString(f_SecondSelection, _T("")).c_str();
if (m_dwContextMenuEnabled & EXT_ENABLED) // Context menu enabled
{
if ((m_dwContextMenuEnabled & EXT_ADVANCED) == 0)
{
m_dwMenuState = MENU_SIMPLE;
- nItemsAdded = DrawSimpleMenu(hmenu, uMenuIndex, uidFirstCmd);
+ uidUserLastCmd = DrawSimpleMenu(hmenu, uMenuIndex, uidFirstCmd);
}
else
{
- if (m_nSelectedItems == 1 && m_strPreviousPath.empty())
+ if (m_nSelectedItems == 1 && m_strPreviousPath[0].empty())
m_dwMenuState = MENU_ONESEL_NOPREV;
- else if (m_nSelectedItems == 1 && !m_strPreviousPath.empty())
+ else if (m_nSelectedItems == 1 && !m_strPreviousPath[0].empty() && m_strPreviousPath[1].empty())
m_dwMenuState = MENU_ONESEL_PREV;
+ else if (m_nSelectedItems == 1 && !m_strPreviousPath[0].empty() && !m_strPreviousPath[1].empty())
+ m_dwMenuState = MENU_ONESEL_TWO_PREV;
else if (m_nSelectedItems == 2)
m_dwMenuState = MENU_TWOSEL;
+ else if (m_nSelectedItems == 3)
+ m_dwMenuState = MENU_THREESEL;
- nItemsAdded = DrawAdvancedMenu(hmenu, uMenuIndex, uidFirstCmd);
+ uidUserLastCmd = DrawAdvancedMenu(hmenu, uMenuIndex, uidFirstCmd);
}
- return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, nItemsAdded);
+ return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, (uidUserLastCmd - uidFirstCmd) + 1);
}
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
}
UINT* pwReserved, LPSTR pszName, UINT cchMax)
{
USES_CONVERSION;
- USES_WINMERGELOCALE;
// Check idCmd, it must be 0 in simple mode and 0 or 1 in advanced mode.
if ((m_dwMenuState & EXT_ADVANCED) == 0)
}
else
{
- if (idCmd > 1)
+ if (idCmd > 2)
return E_INVALIDARG;
}
// supplied buffer.
if (uFlags & GCS_HELPTEXT)
{
- String strHelp;
-
- strHelp = GetHelpText(idCmd);
+ String strHelp = GetHelpText(idCmd);
if (uFlags & GCS_UNICODE)
// We need to cast pszName to a Unicode string, and then use the
String strWinMergePath;
BOOL bCompare = FALSE;
BOOL bAlterSubFolders = FALSE;
- USES_WINMERGELOCALE;
// If lpVerb really points to a string, ignore this function call and bail out.
if (HIWORD(pCmdInfo->lpVerb) != 0)
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)
+ if (LOWORD(pCmdInfo->lpVerb) == CMD_COMPARE)
{
switch (m_dwMenuState)
{
bCompare = TRUE;
break;
- case MENU_ONESEL_NOPREV:
- m_strPreviousPath = m_strPaths[0];
+ case MENU_ONESEL_PREV:
+ m_strPaths[1] = m_strPaths[0];
+ m_strPaths[0] = m_strPreviousPath[0];
+ bCompare = TRUE;
+
+ // Forget previous selection
if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
- reg.WriteString(f_FirstSelection, m_strPreviousPath.c_str());
+ {
+ reg.WriteString(f_FirstSelection, _T(""));
+ reg.WriteString(f_SecondSelection, _T(""));
+ }
break;
- case MENU_ONESEL_PREV:
- m_strPaths[1] = m_strPaths[0];
- m_strPaths[0] = m_strPreviousPath;
+ case MENU_ONESEL_TWO_PREV:
+ m_strPaths[2] = m_strPaths[0];
+ m_strPaths[0] = m_strPreviousPath[0];
+ m_strPaths[1] = m_strPreviousPath[1];
bCompare = TRUE;
-
+
// Forget previous selection
if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
+ {
reg.WriteString(f_FirstSelection, _T(""));
+ reg.WriteString(f_SecondSelection, _T(""));
+ }
break;
case MENU_TWOSEL:
+ case MENU_THREESEL:
// "Compare" - compare paths
bCompare = TRUE;
- m_strPreviousPath.erase();
+ m_strPreviousPath[0].erase();
+ m_strPreviousPath[1].erase();
break;
}
}
- else if (LOWORD(pCmdInfo->lpVerb) == 1)
+ else if (LOWORD(pCmdInfo->lpVerb) == CMD_COMPARE_ELLIPSE)
{
- switch (m_dwMenuState)
+ // "Compare..." - user wants to compare this single item and open WinMerge
+ m_strPaths[1].erase();
+ m_strPaths[2].erase();
+ bCompare = TRUE;
+ }
+ else if (LOWORD(pCmdInfo->lpVerb) == CMD_SELECT_LEFT)
+ {
+ // Select Left
+ m_strPreviousPath[0] = m_strPaths[0];
+ if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
+ reg.WriteString(f_FirstSelection, m_strPreviousPath[0].c_str());
+ }
+ else if (LOWORD(pCmdInfo->lpVerb) == CMD_SELECT_MIDDLE)
+ {
+ // Select Middle
+ m_strPreviousPath[1] = m_strPaths[0];
+ if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
+ reg.WriteString(f_SecondSelection, m_strPreviousPath[1].c_str());
+ }
+ else if (LOWORD(pCmdInfo->lpVerb) == CMD_RESELECT_LEFT)
+ {
+ // Re-select Left
+ m_strPreviousPath[0] = m_strPaths[0];
+ m_strPreviousPath[1].clear();
+ if (reg.Open(HKEY_CURRENT_USER, f_RegDir) == ERROR_SUCCESS)
{
- 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.c_str());
- bCompare = FALSE;
- break;
- default:
- // "Compare..." - user wants to compare this single item and open WinMerge
- m_strPaths[1].erase();
- bCompare = TRUE;
- break;
+ reg.WriteString(f_FirstSelection, m_strPreviousPath[0].c_str());
+ reg.WriteString(f_SecondSelection, _T(""));
}
}
else
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0)
bAlterSubFolders = TRUE;
- String strCommandLine = FormatCmdLine(strWinMergePath, m_strPaths[0],
- m_strPaths[1], bAlterSubFolders);
+ String strCommandLine = FormatCmdLine(strWinMergePath,
+ m_strPaths[0], m_strPaths[1], m_strPaths[2], bAlterSubFolders);
// Finally start a new WinMerge process
BOOL retVal = FALSE;
STARTUPINFO stInfo = {0};
stInfo.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION processInfo = {0};
-
+
retVal = CreateProcess(NULL, (LPTSTR)strCommandLine.c_str(),
- NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
- &stInfo, &processInfo);
+ NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE, NULL, NULL,
+ &stInfo, &processInfo);
- if (!retVal)
+ if (retVal)
+ {
+ CloseHandle(processInfo.hThread);
+ CloseHandle(processInfo.hProcess);
+ }
+ else if (GetLastError() == ERROR_ELEVATION_REQUIRED)
+ {
+ String strCommandLine = FormatCmdLine(_T(""),
+ m_strPaths[0], m_strPaths[1], m_strPaths[2], bAlterSubFolders);
+ HINSTANCE hInstance = ShellExecute(nullptr, _T("runas"), strWinMergePath.c_str(), strCommandLine.c_str(), 0, SW_SHOWNORMAL);
+ if (reinterpret_cast<intptr_t>(hInstance) < 32)
+ return S_FALSE;
+ }
+ else
+ {
return S_FALSE;
+ }
- CloseHandle(processInfo.hThread);
- CloseHandle(processInfo.hProcess);
return S_OK;
}
+/**
+ * @brief Load a string from resource.
+ * @param [in] Resource string ID.
+ * @return String loaded from resource.
+ */
+String CWinMergeShell::GetResourceString(UINT resourceID)
+{
+ if (!s_pLang)
+ s_pLang = new CLanguageSelect();
+ if (s_pLang->GetLangId() != m_langID)
+ {
+ TCHAR szFileName[1024] = {0};
+ GetModuleFileName(_AtlComModule.m_hInstTypeLib, szFileName, sizeof(szFileName) / sizeof(TCHAR));
+ PathRemoveFileSpec(szFileName);
+ String languagesFolder = String(szFileName) + _T("\\Languages\\ShellExtension");
+ s_pLang->LoadLanguageFile(m_langID, languagesFolder);
+ }
+ TCHAR resStr[1024] = {0};
+ int res = LoadString(_AtlComModule.m_hInstTypeLib, resourceID, resStr, 1024);
+ ATLASSERT(res != 0);
+ String strResource;
+ s_pLang->TranslateString(resStr, strResource);
+ return strResource;
+}
+
+BOOL CWinMergeShell::InsertMenuString(HMENU hMenu, UINT uPosition, UINT uIDNewItem, UINT uStringId)
+{
+ String str = GetResourceString(uStringId);
+ MENUITEMINFO mii{sizeof mii};
+ mii.fMask = MIIM_ID | MIIM_STRING | MIIM_DATA;
+ mii.wID = uIDNewItem;
+ mii.dwTypeData = const_cast<LPTSTR>(str.c_str());
+ mii.dwItemData = uStringId;
+ return InsertMenuItem(hMenu, uPosition, TRUE, &mii);
+}
+
/// Reads WinMerge path from registry
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(""));
if (strDir.empty())
strDir = reg.ReadString(f_RegValuePath, _T(""));
if (strDir.empty())
return FALSE;
- }
+ }
return TRUE;
}
-/// Checks if given file exists and is executable
-BOOL CWinMergeShell::CheckExecutable(String path)
-{
- String sExt;
- SplitFilename(path.c_str(), NULL, NULL, &sExt);
-
- // Check extension
- if (_tcsicmp(sExt.c_str(), _T("exe")) == 0 ||
- _tcsicmp(sExt.c_str(), _T("cmd")) == 0 ||
- _tcsicmp(sExt.c_str(), _T("bat")) == 0)
- {
- // Check if file exists
- struct _stati64 statBuffer;
- int nRetVal = _tstati64(path.c_str(), &statBuffer);
- if (nRetVal > -1)
- return TRUE;
- }
- return FALSE;
-}
-
/// Create menu for simple mode
int CWinMergeShell::DrawSimpleMenu(HMENU hmenu, UINT uMenuIndex,
UINT uidFirstCmd)
{
- String strMenu = GetResourceString(IDS_CONTEXT_MENU);
- InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd, strMenu.c_str());
-
+ InsertMenuString(hmenu, uMenuIndex, uidFirstCmd, IDS_CONTEXT_MENU);
+
// Add bitmap
- if (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;
+
+ return uidFirstCmd + CMD_LAST;
}
/// Create menu for advanced mode
int CWinMergeShell::DrawAdvancedMenu(HMENU hmenu, UINT uMenuIndex,
UINT uidFirstCmd)
{
- 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;
-
+
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.c_str());
- uMenuIndex++;
- uidFirstCmd++;
- InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
- strCompareEllipsis.c_str());
+ InsertMenuString(hmenu, uMenuIndex++, uidFirstCmd + CMD_SELECT_LEFT, IDS_SELECT_LEFT);
+ InsertMenuString(hmenu, uMenuIndex, uidFirstCmd + CMD_COMPARE_ELLIPSE, IDS_COMPARE_ELLIPSIS);
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.c_str());
- uMenuIndex++;
- uidFirstCmd++;
- InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
- strReselect.c_str());
+ InsertMenuString(hmenu, uMenuIndex++, uidFirstCmd + CMD_COMPARE, IDS_COMPARE);
+ InsertMenuString(hmenu, uMenuIndex++, uidFirstCmd + CMD_SELECT_MIDDLE, IDS_SELECT_MIDDLE);
+ InsertMenuString(hmenu, uMenuIndex, uidFirstCmd + CMD_RESELECT_LEFT, IDS_RESELECT_LEFT);
+ nItemsAdded = 3;
+ break;
+
+ // Two items are selected earlier:
+ // Allow re-selecting first item or selecting second item
+ case MENU_ONESEL_TWO_PREV:
+ InsertMenuString(hmenu, uMenuIndex++, uidFirstCmd + CMD_COMPARE, IDS_COMPARE);
+ InsertMenuString(hmenu, uMenuIndex, uidFirstCmd + CMD_RESELECT_LEFT, IDS_RESELECT_LEFT);
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.c_str());
+ case MENU_THREESEL:
+ InsertMenuString(hmenu, uMenuIndex, uidFirstCmd + CMD_COMPARE, IDS_COMPARE);
nItemsAdded = 1;
break;
default:
- InsertMenu(hmenu, uMenuIndex, MF_BYPOSITION, uidFirstCmd,
- strCompare.c_str());
+ InsertMenuString(hmenu, uMenuIndex, uidFirstCmd + CMD_COMPARE, IDS_COMPARE);
nItemsAdded = 1;
break;
}
-
+
// Add bitmap
- if (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);
+ for (int i = 0; i < nItemsAdded; i++)
+ SetMenuItemBitmaps(hmenu, uMenuIndex - (nItemsAdded - 1 - i), MF_BYPOSITION, hBitmap, NULL);
}
-
+
// Show menu item as grayed if more than two items selected
if (m_nSelectedItems > MaxFileCount)
{
- if (nItemsAdded == 2)
- EnableMenuItem(hmenu, uMenuIndex - 1, MF_BYPOSITION | MF_GRAYED);
- EnableMenuItem(hmenu, uMenuIndex, MF_BYPOSITION | MF_GRAYED);
+ for (int i = 0; i < nItemsAdded; i++)
+ EnableMenuItem(hmenu, uMenuIndex - (nItemsAdded - 1 - i), MF_BYPOSITION | MF_GRAYED);
}
- return nItemsAdded;
+ return uidFirstCmd + CMD_LAST;
}
/// Determine help text shown in explorer's statusbar
return strHelp;
}
- if (idCmd == 0)
+ if (idCmd == CMD_COMPARE)
{
switch (m_dwMenuState)
{
- case MENU_SIMPLE:
- strHelp = GetResourceString(IDS_CONTEXT_HELP);;
+ case MENU_ONESEL_PREV:
+ strHelp = GetResourceString(IDS_HELP_COMPARESAVED);
+ strutils::replace(strHelp, _T("%1"), m_strPreviousPath[0]);
break;
- case MENU_ONESEL_NOPREV:
- strHelp = GetResourceString(IDS_HELP_SAVETHIS);
- break;
-
- case MENU_ONESEL_PREV:
+ case MENU_ONESEL_TWO_PREV:
strHelp = GetResourceString(IDS_HELP_COMPARESAVED);
- string_replace(strHelp, _T("%1"), m_strPreviousPath);
+ strutils::replace(strHelp, _T("%1"), m_strPreviousPath[0] + _T(" - ") + m_strPreviousPath[1]);
break;
-
- case MENU_TWOSEL:
+ default:
strHelp = GetResourceString(IDS_CONTEXT_HELP);
break;
}
}
- else if (idCmd == 1)
+ else if (idCmd == CMD_COMPARE_ELLIPSE)
{
- switch (m_dwMenuState)
- {
- case MENU_ONESEL_PREV:
- strHelp = GetResourceString(IDS_HELP_SAVETHIS);
- break;
- default:
- strHelp = GetResourceString(IDS_CONTEXT_HELP);
- break;
- }
+ strHelp = GetResourceString(IDS_CONTEXT_HELP);
+ }
+ else if (idCmd == CMD_SELECT_LEFT)
+ {
+ strHelp = GetResourceString(IDS_HELP_SAVETHIS);
+ }
+ else if (idCmd == CMD_SELECT_MIDDLE)
+ {
+ strHelp = GetResourceString(IDS_HELP_SAVETHIS);
+ }
+ else if (idCmd == CMD_RESELECT_LEFT)
+ {
+ strHelp = GetResourceString(IDS_HELP_SAVETHIS);
}
return strHelp;
}
/// Format commandline used to start WinMerge
String CWinMergeShell::FormatCmdLine(const String &winmergePath,
- const String &path1, const String &path2, BOOL bAlterSubFolders)
+ const String &path1, const String &path2, const String &path3, BOOL bAlterSubFolders)
{
- String strCommandline(winmergePath);
+ String strCommandline = winmergePath.empty() ? _T("") : _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].empty())
strCommandline += _T(" \"") + path2 + _T("\"");
+ if (!m_strPaths[2].empty())
+ strCommandline += _T(" \"") + path3 + _T("\"");
+
return strCommandline;
}