From: sdottaka Date: Tue, 17 Feb 2015 06:29:04 +0000 (+0900) Subject: Merge with stable X-Git-Tag: 2.16.5~1472 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=abfa570d4a008e77be4a18a6d8c2cdf10c2c9b97;p=winmerge-jp%2Fwinmerge-jp.git Merge with stable --- abfa570d4a008e77be4a18a6d8c2cdf10c2c9b97 diff --cc Src/7zCommon.cpp index 2ca35cb3f,a03e24868..b1c8d0eed --- a/Src/7zCommon.cpp +++ b/Src/7zCommon.cpp @@@ -1,1184 -1,1249 +1,1184 @@@ -///////////////////////////////////////////////////////////////////////////// -// WinMerge: an interactive diff/merge utility -// Copyright (C) 1997-2000 Thingamahoochie Software -// Author: Dean Grimm -// -// 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, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -// -///////////////////////////////////////////////////////////////////////////// - -/* 7zCommon.cpp: Implement 7z related classes and functions - * Copyright (c) 2003 Jochen Tucht - * - * Remarks: Different versions of 7-Zip are interfaced through specific - * versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.) - * WinMerge can either use an installed copy of the 7-Zip software, - * or fallback to a local set of 7-Zip DLLs, which are to be included - * in the WinMerge binary distribution. - * - * Fallback policies are as follows: - * - * 1. Detect 7-Zip version installed (XXX). - * 2. If there is a Merge7zXXX.dll, be happy to use it - * 3. Detect 7-Zip version from WinMerge distribution (YYY). - * 4. If there is a Merge7zYYY.dll, be happy to use it. - * 5. Sorry, no way. - * - * These rules can be customized by setting a registry variable - * *Merge7z/Enable* of type DWORD to one of the following values: - * - * 0 - Entirely disable 7-Zip integration. - * 1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip. - * 2 - Always use local 7-Zip. - * - -Please mind 2. a) of the GNU General Public License, and log your changes below. - -DATE: BY: DESCRIPTION: -========== ================== ================================================ -2003-12-09 Jochen Tucht Created -2003-12-16 Jochen Tucht Properly generate .tar.gz and .tar.bz2 -2003-12-16 Jochen Tucht Obtain long path to temporary folder -2004-01-20 Jochen Tucht Complain only once if Merge7z*.dll is missing -2004-01-25 Jochen Tucht Fix bad default for OPENFILENAME::nFilterIndex -2004-03-15 Jochen Tucht Fix Visual Studio 2003 build issue -2004-04-13 Jochen Tucht Avoid StrNCat to get away with shlwapi 4.70 -2004-08-25 Jochen Tucht More explicit error message -2004-10-17 Jochen Tucht Leave decision whether to recurse into folders - to enumerator (Mask.Recurse) -2004-11-03 Jochen Tucht FIX [1048997] as proposed by Kimmo 2004-11-02 -2005-01-15 Jochen Tucht Read 7-Zip version from 7zip_pad.xml - Set Merge7z UI language if DllBuild_Merge7z >= 9 -2005-01-22 Jochen Tucht Better explain what's present/missing/outdated -2005-02-05 Jochen Tucht Fall back to IDD_MERGE7ZMISMATCH template from - .exe if .lang file isn't up to date. -2005-02-26 Jochen Tucht Add download link to error message -2005-02-26 Jochen Tucht Use WinAPI to obtain ISO language/region codes -2005-02-27 Jochen Tucht FIX [1152375] -2005-04-24 Kimmo Varis Don't use DiffContext exported from DirView -2005-06-08 Kimmo Varis Use DIFFITEM, not reference to it (hopefully only - temporarily, to sort out new directory compare) -2005-06-22 Jochen Tucht Change recommended version of 7-Zip to 4.20 - Remove noise from Nagbox -2005-07-03 Jochen Tucht DIFFITEM has changed due to RFE [ 1205516 ] -2005-07-04 Jochen Tucht New global ArchiveGuessFormat() checks for - formats to be handled by external command line - tools. These take precedence over Merge7z - internal handlers. -2005-07-05 Jochen Tucht Move to Merge7z::Format::GetDefaultName() to - build intermediate filenames for multi-step - compression. -2005-07-15 Jochen Tucht Remove external command line tool integration - for now. Rethink about it after 2.4 branch. -2005-08-20 Jochen Tucht Option to guess archive format by signature - Map extensions through ExternalArchiveFormat.ini -2005-08-23 Jochen Tucht Option to entirely disable 7-Zip integration -2007-01-04 Kimmo Varis Convert using COptionsMgr for options. -2007-06-16 Jochen Neubeck FIX [1723263] "Zip --> Both" operation... -2007-12-22 Jochen Neubeck Fix Merge7z UI lang for new translation system - Change recommended version of 7-Zip to 4.57 -2010-05-16 Jochen Neubeck Read 7-Zip version from 7z.dll (which has long - ago replaced the various format and codec DLLs) - Change recommended version of 7-Zip to 4.65 -*/ - -// ID line follows -- this is updated by SVN -// $Id: 7zCommon.cpp 7169 2010-05-16 14:44:19Z jtuc $ - -#include "stdafx.h" -#include "7zCommon.h" -#include -#include -#include -#include "OptionsDef.h" -#include "OptionsMgr.h" -#include "Merge.h" // DirDocFilter theApp GetOptionsMgr() -#include "resource.h" -#include "DirDoc.h" -//#include "ExternalArchiveFormat.h" -#include "version.h" -#include "Environment.h" -#include "Merge7zFormatRegister.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -/** - * @brief Proxy for Merge7z - */ -static __declspec(thread) Merge7z::Proxy m_Merge7z = -{ - { 0, 0, DllBuild_Merge7z, }, - "Merge7z%u%02u"DECORATE_U".dll", - "Merge7z", - NULL -}; - -std::vector Merge7zFormatRegister::optionalFormats; - -bool IsArchiveFile(const String& pszFile) -{ - try { - Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile); - if (piHandler) - return TRUE; - else - return FALSE; - } - catch (CException *e) - { - e->Delete(); - return FALSE; - } - return FALSE; -} - -/** - * @brief Wrap Merge7z::GuessFormat() to allow for some customizing: - * - Check if 7-Zip integration is enabled. - * - Check for filename extension mappings. - */ -Merge7z::Format *ArchiveGuessFormat(const String& path) -{ - if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0) - return NULL; - if (paths_IsDirectory(path)) - return NULL; - String path2(path); - // Map extensions through ExternalArchiveFormat.ini - static TCHAR null[] = _T(""); - static const TCHAR section[] = _T("extensions"); - String entry = paths_FindExtension(path); - TCHAR value[20]; - static LPCTSTR filename = NULL; - if (filename == NULL) - { - TCHAR cPath[INTERNET_MAX_PATH_LENGTH]; - DWORD cchPath = SearchPath(NULL, _T("ExternalArchiveFormat.ini"), NULL, - INTERNET_MAX_PATH_LENGTH, cPath, NULL); - filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? StrDup(cPath) : null; - } - if (*filename && - GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) && - *value == '.') - { - // Remove end-of-line comments (in string returned from GetPrivateProfileString) - // that is, remove semicolon & whatever follows it - if (LPTSTR p = StrChr(value, ';')) - { - *p = '\0'; - StrTrim(value, _T(" \t")); - } - path2 = value; - } - - // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues - // command line integration portion is not yet applied - // so following code not yet valid, so temporarily commented out - // Look for command line tool first - /*Merge7z::Format *pFormat; - if (CExternalArchiveFormat::GuessFormat(path, pFormat)) - { - return pFormat; - }*/ - // Default to Merge7z*.dll - - try - { - Merge7z::Format *pFormat = m_Merge7z->GuessFormat(path2.c_str()); - if (!pFormat) - pFormat = Merge7zFormatRegister::GuessFormat(path2); - return pFormat; - } - catch (...) - { - Merge7z::Format *pFormat = Merge7zFormatRegister::GuessFormat(path2); - if (pFormat) - return pFormat; - throw; - } -} - -/** - * @brief Self-initializing raw C character buffer class. - */ -template struct CRawString -{ - enum { Size = SIZE }; - TYPE Data[SIZE]; - CRawString() - { - Data[0] = 0; - } -}; - -/** - * @brief Exception class for more explicit error message. - */ -class C7ZipMismatchException : public CException -{ -public: - C7ZipMismatchException(DWORD dwVer7zInstalled, DWORD dwVer7zLocal, CException *pCause) - { - m_dwVer7zInstalled = dwVer7zInstalled; - m_dwVer7zLocal = dwVer7zLocal; - m_pCause = pCause; - } - ~C7ZipMismatchException() - { - if (m_pCause) - m_pCause->Delete(); - } - virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0); -protected: - DWORD m_dwVer7zInstalled; - DWORD m_dwVer7zLocal; - CException *m_pCause; - BOOL m_bShowAllways; - static const DWORD m_dwVer7zRecommended; - static const TCHAR m_strRegistryKey[]; - static const TCHAR m_strDownloadURL[]; - static INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM); - static DWORD FormatVersion(LPTSTR, LPTSTR, DWORD); -}; - -/** - * @brief Recommended version of 7-Zip. - */ -const DWORD C7ZipMismatchException::m_dwVer7zRecommended = DWORD MAKELONG(65,4); - -/** - * @brief Registry key for C7ZipMismatchException's ReportError() popup. - */ -const TCHAR C7ZipMismatchException::m_strRegistryKey[] = _T("7ZipMismatch"); - -/** - * @brief Download URL for C7ZipMismatchException's ReportError() popup. - */ -const TCHAR C7ZipMismatchException::m_strDownloadURL[] = _T("https://sourceforge.net/project/showfiles.php?group_id=13216&package_id=143957"); - -/** - * @brief Retrieve build number of given DLL. - */ -static DWORD NTAPI GetDllBuild(LPCTSTR cPath) -{ - HMODULE hModule = LoadLibrary(cPath); - DLLVERSIONINFO dvi; - dvi.cbSize = sizeof dvi; - dvi.dwBuildNumber = ~0UL; - if (hModule) - { - dvi.dwBuildNumber = 0UL; - DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hModule, "DllGetVersion"); - if (DllGetVersion) - { - DllGetVersion(&dvi); - } - FreeLibrary(hModule); - } - return dvi.dwBuildNumber; -} - -/** - * @brief Format Merge7z version number and plugin name, and retrieve DllBuild. - */ -DWORD C7ZipMismatchException::FormatVersion(LPTSTR pcVersion, LPTSTR pcPluginName, DWORD dwVersion) -{ - *pcVersion = '\0'; - *pcPluginName = '\0'; - if (dwVersion) - { - wsprintf - ( - pcVersion, _T("%u.%02u"), - UINT HIWORD(dwVersion), - UINT LOWORD(dwVersion) - ); - wsprintf - ( - pcPluginName, - sizeof(TCHAR) == 1 ? _T("Merge7z%u%02u.dll") : _T("Merge7z%u%02uU.dll"), - UINT HIWORD(dwVersion), - UINT LOWORD(dwVersion) - ); - } - return GetDllBuild(pcPluginName); -} - -/** - * @brief Populate ListBox with names/revisions of DLLs matching given pattern. - */ -static void NTAPI DlgDirListDLLs(HWND hWnd, LPTSTR cPattern, int nIDListBox) -{ - HDC hDC = GetDC(hWnd); - HFONT hFont = (HFONT)SendDlgItemMessage(hWnd, nIDListBox, WM_GETFONT, 0, 0); - int cxView = (int)SendDlgItemMessage(hWnd, nIDListBox, LB_GETHORIZONTALEXTENT, 0, 0) - 8; - HGDIOBJ hObject = SelectObject(hDC, hFont); - WIN32_FIND_DATA ff; - HANDLE h = FindFirstFile(cPattern, &ff); - if (h != INVALID_HANDLE_VALUE) - { - do - { - PathRemoveFileSpec(cPattern); - PathAppend(cPattern, ff.cFileName); - wsprintf(ff.cFileName, _T(" (dllbuild %04u)"), GetDllBuild(cPattern)); - lstrcat(cPattern, ff.cFileName); - int cxText = (int)(WORD)GetTabbedTextExtent(hDC, cPattern, lstrlen(cPattern), 0, 0); - if (cxView < cxText) - cxView = cxText; - SendDlgItemMessage(hWnd, nIDListBox, LB_ADDSTRING, 0, (LPARAM)cPattern); - } while (FindNextFile(h, &ff)); - FindClose(h); - } - SelectObject(hDC, hObject); - ReleaseDC(hWnd, hDC); - SendDlgItemMessage(hWnd, nIDListBox, LB_SETHORIZONTALEXTENT, cxView + 8, 0); -} - -/** - * @brief OwnerDraw states from recent SDK. - */ -#define ODS_NOACCEL 0x0100 -#define ODS_NOFOCUSRECT 0x0200 - -/** - * @brief WM_DRAWITEM notification handlers. - */ -struct CDrawItemStruct : DRAWITEMSTRUCT -{ - typedef CDrawItemStruct *From; - void DrawWebLinkButton(); -}; - -void CDrawItemStruct::DrawWebLinkButton() -{ - CRawString cText; - int cchText = ::GetWindowText(hwndItem, cText.Data, cText.Size); - COLORREF clrText = RGB(0,0,255); - if (::GetWindowLong(hwndItem, GWL_STYLE) & BS_LEFTTEXT) - { - clrText = RGB(128,0,128); - } - RECT rcText = rcItem; - ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT|DT_CALCRECT); - ::OffsetRect(&rcText, 1, 0); - rcItem.right = rcText.right + 1; - rcItem.bottom = rcText.bottom + 1; - switch (itemAction) - { - case ODA_DRAWENTIRE: - ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, 0, 0, 0); - ::SetBkMode(hDC, TRANSPARENT); - ::SetTextColor(hDC, clrText); - ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT); - rcText.top = rcText.bottom - 1; - ::SetBkColor(hDC, clrText); - ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0); - if (itemState & ODS_FOCUS) - { - case ODA_FOCUS: - if (!(itemState & ODS_NOFOCUSRECT)) - { - ::SetTextColor(hDC, 0); - ::SetBkColor(hDC, RGB(255,255,255)); - ::SetBkMode(hDC, OPAQUE); - DrawFocusRect(hDC, &rcItem); - } - } - break; - } -} - -/** - * @brief Load a cursor from COMCTL32.DLL. - */ -HCURSOR NTAPI CommCtrl_LoadCursor(LPCTSTR lpCursorName) -{ - HMODULE hModule = GetModuleHandle(_T("COMCTL32.DLL")); - return hModule ? LoadCursor(hModule, lpCursorName) : NULL; -} - -/** - * @brief DLGPROC for C7ZipMismatchException's ReportError() popup. - */ -INT_PTR CALLBACK C7ZipMismatchException::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - switch (uMsg) - { - case WM_INITDIALOG: - { - theApp.TranslateDialog(hWnd); - if (GetDlgItem(hWnd, 9001) == NULL) - { - // Dialog template isn't up to date. Give it a second chance. - EndDialog(hWnd, -1); - return FALSE; - } - C7ZipMismatchException *pThis = (C7ZipMismatchException *)lParam; - CRawString cText; - CRawString cPresent, cMissing, cOutdated, cNone, cPlugin; - if (pThis->m_pCause) - { - pThis->m_pCause->GetErrorMessage(cText.Data, cText.Size); - SetDlgItemText(hWnd, 107, cText.Data); - } - else - { - GetDlgItemText(hWnd, 107, cText.Data, cText.Size); - switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE)) - { - case 0: - lstrcat(cText.Data, theApp.LoadString(IDS_MERGE7Z_ENABLE_0).c_str()); - break; - case 2: - lstrcat(cText.Data, theApp.LoadString(IDS_MERGE7Z_ENABLE_2).c_str()); - break; - } - SetDlgItemText(hWnd, 107, cText.Data); - } - GetDlgItemText(hWnd, 112, cPresent.Data, cPresent.Size); - GetDlgItemText(hWnd, 122, cMissing.Data, cMissing.Size); - GetDlgItemText(hWnd, 132, cOutdated.Data, cOutdated.Size); - GetDlgItemText(hWnd, 120, cNone.Data, cNone.Size); - GetDlgItemText(hWnd, 102, cPlugin.Data, cPlugin.Size); - wsprintf(cText.Data, cPlugin.Data, DllBuild_Merge7z); - SetDlgItemText(hWnd, 102, cText.Data); - SetDlgItemText - ( - hWnd, 109, - ( - pThis->m_dwVer7zRecommended == pThis->m_dwVer7zInstalled - || pThis->m_dwVer7zRecommended == pThis->m_dwVer7zLocal - ) ? cPresent.Data : cMissing.Data - ); - DWORD dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zRecommended); - SetDlgItemText(hWnd, 110, *cText.Data ? cText.Data : cNone.Data); - SetDlgItemText(hWnd, 111, cPlugin.Data); - SetDlgItemText(hWnd, 112, *cPlugin.Data == '\0' ? cPlugin.Data : - dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data); - dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zInstalled); - SetDlgItemText(hWnd, 120, *cText.Data ? cText.Data : cNone.Data); - SetDlgItemText(hWnd, 121, cPlugin.Data); - SetDlgItemText(hWnd, 122, *cPlugin.Data == '\0' ? cPlugin.Data : - dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data); - dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zLocal); - SetDlgItemText(hWnd, 130, *cText.Data ? cText.Data : cNone.Data); - SetDlgItemText(hWnd, 131, cPlugin.Data); - SetDlgItemText(hWnd, 132, *cPlugin.Data == '\0' ? cPlugin.Data : - dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data); - GetModuleFileName(0, cText.Data, MAX_PATH); - PathRemoveFileSpec(cText.Data); - PathAppend(cText.Data, _T("Merge7z*.dll")); - DlgDirListDLLs(hWnd, cText.Data, 105); - if (DWORD cchPath = GetEnvironmentVariable(_T("path"), 0, 0)) - { - static const TCHAR cSep[] = _T(";"); - LPTSTR pchPath = new TCHAR[cchPath]; - GetEnvironmentVariable(_T("PATH"), pchPath, cchPath); - LPTSTR pchItem = pchPath; - while (int cchItem = StrCSpn(pchItem += StrSpn(pchItem, cSep), cSep)) - { - if (cchItem < MAX_PATH) - { - CopyMemory(cText.Data, pchItem, cchItem*sizeof*pchItem); - cText.Data[cchItem] = 0; - PathAppend(cText.Data, _T("Merge7z*.dll")); - DlgDirListDLLs(hWnd, cText.Data, 105); - } - pchItem += cchItem; - } - delete[] pchPath; - } - if (SendDlgItemMessage(hWnd, 105, LB_GETCOUNT, 0, 0) == 0) - { - SendDlgItemMessage(hWnd, 105, LB_ADDSTRING, 0, (LPARAM) cNone.Data); - } - HICON hIcon = LoadIcon(0, IDI_EXCLAMATION); - SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon); - SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon); - if (pThis->m_bShowAllways) - { - ShowWindow(GetDlgItem(hWnd, 106), SW_HIDE); - } - } return TRUE; - case WM_DRAWITEM: - { - switch (wParam) - { - case 108: - CDrawItemStruct::From(lParam)->DrawWebLinkButton(); - break; - } - } return TRUE; - case WM_SETCURSOR: - { - HCURSOR hCursor = 0; - switch (GetDlgCtrlID((HWND)wParam)) - { - case 108: - hCursor = CommCtrl_LoadCursor(MAKEINTRESOURCE(108)); - break; - } - if (hCursor) - { - SetCursor(hCursor); - SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 1); - return TRUE; - } - } return FALSE; - case WM_COMMAND: - { - switch (wParam) - { - case IDOK: - case IDCANCEL: - { - LRESULT nDontShowAgain = SendDlgItemMessage(hWnd, 106, BM_GETCHECK, 0, 0); - EndDialog(hWnd, MAKEWORD(IDOK, nDontShowAgain)); - } break; - case 108: - { - HINSTANCE h = ShellExecute(hWnd, _T("open"), m_strDownloadURL, 0, 0, SW_SHOWNORMAL); - if ((UINT)h > 32) - { - LONG lStyle = ::GetWindowLong((HWND)lParam, GWL_STYLE); - ::SetWindowLong((HWND)lParam, GWL_STYLE, lStyle|BS_LEFTTEXT); - ::InvalidateRect((HWND)lParam, 0, TRUE); - } - else - { - MessageBeep(0); - } - } break; - } - } return TRUE; - } - return FALSE; -} - -/** - * @brief Tell user what went wrong and how she can help. - */ -int C7ZipMismatchException::ReportError(UINT nType, UINT nMessageID) -{ - UINT_PTR response = -1; - m_bShowAllways = nMessageID; - if (!m_bShowAllways) - { - // Suppress error message in case 7-Zip is not installed. - response = - ( - m_dwVer7zInstalled || m_dwVer7zLocal - ? (INT_PTR)(int)theApp.GetProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, -1) - : IDOK - ); - } - if (response == -1) - { - HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd(); - response = DialogBoxParam(AfxGetResourceHandle(), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this); - if (response == -1) - { - response = DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this); - ASSERT(response != -1); - } - if (HIBYTE(response) == 1) - { - theApp.WriteProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, response = int LOBYTE(response)); - } - } - return response; -} - -/** - * @brief Check whether archive support is available. - */ -int NTAPI HasZipSupport() -{ - static int HasZipSupport = -1; - if (HasZipSupport == -1) - { - try - { - m_Merge7z.operator->(); - HasZipSupport = 1; - } - catch (CException *e) - { - e->Delete(); - HasZipSupport = 0; - } - } - return HasZipSupport; -} - -/** - * @brief Tell user why archive support is not available. - */ -void NTAPI Recall7ZipMismatchError() -{ - try - { - m_Merge7z.operator->(); - } - catch (CException *e) - { - e->ReportError(MB_ICONEXCLAMATION, TRUE); - e->Delete(); - } -} - -/** - * @brief Delete head of temp path context list, and return its parent context. - */ -CTempPathContext *CTempPathContext::DeleteHead() -{ - CTempPathContext *pParent = m_pParent; - delete this; - return pParent; -} - -BOOL NTAPI IsMerge7zEnabled() -{ - return AfxGetApp()->GetProfileInt(_T("Merge7z"), _T("Enable"), 0); -} - -/** - * @brief Return installed or local version of 7-Zip. - */ -DWORD NTAPI VersionOf7z(BOOL bLocal) -{ - TCHAR path[MAX_PATH]; - if (bLocal) - { - GetModuleFileName(0, path, sizeof path/sizeof*path); - PathRemoveFileSpec(path); - } - else - { - static const TCHAR szSubKey[] = _T("Software\\7-Zip"); - static const TCHAR szValue[] = _T("Path"); - DWORD type = 0; - DWORD size = sizeof path; - SHGetValue(HKEY_LOCAL_MACHINE, szSubKey, szValue, &type, path, &size); - } - PathAppend(path, _T("7z.dll")); - unsigned versionMS = 0; - unsigned versionLS = 0; - CVersionInfo(path).GetFixedFileVersion(versionMS, versionLS); - return versionMS; -} - -/** - * @brief Access dll functions through proxy. - */ -interface Merge7z *Merge7z::Proxy::operator->() -{ - // As long as the Merge7z*.DLL has not yet been loaded, Merge7z - // [0] points to the name of the DLL (with placeholders for 7- - // Zip major and minor version numbers). Once the DLL has been - // loaded successfully, Merge7z[0] is set to NULL, causing the - // if to fail on subsequent calls. - - if (const char *format = Merge7z[0]) - { - // Merge7z has not yet been loaded - - char name[MAX_PATH]; - DWORD flags = ~0; - CException *pCause = NULL; - switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE)) - { - case 1: //Use installed 7-Zip if present. Otherwise, use local 7-Zip. - if (DWORD ver = VersionOf7z(FALSE)) - { - flags = Initialize::Default; - try - { - wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver)); - Merge7z[0] = name; - stub.Load(); - break; - } - catch (CException *e) - { - Merge7z[0] = format; - pCause = e; - } - } - case 2: //Always use local 7-Zip. - if (DWORD ver = VersionOf7z(TRUE)) - { - flags = Initialize::Default | Initialize::Local7z; - try - { - wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver)); - Merge7z[0] = name; - stub.Load(); - break; - } - catch (CException *e) - { - Merge7z[0] = format; - if (pCause) pCause->Delete(); - pCause = e; - } - } - default: - throw new C7ZipMismatchException - ( - VersionOf7z(FALSE), - VersionOf7z(TRUE), - pCause - ); - } - LANGID wLangID = (LANGID)GetThreadLocale(); - flags |= wLangID << 16; - if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE)) - { - flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension; - } - if (Merge7z[1]) - ((interface Merge7z *)Merge7z[1])->Initialize(flags); - } - return ((interface Merge7z *)Merge7z[1]); -} - -/** - * @brief Tell Merge7z we are going to enumerate just 1 item. - */ -UINT SingleItemEnumerator::Open() -{ - return 1; -} - -/** - * @brief Pass information about the item to Merge7z. - */ -Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item) -{ - item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse; - item.Name = Name; - item.FullPath = FullPath; - return 0; -} - -/** - * @brief SingleFileEnumerator constructor. - */ -SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name) -: FullPath(FullPath) -, Name(Name) -{ -} - -/** - * @brief Construct a CDirView::DirItemEnumerator. - * - * Argument *nFlags* controls operation as follows: - * LVNI_ALL: Enumerate all items. - * LVNI_SELECTED: Enumerate selected items only. - * Original: Set folder prefix for first iteration to "original" - * Altered: Set folder prefix for second iteration to "altered" - * BalanceFolders: Ensure that all nonempty folders on either side have a - * corresponding folder on the other side, even if it is - * empty (DirScan doesn't recurse into folders which - * appear only on one side). - * DiffsOnly: Enumerate diffs only. - */ -CDirView::DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags) -: m_pView(pView) -, m_nFlags(nFlags) -{ - if (m_nFlags & Original) - { - m_rgFolderPrefix.push_back(_T("original")); - } - if (m_nFlags & Altered) - { - m_rgFolderPrefix.push_back(_T("altered")); - } - if (m_nFlags & BalanceFolders) - { - // Collect implied folders - for (UINT i = Open() ; i-- ; ) - { - const DIFFITEM &di = Next(); - if ((m_nFlags & DiffsOnly) && !m_pView->IsItemNavigableDiff(di)) - { - continue; - } - if (m_bRight) - { - // Enumerating items on right side - if (!di.diffcode.isSideFirstOnly()) - { - // Item is present on right side, i.e. folder is implied - m_rgImpliedFoldersRight[di.diffFileInfo[1].path.get()] = PVOID(1); - } - } - else - { - // Enumerating items on left side - if (!di.diffcode.isSideSecondOnly()) - { - // Item is present on left side, i.e. folder is implied - m_rgImpliedFoldersLeft[di.diffFileInfo[0].path.get()] = PVOID(1); - } - } - } - } -} - -/** - * @brief Initialize enumerator, return number of items to be enumerated. - */ -UINT CDirView::DirItemEnumerator::Open() -{ - m_nIndex = -1; - m_curFolderPrefix = m_rgFolderPrefix.begin(); - m_bRight = (m_nFlags & Right) != 0; - size_t nrgFolderPrefix = m_rgFolderPrefix.size(); - if (nrgFolderPrefix) - { - m_strFolderPrefix = *m_curFolderPrefix++; - } - else - { - nrgFolderPrefix = 1; - } - return - ( - m_nFlags & LVNI_SELECTED - ? pView(m_pView)->GetSelectedCount() - : pView(m_pView)->GetItemCount() - ) * nrgFolderPrefix; -} - -/** - * @brief Return next item. - */ -const DIFFITEM &CDirView::DirItemEnumerator::Next() -{ - enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED}; - while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1) - { - m_strFolderPrefix = *m_curFolderPrefix++; - m_bRight = TRUE; - } - return m_pView->GetDiffItem(m_nIndex); -} - -/** - * @brief Pass information about an item to Merge7z. - * - * Information is passed through struct Merge7z::DirItemEnumerator::Item. - * The *mask* member denotes which of the other members contain valid data. - * If *mask* is zero upon return, which will be the case if Enum() decides to - * leave the struct untouched, Merge7z will ignore the item. - * If Enum() allocates temporary storage for string members, it must also - * allocate an Envelope, providing a Free() method to free the temporary - * storage, along with the Envelope itself. The Envelope pointer is passed to - * Merge7z as the return value of the function. It is not meant to be a success - * indicator, so if no temporary storage is required, it is perfectly alright - * to return NULL. - */ -Merge7z::Envelope *CDirView::DirItemEnumerator::Enum(Item &item) -{ - CDirDoc * pDoc = m_pView->GetDocument(); - const DIFFITEM &di = Next(); - - if ((m_nFlags & DiffsOnly) && !m_pView->IsItemNavigableDiff(di)) - { - return 0; - } - - bool isSideFirst = di.diffcode.isSideFirstOnly(); - bool isSideSecond = di.diffcode.isSideSecondOnly(); - - Envelope *envelope = new Envelope; - - const String &sFilename = m_bRight ? di.diffFileInfo[1].filename : di.diffFileInfo[0].filename; - const String &sSubdir = m_bRight ? di.diffFileInfo[1].path : di.diffFileInfo[0].path; - envelope->Name = sFilename; - if (sSubdir.length()) - { - envelope->Name.insert(0, _T("\\")); - envelope->Name.insert(0, sSubdir); - } - envelope->FullPath = sFilename; - envelope->FullPath.insert(0, _T("\\")); - envelope->FullPath.insert(0, m_bRight ? - di.getFilepath(1, pDoc->GetBasePath(1)) : - di.getFilepath(0, pDoc->GetBasePath(0))); - - UINT32 Recurse = item.Mask.Recurse; - - if (m_nFlags & BalanceFolders) - { - if (m_bRight) - { - // Enumerating items on right side - if (isSideFirst) - { - // Item is missing on right side - PVOID &implied = m_rgImpliedFoldersRight[di.diffFileInfo[0].path.get()]; - if (!implied) - { - // Folder is not implied by some other file, and has - // not been enumerated so far, so enumerate it now! - envelope->Name = di.diffFileInfo[0].path; - envelope->FullPath = di.getFilepath(0, pDoc->GetBasePath(0)); - implied = PVOID(2); // Don't enumerate same folder twice! - isSideFirst = false; - Recurse = 0; - } - } - } - else - { - // Enumerating items on left side - if (isSideSecond) - { - // Item is missing on left side - PVOID &implied = m_rgImpliedFoldersLeft[di.diffFileInfo[1].path.get()]; - if (!implied) - { - // Folder is not implied by some other file, and has - // not been enumerated so far, so enumerate it now! - envelope->Name = di.diffFileInfo[1].path; - envelope->FullPath = di.getFilepath(1, pDoc->GetBasePath(1)); - implied = PVOID(2); // Don't enumerate same folder twice! - isSideSecond = false; - Recurse = 0; - } - } - } - } - - if (m_bRight ? isSideFirst : isSideSecond) - { - return envelope; - } - - if (m_strFolderPrefix.length()) - { - if (envelope->Name.length()) - envelope->Name.insert(0, _T("\\")); - envelope->Name.insert(0, m_strFolderPrefix); - } - - item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse; - item.Name = envelope->Name.c_str(); - item.FullPath = envelope->FullPath.c_str(); - return envelope; -} - -/** - * @brief Apply appropriate handlers from left to right. - */ -bool CDirView::DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path) -{ - DeleteFile(path); - Merge7z::Format *piHandler = ArchiveGuessFormat(path); - if (piHandler) - { - HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd(); - CString pathIntermediate; - SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path))); - String pathPrepend = path; - pathPrepend.resize(pathPrepend.rfind('\\') + 1); - pathIntermediate.Insert(0, pathPrepend.c_str()); - bool bDone = MultiStepCompressArchive(pathIntermediate); - if (bDone) - { - piHandler->CompressArchive(hwndOwner, path, - &SingleItemEnumerator(path, pathIntermediate)); - DeleteFile(pathIntermediate); - } - else - { - piHandler->CompressArchive(hwndOwner, path, this); - } - return true; - } - return false; -} - -/** - * @brief Generate archive from DirView items. - */ -void CDirView::DirItemEnumerator::CompressArchive(LPCTSTR path) -{ - String strPath; - if (path == 0) - { - // No path given, so prompt for path! - static const TCHAR _T_Merge7z[] = _T("Merge7z"); - static const TCHAR _T_FilterIndex[] = _T("FilterIndex"); - // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't - // offer other formats here! - static const TCHAR _T_Filter[] - ( - _T("7z|*.7z|") - //_T("z|*.z|") - _T("zip|*.zip|") - _T("jar (zip)|*.jar|") - _T("ear (zip)|*.ear|") - _T("war (zip)|*.war|") - _T("xpi (zip)|*.xpi|") - //_T("rar|*.rar|") - _T("tar|*.tar|") - _T("tar.z|*.tar.z|") - _T("tar.gz|*.tar.gz|") - _T("tar.bz2|*.tar.bz2|") - //_T("tz|*.tz|") - _T("tgz|*.tgz|") - _T("tbz2|*.tbz2|") - //_T("lzh|*.lzh|") - //_T("cab|*.cab|") - //_T("arj|*.arj|") - //_T("deb|*.deb|") - //_T("rpm|*.rpm|") - //_T("cpio|*.cpio|") - //_T("|") - ); - String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString(); - strFilter.insert(0, _T_Filter); - strFilter += _T("|"); - CFileDialog dlg - ( - FALSE, - 0, - 0, - OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN, - strFilter.c_str() - ); - dlg.m_ofn.nFilterIndex = AfxGetApp()->GetProfileInt(_T_Merge7z, _T_FilterIndex, 1); - // Use extension from current filter as default extension: - if (int i = dlg.m_ofn.nFilterIndex) - { - dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter; - while (*dlg.m_ofn.lpstrDefExt && --i) - { - dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1; - dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1; - } - if (*dlg.m_ofn.lpstrDefExt) - { - dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3; - } - } - if (dlg.DoModal() == IDOK) - { - strPath = dlg.GetPathName(); - path = strPath.c_str(); - AfxGetApp()->WriteProfileInt(_T_Merge7z, _T_FilterIndex, dlg.m_ofn.nFilterIndex); - } - } - if (path && !MultiStepCompressArchive(path)) - { - LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION); - } -} - -/** - * @brief Collect files for SHFileOperation - */ -void CDirView::DirItemEnumerator::CollectFiles(String &strBuffer) -{ - CDirDoc *pDoc = m_pView->GetDocument(); - const String sLeftRootPath = pDoc->GetBasePath(0); - const String sRightRootPath = pDoc->GetBasePath(1); - UINT i; - int cchBuffer = 0; - for (i = Open() ; i-- ; ) - { - const DIFFITEM &di = Next(); - if (m_bRight ? m_pView->IsItemOpenableOnRightWith(di) : m_pView->IsItemOpenableOnLeftWith(di)) - { - cchBuffer += - ( - m_bRight ? di.getFilepath(1, sLeftRootPath) : di.getFilepath(0, sRightRootPath) - ).length() + (m_bRight ? di.diffFileInfo[1].filename : di.diffFileInfo[0].filename).get().length() + 2; - } - } - strBuffer.resize(cchBuffer + 1); - LPTSTR pchBuffer = &strBuffer[0]; - for (i = Open() ; i-- ; ) - { - const DIFFITEM &di = Next(); - if (m_bRight ? m_pView->IsItemOpenableOnRightWith(di) : m_pView->IsItemOpenableOnLeftWith(di)) - { - pchBuffer += wsprintf - ( - pchBuffer, - _T("%s\\%s"), - m_bRight ? di.getFilepath(1, sLeftRootPath).c_str() : di.getFilepath(0, sRightRootPath).c_str(), - m_bRight ? di.diffFileInfo[1].filename.get().c_str() : di.diffFileInfo[0].filename.get().c_str() - ) + 1; - } - } - ASSERT(pchBuffer - &strBuffer[0] == cchBuffer); -} - -DecompressResult DecompressArchive(HWND hWnd, const PathContext& files) -{ - DecompressResult res(files, NULL, IS_EXISTING_DIR); - try - { - String path; - USES_CONVERSION; - // Handle archives using 7-zip - Merge7z::Format *piHandler; - if (piHandler = ArchiveGuessFormat(res.files[0].c_str())) - { - res.pTempPathContext = new CTempPathContext; - path = env_GetTempChildPath(); - for (int index = 0; index < res.files.GetSize(); index++) - res.pTempPathContext->m_strDisplayRoot[index] = res.files[index]; - if (res.files.GetSize() == 2 && res.files[0] == res.files[1]) - res.files[1].erase(); - do - { - if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[0].c_str(), path.c_str()))) - break; - if (res.files[0].find(path) == 0) - { - VERIFY(::DeleteFile(res.files[0].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[0].c_str())), false)); - } - BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[0].c_str()); - res.files[0] = OLE2T(pTmp); - SysFreeString(pTmp); - res.files[0].insert(0, _T("\\")); - res.files[0].insert(0, path); - } while (piHandler = ArchiveGuessFormat(res.files[0].c_str())); - res.files[0] = path; - } - if (!res.files[1].empty() && (piHandler = ArchiveGuessFormat(res.files[1].c_str()))) - { - if (!res.pTempPathContext) - { - res.pTempPathContext = new CTempPathContext; - for (int index = 0; index < res.files.GetSize(); index++) - res.pTempPathContext->m_strDisplayRoot[index] = res.files[index]; - } - path = env_GetTempChildPath(); - do - { - if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[1].c_str(), path.c_str()))) - break;; - if (res.files[1].find(path) == 0) - { - VERIFY(::DeleteFile(res.files[1].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[1].c_str())), false)); - } - BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str()); - res.files[1] = OLE2T(pTmp); - SysFreeString(pTmp); - res.files[1].insert(0, _T("\\")); - res.files[1].insert(0, path); - } while (piHandler = ArchiveGuessFormat(res.files[1].c_str())); - res.files[1] = path; - } - if (res.files.GetSize() > 2 && (piHandler = ArchiveGuessFormat(res.files[2].c_str()))) - { - if (!res.pTempPathContext) - { - res.pTempPathContext = new CTempPathContext; - for (int index = 0; index < res.files.GetSize(); index++) - res.pTempPathContext->m_strDisplayRoot[index] = res.files[index]; - } - path = env_GetTempChildPath(); - do - { - if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[2].c_str(), path.c_str()))) - break;; - if (res.files[2].find(path) == 0) - { - VERIFY(::DeleteFile(res.files[2].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[2].c_str())), false)); - } - BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str()); - res.files[2] = OLE2T(pTmp); - SysFreeString(pTmp); - res.files[2].insert(0, _T("\\")); - res.files[2].insert(0, path); - } while (piHandler = ArchiveGuessFormat(res.files[2].c_str())); - res.files[2] = path; - } - if (res.files[1].empty()) - { - // assume Perry style patch - res.files[1] = path; - res.files[0] += _T("\\ORIGINAL"); - res.files[1] += _T("\\ALTERED"); - if (!PathFileExists(res.files[0].c_str()) || !PathFileExists(res.files[1].c_str())) - { - // not a Perry style patch: diff with itself... - res.files[0] = res.files[1] = path; - } - else - { - res.pTempPathContext->m_strDisplayRoot[0] += _T("\\ORIGINAL"); - res.pTempPathContext->m_strDisplayRoot[1] += _T("\\ALTERED"); - } - } - } - catch (CException *e) - { - e->ReportError(MB_ICONSTOP); - e->Delete(); - } - return res; -} - +///////////////////////////////////////////////////////////////////////////// +// WinMerge: an interactive diff/merge utility +// Copyright (C) 1997-2000 Thingamahoochie Software +// Author: Dean Grimm +// +// 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, write to the Free Software +// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// +///////////////////////////////////////////////////////////////////////////// + +/* 7zCommon.cpp: Implement 7z related classes and functions + * Copyright (c) 2003 Jochen Tucht + * + * Remarks: Different versions of 7-Zip are interfaced through specific + * versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.) + * WinMerge can either use an installed copy of the 7-Zip software, + * or fallback to a local set of 7-Zip DLLs, which are to be included + * in the WinMerge binary distribution. + * + * Fallback policies are as follows: + * + * 1. Detect 7-Zip version installed (XXX). + * 2. If there is a Merge7zXXX.dll, be happy to use it + * 3. Detect 7-Zip version from WinMerge distribution (YYY). + * 4. If there is a Merge7zYYY.dll, be happy to use it. + * 5. Sorry, no way. + * + * These rules can be customized by setting a registry variable + * *Merge7z/Enable* of type DWORD to one of the following values: + * + * 0 - Entirely disable 7-Zip integration. + * 1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip. + * 2 - Always use local 7-Zip. + * + +Please mind 2. a) of the GNU General Public License, and log your changes below. + +DATE: BY: DESCRIPTION: +========== ================== ================================================ +2003-12-09 Jochen Tucht Created +2003-12-16 Jochen Tucht Properly generate .tar.gz and .tar.bz2 +2003-12-16 Jochen Tucht Obtain long path to temporary folder +2004-01-20 Jochen Tucht Complain only once if Merge7z*.dll is missing +2004-01-25 Jochen Tucht Fix bad default for OPENFILENAME::nFilterIndex +2004-03-15 Jochen Tucht Fix Visual Studio 2003 build issue +2004-04-13 Jochen Tucht Avoid StrNCat to get away with shlwapi 4.70 +2004-08-25 Jochen Tucht More explicit error message +2004-10-17 Jochen Tucht Leave decision whether to recurse into folders + to enumerator (Mask.Recurse) +2004-11-03 Jochen Tucht FIX [1048997] as proposed by Kimmo 2004-11-02 +2005-01-15 Jochen Tucht Read 7-Zip version from 7zip_pad.xml + Set Merge7z UI language if DllBuild_Merge7z >= 9 +2005-01-22 Jochen Tucht Better explain what's present/missing/outdated +2005-02-05 Jochen Tucht Fall back to IDD_MERGE7ZMISMATCH template from + .exe if .lang file isn't up to date. +2005-02-26 Jochen Tucht Add download link to error message +2005-02-26 Jochen Tucht Use WinAPI to obtain ISO language/region codes +2005-02-27 Jochen Tucht FIX [1152375] +2005-04-24 Kimmo Varis Don't use DiffContext exported from DirView +2005-06-08 Kimmo Varis Use DIFFITEM, not reference to it (hopefully only + temporarily, to sort out new directory compare) +2005-06-22 Jochen Tucht Change recommended version of 7-Zip to 4.20 + Remove noise from Nagbox +2005-07-03 Jochen Tucht DIFFITEM has changed due to RFE [ 1205516 ] +2005-07-04 Jochen Tucht New global ArchiveGuessFormat() checks for + formats to be handled by external command line + tools. These take precedence over Merge7z + internal handlers. +2005-07-05 Jochen Tucht Move to Merge7z::Format::GetDefaultName() to + build intermediate filenames for multi-step + compression. +2005-07-15 Jochen Tucht Remove external command line tool integration + for now. Rethink about it after 2.4 branch. +2005-08-20 Jochen Tucht Option to guess archive format by signature + Map extensions through ExternalArchiveFormat.ini +2005-08-23 Jochen Tucht Option to entirely disable 7-Zip integration +2007-01-04 Kimmo Varis Convert using COptionsMgr for options. +2007-06-16 Jochen Neubeck FIX [1723263] "Zip --> Both" operation... +2007-12-22 Jochen Neubeck Fix Merge7z UI lang for new translation system + Change recommended version of 7-Zip to 4.57 +2010-05-16 Jochen Neubeck Read 7-Zip version from 7z.dll (which has long + ago replaced the various format and codec DLLs) + Change recommended version of 7-Zip to 4.65 +*/ + +// ID line follows -- this is updated by SVN +// $Id: 7zCommon.cpp 7169 2010-05-16 14:44:19Z jtuc $ + +#include "stdafx.h" +#include "7zCommon.h" +#include +#include +#include "OptionsDef.h" +#include "OptionsMgr.h" +#include "Merge.h" // DirDocFilter theApp GetOptionsMgr() +#include "resource.h" +#include "DirView.h" +#include "DirDoc.h" +#include "DirActions.h" +//#include "ExternalArchiveFormat.h" +#include "version.h" +#include "paths.h" +#include "Environment.h" +#include "Merge7zFormatRegister.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +/** + * @brief Proxy for Merge7z + */ +static __declspec(thread) Merge7z::Proxy m_Merge7z = +{ + { 0, 0, DllBuild_Merge7z, }, + "Merge7z%u%02u"DECORATE_U".dll", + "Merge7z", + NULL +}; + +std::vector Merge7zFormatRegister::optionalFormats; + +/** + * @brief assign BSTR to String, and return BSTR for optional SysFreeString() + */ +inline BSTR Assign(CString &dst, BSTR src) +{ + dst = src; + return src; +} + +bool IsArchiveFile(const String& pszFile) +{ + try { + Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile); + if (piHandler) + return TRUE; + else + return FALSE; + } + catch (CException *e) + { + e->Delete(); + return FALSE; + } + return FALSE; +} + +/** + * @brief Wrap Merge7z::GuessFormat() to allow for some customizing: + * - Check if 7-Zip integration is enabled. + * - Check for filename extension mappings. + */ +Merge7z::Format *ArchiveGuessFormat(const String& path) +{ + if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0) + return NULL; + if (paths_IsDirectory(path)) + return NULL; + String path2(path); + // Map extensions through ExternalArchiveFormat.ini + static TCHAR null[] = _T(""); + static const TCHAR section[] = _T("extensions"); + String entry = paths_FindExtension(path); + TCHAR value[20]; + static LPCTSTR filename = NULL; + if (filename == NULL) + { + TCHAR cPath[INTERNET_MAX_PATH_LENGTH]; + DWORD cchPath = SearchPath(NULL, _T("ExternalArchiveFormat.ini"), NULL, + INTERNET_MAX_PATH_LENGTH, cPath, NULL); + filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? StrDup(cPath) : null; + } + if (*filename && + GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) && + *value == '.') + { + // Remove end-of-line comments (in string returned from GetPrivateProfileString) + // that is, remove semicolon & whatever follows it + if (LPTSTR p = StrChr(value, ';')) + { + *p = '\0'; + StrTrim(value, _T(" \t")); + } + path2 = value; + } + + // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues + // command line integration portion is not yet applied + // so following code not yet valid, so temporarily commented out + // Look for command line tool first + /*Merge7z::Format *pFormat; + if (CExternalArchiveFormat::GuessFormat(path, pFormat)) + { + return pFormat; + }*/ + // Default to Merge7z*.dll + + try + { + Merge7z::Format *pFormat = m_Merge7z->GuessFormat(path2.c_str()); + if (!pFormat) + pFormat = Merge7zFormatRegister::GuessFormat(path2); + return pFormat; + } + catch (...) + { + Merge7z::Format *pFormat = Merge7zFormatRegister::GuessFormat(path2); + if (pFormat) + return pFormat; + throw; + } +} + +/** + * @brief Self-initializing raw C character buffer class. + */ +template struct CRawString +{ + enum { Size = SIZE }; + TYPE Data[SIZE]; + CRawString() + { + Data[0] = 0; + } +}; + +/** + * @brief Exception class for more explicit error message. + */ +class C7ZipMismatchException : public CException +{ +public: + C7ZipMismatchException(DWORD dwVer7zInstalled, DWORD dwVer7zLocal, CException *pCause) + { + m_dwVer7zInstalled = dwVer7zInstalled; + m_dwVer7zLocal = dwVer7zLocal; + m_pCause = pCause; + } + ~C7ZipMismatchException() + { + if (m_pCause) + m_pCause->Delete(); + } + virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0); +protected: + DWORD m_dwVer7zInstalled; + DWORD m_dwVer7zLocal; + CException *m_pCause; + BOOL m_bShowAllways; + static const DWORD m_dwVer7zRecommended; + static const TCHAR m_strRegistryKey[]; + static const TCHAR m_strDownloadURL[]; + static INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM); + static DWORD FormatVersion(LPTSTR, LPTSTR, DWORD); +}; + +/** + * @brief Recommended version of 7-Zip. + */ +const DWORD C7ZipMismatchException::m_dwVer7zRecommended = DWORD MAKELONG(65,4); + +/** + * @brief Registry key for C7ZipMismatchException's ReportError() popup. + */ +const TCHAR C7ZipMismatchException::m_strRegistryKey[] = _T("7ZipMismatch"); + +/** + * @brief Download URL for C7ZipMismatchException's ReportError() popup. + */ +const TCHAR C7ZipMismatchException::m_strDownloadURL[] = _T("https://sourceforge.net/project/showfiles.php?group_id=13216&package_id=143957"); + +/** + * @brief Retrieve build number of given DLL. + */ +static DWORD NTAPI GetDllBuild(LPCTSTR cPath) +{ + HMODULE hModule = LoadLibrary(cPath); + DLLVERSIONINFO dvi; + dvi.cbSize = sizeof dvi; + dvi.dwBuildNumber = ~0UL; + if (hModule) + { + dvi.dwBuildNumber = 0UL; + DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hModule, "DllGetVersion"); + if (DllGetVersion) + { + DllGetVersion(&dvi); + } + FreeLibrary(hModule); + } + return dvi.dwBuildNumber; +} + +/** + * @brief Format Merge7z version number and plugin name, and retrieve DllBuild. + */ +DWORD C7ZipMismatchException::FormatVersion(LPTSTR pcVersion, LPTSTR pcPluginName, DWORD dwVersion) +{ + *pcVersion = '\0'; + *pcPluginName = '\0'; + if (dwVersion) + { + wsprintf + ( + pcVersion, _T("%u.%02u"), + UINT HIWORD(dwVersion), + UINT LOWORD(dwVersion) + ); + wsprintf + ( + pcPluginName, + sizeof(TCHAR) == 1 ? _T("Merge7z%u%02u.dll") : _T("Merge7z%u%02uU.dll"), + UINT HIWORD(dwVersion), + UINT LOWORD(dwVersion) + ); + } + return GetDllBuild(pcPluginName); +} + +/** + * @brief Populate ListBox with names/revisions of DLLs matching given pattern. + */ +static void NTAPI DlgDirListDLLs(HWND hWnd, LPTSTR cPattern, int nIDListBox) +{ + HDC hDC = GetDC(hWnd); + HFONT hFont = (HFONT)SendDlgItemMessage(hWnd, nIDListBox, WM_GETFONT, 0, 0); + int cxView = (int)SendDlgItemMessage(hWnd, nIDListBox, LB_GETHORIZONTALEXTENT, 0, 0) - 8; + HGDIOBJ hObject = SelectObject(hDC, hFont); + WIN32_FIND_DATA ff; + HANDLE h = FindFirstFile(cPattern, &ff); + if (h != INVALID_HANDLE_VALUE) + { + do + { + PathRemoveFileSpec(cPattern); + PathAppend(cPattern, ff.cFileName); + wsprintf(ff.cFileName, _T(" (dllbuild %04u)"), GetDllBuild(cPattern)); + lstrcat(cPattern, ff.cFileName); + int cxText = (int)(WORD)GetTabbedTextExtent(hDC, cPattern, lstrlen(cPattern), 0, 0); + if (cxView < cxText) + cxView = cxText; + SendDlgItemMessage(hWnd, nIDListBox, LB_ADDSTRING, 0, (LPARAM)cPattern); + } while (FindNextFile(h, &ff)); + FindClose(h); + } + SelectObject(hDC, hObject); + ReleaseDC(hWnd, hDC); + SendDlgItemMessage(hWnd, nIDListBox, LB_SETHORIZONTALEXTENT, cxView + 8, 0); +} + +/** + * @brief OwnerDraw states from recent SDK. + */ +#define ODS_NOACCEL 0x0100 +#define ODS_NOFOCUSRECT 0x0200 + +/** + * @brief WM_DRAWITEM notification handlers. + */ +struct CDrawItemStruct : DRAWITEMSTRUCT +{ + typedef CDrawItemStruct *From; + void DrawWebLinkButton(); +}; + +void CDrawItemStruct::DrawWebLinkButton() +{ + CRawString cText; + int cchText = ::GetWindowText(hwndItem, cText.Data, cText.Size); + COLORREF clrText = RGB(0,0,255); + if (::GetWindowLong(hwndItem, GWL_STYLE) & BS_LEFTTEXT) + { + clrText = RGB(128,0,128); + } + RECT rcText = rcItem; + ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT|DT_CALCRECT); + ::OffsetRect(&rcText, 1, 0); + rcItem.right = rcText.right + 1; + rcItem.bottom = rcText.bottom + 1; + switch (itemAction) + { + case ODA_DRAWENTIRE: + ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, 0, 0, 0); + ::SetBkMode(hDC, TRANSPARENT); + ::SetTextColor(hDC, clrText); + ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT); + rcText.top = rcText.bottom - 1; + ::SetBkColor(hDC, clrText); + ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0); + if (itemState & ODS_FOCUS) + { + case ODA_FOCUS: + if (!(itemState & ODS_NOFOCUSRECT)) + { + ::SetTextColor(hDC, 0); + ::SetBkColor(hDC, RGB(255,255,255)); + ::SetBkMode(hDC, OPAQUE); + DrawFocusRect(hDC, &rcItem); + } + } + break; + } +} + +/** + * @brief Load a cursor from COMCTL32.DLL. + */ +HCURSOR NTAPI CommCtrl_LoadCursor(LPCTSTR lpCursorName) +{ + HMODULE hModule = GetModuleHandle(_T("COMCTL32.DLL")); + return hModule ? LoadCursor(hModule, lpCursorName) : NULL; +} + +/** + * @brief DLGPROC for C7ZipMismatchException's ReportError() popup. + */ +INT_PTR CALLBACK C7ZipMismatchException::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + theApp.TranslateDialog(hWnd); + if (GetDlgItem(hWnd, 9001) == NULL) + { + // Dialog template isn't up to date. Give it a second chance. + EndDialog(hWnd, -1); + return FALSE; + } + C7ZipMismatchException *pThis = (C7ZipMismatchException *)lParam; + CRawString cText; + CRawString cPresent, cMissing, cOutdated, cNone, cPlugin; + if (pThis->m_pCause) + { + pThis->m_pCause->GetErrorMessage(cText.Data, cText.Size); + SetDlgItemText(hWnd, 107, cText.Data); + } + else + { + GetDlgItemText(hWnd, 107, cText.Data, cText.Size); + switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE)) + { + case 0: + lstrcat(cText.Data, _("\nNote: 7-Zip integration is disabled in WinMerge settings.").c_str()); + break; + case 2: + lstrcat(cText.Data, _("\nNote: 7-Zip integration is restricted to standalone operation in WinMerge settings.").c_str()); + break; + } + SetDlgItemText(hWnd, 107, cText.Data); + } + GetDlgItemText(hWnd, 112, cPresent.Data, cPresent.Size); + GetDlgItemText(hWnd, 122, cMissing.Data, cMissing.Size); + GetDlgItemText(hWnd, 132, cOutdated.Data, cOutdated.Size); + GetDlgItemText(hWnd, 120, cNone.Data, cNone.Size); + GetDlgItemText(hWnd, 102, cPlugin.Data, cPlugin.Size); + wsprintf(cText.Data, cPlugin.Data, DllBuild_Merge7z); + SetDlgItemText(hWnd, 102, cText.Data); + SetDlgItemText + ( + hWnd, 109, + ( + pThis->m_dwVer7zRecommended == pThis->m_dwVer7zInstalled + || pThis->m_dwVer7zRecommended == pThis->m_dwVer7zLocal + ) ? cPresent.Data : cMissing.Data + ); + DWORD dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zRecommended); + SetDlgItemText(hWnd, 110, *cText.Data ? cText.Data : cNone.Data); + SetDlgItemText(hWnd, 111, cPlugin.Data); + SetDlgItemText(hWnd, 112, *cPlugin.Data == '\0' ? cPlugin.Data : + dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data); + dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zInstalled); + SetDlgItemText(hWnd, 120, *cText.Data ? cText.Data : cNone.Data); + SetDlgItemText(hWnd, 121, cPlugin.Data); + SetDlgItemText(hWnd, 122, *cPlugin.Data == '\0' ? cPlugin.Data : + dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data); + dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zLocal); + SetDlgItemText(hWnd, 130, *cText.Data ? cText.Data : cNone.Data); + SetDlgItemText(hWnd, 131, cPlugin.Data); + SetDlgItemText(hWnd, 132, *cPlugin.Data == '\0' ? cPlugin.Data : + dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data); + GetModuleFileName(0, cText.Data, MAX_PATH); + PathRemoveFileSpec(cText.Data); + PathAppend(cText.Data, _T("Merge7z*.dll")); + DlgDirListDLLs(hWnd, cText.Data, 105); + if (DWORD cchPath = GetEnvironmentVariable(_T("path"), 0, 0)) + { + static const TCHAR cSep[] = _T(";"); + LPTSTR pchPath = new TCHAR[cchPath]; + GetEnvironmentVariable(_T("PATH"), pchPath, cchPath); + LPTSTR pchItem = pchPath; + while (int cchItem = StrCSpn(pchItem += StrSpn(pchItem, cSep), cSep)) + { + if (cchItem < MAX_PATH) + { + CopyMemory(cText.Data, pchItem, cchItem*sizeof*pchItem); + cText.Data[cchItem] = 0; + PathAppend(cText.Data, _T("Merge7z*.dll")); + DlgDirListDLLs(hWnd, cText.Data, 105); + } + pchItem += cchItem; + } + delete[] pchPath; + } + if (SendDlgItemMessage(hWnd, 105, LB_GETCOUNT, 0, 0) == 0) + { + SendDlgItemMessage(hWnd, 105, LB_ADDSTRING, 0, (LPARAM) cNone.Data); + } + HICON hIcon = LoadIcon(0, IDI_EXCLAMATION); + SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon); + SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon); + if (pThis->m_bShowAllways) + { + ShowWindow(GetDlgItem(hWnd, 106), SW_HIDE); + } + } return TRUE; + case WM_DRAWITEM: + { + switch (wParam) + { + case 108: + CDrawItemStruct::From(lParam)->DrawWebLinkButton(); + break; + } + } return TRUE; + case WM_SETCURSOR: + { + HCURSOR hCursor = 0; + switch (GetDlgCtrlID((HWND)wParam)) + { + case 108: + hCursor = CommCtrl_LoadCursor(MAKEINTRESOURCE(108)); + break; + } + if (hCursor) + { + SetCursor(hCursor); + SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 1); + return TRUE; + } + } return FALSE; + case WM_COMMAND: + { + switch (wParam) + { + case IDOK: + case IDCANCEL: + { + LRESULT nDontShowAgain = SendDlgItemMessage(hWnd, 106, BM_GETCHECK, 0, 0); + EndDialog(hWnd, MAKEWORD(IDOK, nDontShowAgain)); + } break; + case 108: + { + HINSTANCE h = ShellExecute(hWnd, _T("open"), m_strDownloadURL, 0, 0, SW_SHOWNORMAL); + if ((UINT)h > 32) + { + LONG lStyle = ::GetWindowLong((HWND)lParam, GWL_STYLE); + ::SetWindowLong((HWND)lParam, GWL_STYLE, lStyle|BS_LEFTTEXT); + ::InvalidateRect((HWND)lParam, 0, TRUE); + } + else + { + MessageBeep(0); + } + } break; + } + } return TRUE; + } + return FALSE; +} + +/** + * @brief Tell user what went wrong and how she can help. + */ +int C7ZipMismatchException::ReportError(UINT nType, UINT nMessageID) +{ + UINT_PTR response = -1; + m_bShowAllways = nMessageID; + if (!m_bShowAllways) + { + // Suppress error message in case 7-Zip is not installed. + response = + ( + m_dwVer7zInstalled || m_dwVer7zLocal + ? (INT_PTR)(int)theApp.GetProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, -1) + : IDOK + ); + } + if (response == -1) + { + HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd(); + response = DialogBoxParam(AfxGetResourceHandle(), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this); + if (response == -1) + { + response = DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this); + ASSERT(response != -1); + } + if (HIBYTE(response) == 1) + { + theApp.WriteProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, response = int LOBYTE(response)); + } + } + return response; +} + +/** + * @brief Check whether archive support is available. + */ +int NTAPI HasZipSupport() +{ + static int HasZipSupport = -1; + if (HasZipSupport == -1) + { + try + { + m_Merge7z.operator->(); + HasZipSupport = 1; + } + catch (CException *e) + { + e->Delete(); + HasZipSupport = 0; + } + } + return HasZipSupport; +} + +/** + * @brief Tell user why archive support is not available. + */ +void NTAPI Recall7ZipMismatchError() +{ + try + { + m_Merge7z.operator->(); + } + catch (CException *e) + { + e->ReportError(MB_ICONEXCLAMATION, TRUE); + e->Delete(); + } +} + +/** + * @brief Delete head of temp path context list, and return its parent context. + */ +CTempPathContext *CTempPathContext::DeleteHead() +{ + CTempPathContext *pParent = m_pParent; + delete this; + return pParent; +} + +BOOL NTAPI IsMerge7zEnabled() +{ + return AfxGetApp()->GetProfileInt(_T("Merge7z"), _T("Enable"), 0); +} + +/** + * @brief Return installed or local version of 7-Zip. + */ +DWORD NTAPI VersionOf7z(BOOL bLocal) +{ + TCHAR path[MAX_PATH]; + if (bLocal) + { + GetModuleFileName(0, path, sizeof path/sizeof*path); + PathRemoveFileSpec(path); + } + else + { + static const TCHAR szSubKey[] = _T("Software\\7-Zip"); + static const TCHAR szValue[] = _T("Path"); + DWORD type = 0; + DWORD size = sizeof path; + SHGetValue(HKEY_LOCAL_MACHINE, szSubKey, szValue, &type, path, &size); + } + PathAppend(path, _T("7z.dll")); + unsigned versionMS = 0; + unsigned versionLS = 0; + CVersionInfo(path).GetFixedFileVersion(versionMS, versionLS); + return versionMS; +} + +/** + * @brief Access dll functions through proxy. + */ +interface Merge7z *Merge7z::Proxy::operator->() +{ + // As long as the Merge7z*.DLL has not yet been loaded, Merge7z + // [0] points to the name of the DLL (with placeholders for 7- + // Zip major and minor version numbers). Once the DLL has been + // loaded successfully, Merge7z[0] is set to NULL, causing the + // if to fail on subsequent calls. + + if (const char *format = Merge7z[0]) + { + // Merge7z has not yet been loaded + + char name[MAX_PATH]; + DWORD flags = ~0; + CException *pCause = NULL; + switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE)) + { + case 1: //Use installed 7-Zip if present. Otherwise, use local 7-Zip. + if (DWORD ver = VersionOf7z(FALSE)) + { + flags = Initialize::Default; + try + { + wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver)); + Merge7z[0] = name; + stub.Load(); + break; + } + catch (CException *e) + { + Merge7z[0] = format; + pCause = e; + } + } + case 2: //Always use local 7-Zip. + if (DWORD ver = VersionOf7z(TRUE)) + { + flags = Initialize::Default | Initialize::Local7z; + try + { + wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver)); + Merge7z[0] = name; + stub.Load(); + break; + } + catch (CException *e) + { + Merge7z[0] = format; + if (pCause) pCause->Delete(); + pCause = e; + } + } + default: + throw new C7ZipMismatchException + ( + VersionOf7z(FALSE), + VersionOf7z(TRUE), + pCause + ); + } + LANGID wLangID = (LANGID)GetThreadLocale(); + flags |= wLangID << 16; + if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE)) + { + flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension; + } + if (Merge7z[1]) + ((interface Merge7z *)Merge7z[1])->Initialize(flags); + } + return ((interface Merge7z *)Merge7z[1]); +} + +/** + * @brief Tell Merge7z we are going to enumerate just 1 item. + */ +UINT SingleItemEnumerator::Open() +{ + return 1; +} + +/** + * @brief Pass information about the item to Merge7z. + */ +Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item) +{ + item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse; + item.Name = Name; + item.FullPath = FullPath; + return 0; +} + +/** + * @brief SingleFileEnumerator constructor. + */ +SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name) +: FullPath(FullPath) +, Name(Name) +{ +} + +/** + * @brief Construct a DirItemEnumerator. + * + * Argument *nFlags* controls operation as follows: + * LVNI_ALL: Enumerate all items. + * LVNI_SELECTED: Enumerate selected items only. + * Original: Set folder prefix for first iteration to "original" + * Altered: Set folder prefix for second iteration to "altered" + * BalanceFolders: Ensure that all nonempty folders on either side have a + * corresponding folder on the other side, even if it is + * empty (DirScan doesn't recurse into folders which + * appear only on one side). + * DiffsOnly: Enumerate diffs only. + */ +DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags) +: m_pView(pView) +, m_nFlags(nFlags) +{ + if (m_nFlags & Original) + { + m_rgFolderPrefix.push_back(_T("original")); + } + if (m_nFlags & Altered) + { + m_rgFolderPrefix.push_back(_T("altered")); + } + if (m_nFlags & BalanceFolders) + { + const CDiffContext& ctxt = pView->GetDiffContext(); + // Collect implied folders + for (UINT i = Open() ; i-- ; ) + { + const DIFFITEM &di = Next(); + if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di)) + { + continue; + } + // Enumerating items - if (di.diffcode.isExists(m_index)) ++ if (di.diffcode.exists(m_index)) + { + // Item is present on right side, i.e. folder is implied + m_rgImpliedFolders[m_index][di.diffFileInfo[m_index].path.get()] = PVOID(1); + } + } + } +} + +/** + * @brief Initialize enumerator, return number of items to be enumerated. + */ +UINT DirItemEnumerator::Open() +{ + m_nIndex = -1; + m_curFolderPrefix = m_rgFolderPrefix.begin(); + m_index = (m_nFlags & Right) != 0 ? 1 : 0; + size_t nrgFolderPrefix = m_rgFolderPrefix.size(); + if (nrgFolderPrefix) + { + m_strFolderPrefix = *m_curFolderPrefix++; + } + else + { + nrgFolderPrefix = 1; + } + return + ( + m_nFlags & LVNI_SELECTED + ? pView(m_pView)->GetSelectedCount() + : pView(m_pView)->GetItemCount() + ) * nrgFolderPrefix; +} + +/** + * @brief Return next item. + */ +const DIFFITEM &DirItemEnumerator::Next() +{ + enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED}; + while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1) + { + m_strFolderPrefix = *m_curFolderPrefix++; + m_index = 1; + } + return m_pView->GetDiffItem(m_nIndex); +} + +/** + * @brief Pass information about an item to Merge7z. + * + * Information is passed through struct Merge7z::DirItemEnumerator::Item. + * The *mask* member denotes which of the other members contain valid data. + * If *mask* is zero upon return, which will be the case if Enum() decides to + * leave the struct untouched, Merge7z will ignore the item. + * If Enum() allocates temporary storage for string members, it must also + * allocate an Envelope, providing a Free() method to free the temporary + * storage, along with the Envelope itself. The Envelope pointer is passed to + * Merge7z as the return value of the function. It is not meant to be a success + * indicator, so if no temporary storage is required, it is perfectly alright + * to return NULL. + */ +Merge7z::Envelope *DirItemEnumerator::Enum(Item &item) +{ + const CDiffContext& ctxt = m_pView->GetDiffContext(); + const DIFFITEM &di = Next(); + + if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di)) + { + return 0; + } + - bool isSideOnly = !di.diffcode.isExists(m_index); ++ bool isSideOnly = !di.diffcode.exists(m_index); + + Envelope *envelope = new Envelope; + + const String &sFilename = di.diffFileInfo[m_index].filename; + const String &sSubdir = di.diffFileInfo[m_index].path; + if (sSubdir.length()) + envelope->Name = paths_ConcatPath(sSubdir, sFilename); + else + envelope->Name = sFilename; + envelope->FullPath = paths_ConcatPath( + di.getFilepath(m_index, ctxt.GetNormalizedPath(m_index)), + sFilename); + + UINT32 Recurse = item.Mask.Recurse; + + if (m_nFlags & BalanceFolders) + { + // Enumerating items on right side + if (isSideOnly) + { + // Item is missing on right side + PVOID &implied = m_rgImpliedFolders[m_index][di.diffFileInfo[1-m_index].path.get()]; + if (!implied) + { + // Folder is not implied by some other file, and has + // not been enumerated so far, so enumerate it now! + envelope->Name = di.diffFileInfo[1-m_index].path; + envelope->FullPath = di.getFilepath(1-m_index, ctxt.GetNormalizedPath(1-m_index)); + implied = PVOID(2); // Don't enumerate same folder twice! + isSideOnly = false; + Recurse = 0; + } + } + } + + if (isSideOnly) + { + return envelope; + } + + if (m_strFolderPrefix.length()) + { + if (envelope->Name.length()) + envelope->Name.insert(0, _T("\\")); + envelope->Name.insert(0, m_strFolderPrefix); + } + + item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse; + item.Name = envelope->Name.c_str(); + item.FullPath = envelope->FullPath.c_str(); + return envelope; +} + +/** + * @brief Apply appropriate handlers from left to right. + */ +bool DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path) +{ + DeleteFile(path); + Merge7z::Format *piHandler = ArchiveGuessFormat(path); + if (piHandler) + { + HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd(); + CString pathIntermediate; + SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path))); + String pathPrepend = path; + pathPrepend.resize(pathPrepend.rfind('\\') + 1); + pathIntermediate.Insert(0, pathPrepend.c_str()); + bool bDone = MultiStepCompressArchive(pathIntermediate); + if (bDone) + { + piHandler->CompressArchive(hwndOwner, path, + &SingleItemEnumerator(path, pathIntermediate)); + DeleteFile(pathIntermediate); + } + else + { + piHandler->CompressArchive(hwndOwner, path, this); + } + return true; + } + return false; +} + +/** + * @brief Generate archive from DirView items. + */ +void DirItemEnumerator::CompressArchive(LPCTSTR path) +{ + String strPath; + if (path == 0) + { + // No path given, so prompt for path! + static const TCHAR _T_Merge7z[] = _T("Merge7z"); + static const TCHAR _T_FilterIndex[] = _T("FilterIndex"); + // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't + // offer other formats here! + static const TCHAR _T_Filter[] + ( + _T("7z|*.7z|") + //_T("z|*.z|") + _T("zip|*.zip|") + _T("jar (zip)|*.jar|") + _T("ear (zip)|*.ear|") + _T("war (zip)|*.war|") + _T("xpi (zip)|*.xpi|") + //_T("rar|*.rar|") + _T("tar|*.tar|") + _T("tar.z|*.tar.z|") + _T("tar.gz|*.tar.gz|") + _T("tar.bz2|*.tar.bz2|") + //_T("tz|*.tz|") + _T("tgz|*.tgz|") + _T("tbz2|*.tbz2|") + //_T("lzh|*.lzh|") + //_T("cab|*.cab|") + //_T("arj|*.arj|") + //_T("deb|*.deb|") + //_T("rpm|*.rpm|") + //_T("cpio|*.cpio|") + //_T("|") + ); + String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString(); + strFilter.insert(0, _T_Filter); + strFilter += _T("|"); + CFileDialog dlg + ( + FALSE, + 0, + 0, + OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN, + strFilter.c_str() + ); + dlg.m_ofn.nFilterIndex = AfxGetApp()->GetProfileInt(_T_Merge7z, _T_FilterIndex, 1); + // Use extension from current filter as default extension: + if (int i = dlg.m_ofn.nFilterIndex) + { + dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter; + while (*dlg.m_ofn.lpstrDefExt && --i) + { + dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1; + dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1; + } + if (*dlg.m_ofn.lpstrDefExt) + { + dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3; + } + } + if (dlg.DoModal() == IDOK) + { + strPath = dlg.GetPathName(); + path = strPath.c_str(); + AfxGetApp()->WriteProfileInt(_T_Merge7z, _T_FilterIndex, dlg.m_ofn.nFilterIndex); + } + } + if (path && !MultiStepCompressArchive(path)) + { + LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION); + } +} + + +DecompressResult DecompressArchive(HWND hWnd, const PathContext& files) +{ + DecompressResult res(files, NULL, IS_EXISTING_DIR); + try + { + String path; + USES_CONVERSION; + // Handle archives using 7-zip + Merge7z::Format *piHandler; + if (piHandler = ArchiveGuessFormat(res.files[0].c_str())) + { + res.pTempPathContext = new CTempPathContext; + path = env_GetTempChildPath(); + for (int index = 0; index < res.files.GetSize(); index++) + res.pTempPathContext->m_strDisplayRoot[index] = res.files[index]; + if (res.files.GetSize() == 2 && res.files[0] == res.files[1]) + res.files[1].erase(); + do + { + if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[0].c_str(), path.c_str()))) + break; + if (res.files[0].find(path) == 0) + { + VERIFY(::DeleteFile(res.files[0].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[0].c_str())), false)); + } + BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[0].c_str()); + res.files[0] = OLE2T(pTmp); + SysFreeString(pTmp); + res.files[0].insert(0, _T("\\")); + res.files[0].insert(0, path); + } while (piHandler = ArchiveGuessFormat(res.files[0].c_str())); + res.files[0] = path; + } + if (!res.files[1].empty() && (piHandler = ArchiveGuessFormat(res.files[1].c_str()))) + { + if (!res.pTempPathContext) + { + res.pTempPathContext = new CTempPathContext; + for (int index = 0; index < res.files.GetSize(); index++) + res.pTempPathContext->m_strDisplayRoot[index] = res.files[index]; + } + path = env_GetTempChildPath(); + do + { + if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[1].c_str(), path.c_str()))) + break;; + if (res.files[1].find(path) == 0) + { + VERIFY(::DeleteFile(res.files[1].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[1].c_str())), false)); + } + BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str()); + res.files[1] = OLE2T(pTmp); + SysFreeString(pTmp); + res.files[1].insert(0, _T("\\")); + res.files[1].insert(0, path); + } while (piHandler = ArchiveGuessFormat(res.files[1].c_str())); + res.files[1] = path; + } + if (res.files.GetSize() > 2 && (piHandler = ArchiveGuessFormat(res.files[2].c_str()))) + { + if (!res.pTempPathContext) + { + res.pTempPathContext = new CTempPathContext; + for (int index = 0; index < res.files.GetSize(); index++) + res.pTempPathContext->m_strDisplayRoot[index] = res.files[index]; + } + path = env_GetTempChildPath(); + do + { + if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[2].c_str(), path.c_str()))) + break;; + if (res.files[2].find(path) == 0) + { + VERIFY(::DeleteFile(res.files[2].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[2].c_str())), false)); + } + BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str()); + res.files[2] = OLE2T(pTmp); + SysFreeString(pTmp); + res.files[2].insert(0, _T("\\")); + res.files[2].insert(0, path); + } while (piHandler = ArchiveGuessFormat(res.files[2].c_str())); + res.files[2] = path; + } + if (res.files[1].empty()) + { + // assume Perry style patch + res.files[1] = path; + res.files[0] += _T("\\ORIGINAL"); + res.files[1] += _T("\\ALTERED"); + if (!PathFileExists(res.files[0].c_str()) || !PathFileExists(res.files[1].c_str())) + { + // not a Perry style patch: diff with itself... + res.files[0] = res.files[1] = path; + } + else + { + res.pTempPathContext->m_strDisplayRoot[0] += _T("\\ORIGINAL"); + res.pTempPathContext->m_strDisplayRoot[1] += _T("\\ALTERED"); + } + } + } + catch (CException *e) + { + e->ReportError(MB_ICONSTOP); + e->Delete(); + } + return res; +} + + diff --cc Src/DirActions.cpp index ca7c1a9a1,1bfaafb8a..018c13adb --- a/Src/DirActions.cpp +++ b/Src/DirActions.cpp @@@ -346,31 -1127,53 +346,31 @@@ bool IsItemCopyable(const DIFFITEM & di if (di.diffcode.isResultError()) return false; // can't copy same items if (di.diffcode.isResultSame()) return false; - // impossible if only on left - if (di.diffcode.isSideFirstOnly()) return false; - - // everything else can be copied to left + // impossible if not existing - if (!di.diffcode.isExists(index)) return false; ++ if (!di.diffcode.exists(index)) return false; + // everything else can be copied to other side return true; } -/// is it possible to copy item to right ? -bool CDirView::IsItemCopyableToRight(const DIFFITEM & di) const -{ - // don't let them mess with error items - if (di.diffcode.isResultError()) return false; - // can't copy same items - if (di.diffcode.isResultSame()) return false; - // impossible if only on right - if (di.diffcode.isSideSecondOnly()) return false; - // everything else can be copied to right - return true; -} -/// is it possible to delete left item ? -bool CDirView::IsItemDeletableOnLeft(const DIFFITEM & di) const +/// is it possible to delete item ? +bool IsItemDeletable(const DIFFITEM & di, int index) { // don't let them mess with error items if (di.diffcode.isResultError()) return false; - // impossible if only on right - if (di.diffcode.isSideSecondOnly()) return false; - // everything else can be deleted on left + // impossible if not existing - if (!di.diffcode.isExists(index)) return false; ++ if (!di.diffcode.exists(index)) return false; + // everything else can be deleted return true; } -/// is it possible to delete right item ? -bool CDirView::IsItemDeletableOnRight(const DIFFITEM & di) const -{ - // don't let them mess with error items - if (di.diffcode.isResultError()) return false; - // impossible if only on right - if (di.diffcode.isSideFirstOnly()) return false; - // everything else can be deleted on right - return true; -} /// is it possible to delete both items ? -bool CDirView::IsItemDeletableOnBoth(const DIFFITEM & di) const +bool IsItemDeletableOnBoth(const CDiffContext& ctxt, const DIFFITEM & di) { // don't let them mess with error items if (di.diffcode.isResultError()) return false; // impossible if only on right or left - if (di.diffcode.isSideFirstOnly() || di.diffcode.isSideSecondOnly()) return false; + for (int i = 0; i < ctxt.GetCompareDirs(); ++i) - if (!di.diffcode.isExists(i)) return false; ++ if (!di.diffcode.exists(i)) return false; // everything else can be deleted on both return true; @@@ -505,26 -1312,39 +505,26 @@@ bool AreItemsOpenable(const CDiffContex return false; } -/// is it possible to open left item ? -bool CDirView::IsItemOpenableOnLeft(const DIFFITEM & di) const +/// is it possible to open item ? +bool IsItemOpenableOn(const DIFFITEM & di, int index) { - // impossible if only on right - if (di.diffcode.isSideSecondOnly()) return false; + // impossible if not existing - if (!di.diffcode.isExists(index)) return false; ++ if (!di.diffcode.exists(index)) return false; // everything else can be opened on right return true; } -/// is it possible to open right item ? -bool CDirView::IsItemOpenableOnRight(const DIFFITEM & di) const -{ - // impossible if only on left - if (di.diffcode.isSideFirstOnly()) return false; - // everything else can be opened on left - return true; -} /// is it possible to open left ... item ? -bool CDirView::IsItemOpenableOnLeftWith(const DIFFITEM & di) const -{ - return (!di.diffcode.isDirectory() && IsItemOpenableOnLeft(di)); -} -/// is it possible to open with ... right item ? -bool CDirView::IsItemOpenableOnRightWith(const DIFFITEM & di) const +bool IsItemOpenableOnWith(const DIFFITEM & di, int index) { - return (!di.diffcode.isDirectory() && IsItemOpenableOnRight(di)); + return (!di.diffcode.isDirectory() && IsItemOpenableOn(di, index)); } /// is it possible to copy to... left item? -bool CDirView::IsItemCopyableToOnLeft(const DIFFITEM & di) const +bool IsItemCopyableToOn(const DIFFITEM & di, int index) { // impossible if only on right - if (!di.diffcode.isExists(index)) return false; - if (di.diffcode.isSideSecondOnly()) return false; ++ if (!di.diffcode.exists(index)) return false; // everything else can be copied to from left return true; @@@ -543,374 -1359,69 +543,374 @@@ bool IsItemNavigableDiff(const CDiffCon return true; } -/// get the file names on both sides for first selected item -bool CDirView::GetSelectedFileNames(String& strLeft, String& strRight) const +bool IsItemExistAll(const CDiffContext& ctxt, const DIFFITEM & di) { - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) + // Not a valid diffitem, one of special items (e.g "..") + if (di.diffcode.diffcode == 0) return false; - GetItemFileNames(sel, strLeft, strRight); + if (ctxt.GetCompareDirs() == 2) + return di.diffcode.isSideBoth(); + else + return di.diffcode.isSideAll(); +} + + +/** + * @brief Determines if the user wants to see given item. + * This function determines what items to show and what items to hide. There + * are lots of combinations, but basically we check if menuitem is enabled or + * disabled and show/hide matching items. For non-recursive compare we never + * hide folders as that would disable user browsing into them. And we even + * don't really know if folders are identical or different as we haven't + * compared them. + * @param [in] di Item to check. + * @return true if item should be shown, false if not. + * @sa CDirDoc::Redisplay() + */ +bool IsShowable(const CDiffContext& ctxt, const DIFFITEM & di, const DirViewFilterSettings& filter) +{ + if (di.customFlags1 & ViewCustomFlags::HIDDEN) + return false; + + if (di.diffcode.isResultFiltered()) + { + // Treat SKIPPED as a 'super'-flag. If item is skipped and user + // wants to see skipped items show item regardless of other flags + return filter.show_skipped; + } + + if (di.diffcode.isDirectory()) + { + // Subfolders in non-recursive compare can only be skipped or unique + if (!ctxt.m_bRecursive) + { + // left/right filters + if (di.diffcode.isSideFirstOnly() && !filter.show_unique_left) + return false; + if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right) + return false; + + // result filters + if (di.diffcode.isResultError() && FALSE/* !GetMainFrame()->m_bShowErrors FIXME:*/) + return false; + } + else // recursive mode (including tree-mode) + { + // left/right filters + if (di.diffcode.isSideFirstOnly() && !filter.show_unique_left) + return false; + if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right) + return false; + + // ONLY filter folders by result (identical/different) for tree-view. + // In the tree-view we show subfolders with identical/different + // status. The flat view only shows files inside folders. So if we + // filter by status the files inside folder are filtered too and + // users see files appearing/disappearing without clear logic. + if (filter.tree_mode) + { + // result filters + if (di.diffcode.isResultError() && FALSE/* !GetMainFrame()->m_bShowErrors FIXME:*/) + return false; + + // result filters + if (di.diffcode.isResultSame() && !filter.show_identical) + return false; + if (di.diffcode.isResultDiff() && !filter.show_different) + return false; + } + } + } + else + { + // left/right filters + if (di.diffcode.isSideFirstOnly() && !filter.show_unique_left) + return false; + if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right) + return false; + + // file type filters + if (di.diffcode.isBin() && !filter.show_binaries) + return false; + + // result filters + if (di.diffcode.isResultSame() && !filter.show_identical) + return false; + if (di.diffcode.isResultError() /* && !GetMainFrame()->m_bShowErrors FIXME:*/) + return false; + if (di.diffcode.isResultDiff() && !filter.show_different) + return false; + } return true; } -/// get file name on specified side for first selected item -String CDirView::GetSelectedFileName(SIDE_TYPE stype) const + +/** + * @brief Open one selected item. + * @param [in] pos1 Item position. + * @param [in,out] di1 Pointer to first diffitem. + * @param [in,out] di2 Pointer to second diffitem. + * @param [in,out] di3 Pointer to third diffitem. + * @param [out] paths First/Second/Third paths. + * @param [out] sel1 Item's selection index in listview. + * @param [in,out] isDir Is item folder? + * return false if there was error or item was completely processed. + */ +bool GetOpenOneItem(const CDiffContext& ctxt, uintptr_t pos1, const DIFFITEM *pdi[3], + PathContext & paths, int & sel1, bool & isdir, int nPane[3], String& errmsg) { - String left, right; - if (!GetSelectedFileNames(left, right)) return _T(""); - return stype==SIDE_LEFT ? left : right; + pdi[0] = &ctxt.GetDiffAt(pos1); + pdi[1] = pdi[0]; + pdi[2] = pdi[0]; + + paths = GetItemFileNames(ctxt, *pdi[0]); + + for (int nIndex = 0; nIndex < paths.GetSize(); ++nIndex) + nPane[nIndex] = nIndex; + + if (pdi[0]->diffcode.isDirectory()) + isdir = true; + - if (isdir && (pdi[0]->diffcode.isExistsFirst() && pdi[1]->diffcode.isExistsSecond() && pdi[2]->diffcode.isExistsThird())) ++ if (isdir && (pdi[0]->diffcode.existsFirst() && pdi[1]->diffcode.existsSecond() && pdi[2]->diffcode.existsThird())) + { + // Check both folders exist. If either folder is missing that means + // folder has been changed behind our back, so we just tell user to + // refresh the compare. + PATH_EXISTENCE path1Exists = paths_DoesPathExist(paths[0]); + PATH_EXISTENCE path2Exists = paths_DoesPathExist(paths[1]); + if (path1Exists != IS_EXISTING_DIR || path2Exists != IS_EXISTING_DIR) + { + String invalid = path1Exists == IS_EXISTING_DIR ? paths[0] : paths[1]; + errmsg = string_format_string1( + _("Operation aborted!\n\nFolder contents at disks has changed, path\n%1\nwas not found.\n\nPlease refresh the compare."), + invalid); + return false; + } + } + + return true; } + /** - * @brief Get the file names on both sides for specified item. - * @note Return empty strings if item is special item. + * @brief Open two selected items. + * @param [in] pos1 First item position. + * @param [in] pos2 Second item position. + * @param [in,out] di1 Pointer to first diffitem. + * @param [in,out] di2 Pointer to second diffitem. + * @param [out] paths First/Second/Third paths. + * @param [out] sel1 First item's selection index in listview. + * @param [out] sel2 Second item's selection index in listview. + * @param [in,out] isDir Is item folder? + * return false if there was error or item was completely processed. */ -void CDirView::GetItemFileNames(int sel, String& strLeft, String& strRight) const +bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, uintptr_t pos1, uintptr_t pos2, const DIFFITEM *pdi[3], + PathContext & paths, int & sel1, int & sel2, bool & isDir, int nPane[3], String& errmsg) { - UINT_PTR diffpos = GetItemKey(sel); - if (diffpos == (UINT_PTR)SPECIAL_ITEM_POS) + String pathLeft, pathRight; + + // Two items selected, get their info + pdi[0] = &ctxt.GetDiffAt(pos1); + pdi[1] = &ctxt.GetDiffAt(pos2); + nPane[0] = 0; + nPane[1] = 1; + + // Check for binary & side compatibility & file/dir compatibility + if (!AreItemsOpenable(ctxt, selectionType, *pdi[0], *pdi[1])) { - strLeft.erase(); - strRight.erase(); + return false; } - else + + String temp; + switch (selectionType) { - const DIFFITEM & di = GetDocument()->GetDiffByKey(diffpos); - const String leftrelpath = di.diffFileInfo[0].GetFile(); - const String rightrelpath = di.diffFileInfo[1].GetFile(); - const String & leftpath = GetDocument()->GetBasePath(0); - const String & rightpath = GetDocument()->GetBasePath(1); - strLeft = paths_ConcatPath(leftpath, leftrelpath); - strRight = paths_ConcatPath(rightpath, rightrelpath); + case SELECTIONTYPE_NORMAL: + // Ensure that di1 is on left (swap if needed) + if (pdi[0]->diffcode.isSideSecondOnly() || (pdi[0]->diffcode.isSideBoth() && + pdi[1]->diffcode.isSideFirstOnly())) + { + std::swap(pdi[0], pdi[1]); + std::swap(sel1, sel2); + } + break; + case SELECTIONTYPE_LEFT1LEFT2: + nPane[0] = nPane[1] = 0; + break; + case SELECTIONTYPE_RIGHT1RIGHT2: + nPane[0] = nPane[1] = 1; + break; + case SELECTIONTYPE_LEFT1RIGHT2: + break; + case SELECTIONTYPE_LEFT2RIGHT1: + std::swap(pdi[0], pdi[1]); + std::swap(sel1, sel2); + break; + } + + PathContext files1, files2; + files1 = GetItemFileNames(ctxt, *pdi[0]); + files2 = GetItemFileNames(ctxt, *pdi[1]); + paths.SetLeft(files1[nPane[0]]); + paths.SetRight(files2[nPane[1]]); + + if (pdi[0]->diffcode.isDirectory()) + { + isDir = true; + if (GetPairComparability(paths) != IS_EXISTING_DIR) + { + errmsg = _("The selected folder is invalid."); + return false; + } } + + return true; } /** - * @brief Get the file names on both sides for specified item. - * @note Return empty strings if item is special item. + * @brief Open three selected items. + * @param [in] pos1 First item position. + * @param [in] pos2 Second item position. + * @param [in] pos3 Third item position. + * @param [in,out] di1 Pointer to first diffitem. + * @param [in,out] di2 Pointer to second diffitem. + * @param [in,out] di3 Pointer to third diffitem. + * @param [out] paths First/Second/Third paths. + * @param [out] sel1 First item's selection index in listview. + * @param [out] sel2 Second item's selection index in listview. + * @param [out] sel3 Third item's selection index in listview. + * @param [in,out] isDir Is item folder? + * return false if there was error or item was completely processed. */ -void CDirView::GetItemFileNames(int sel, PathContext * paths) const +bool GetOpenThreeItems(const CDiffContext& ctxt, uintptr_t pos1, uintptr_t pos2, uintptr_t pos3, const DIFFITEM *pdi[3], + PathContext & paths, int & sel1, int & sel2, int & sel3, bool & isDir, int nPane[3], String& errmsg) { - String strPath[3]; - UINT_PTR diffpos = GetItemKey(sel); - if (diffpos == SPECIAL_ITEM_POS) + String pathLeft, pathMiddle, pathRight; + + // FIXME: + for (int nIndex = 0; nIndex < 3; ++nIndex) + nPane[nIndex] = nIndex; + if (!pos3) { - for (int nIndex = 0; nIndex < GetDocument()->m_nDirs; nIndex++) - paths->SetPath(nIndex, _T("")); + // Two items selected, get their info + pdi[0] = &ctxt.GetDiffAt(pos1); + pdi[1] = &ctxt.GetDiffAt(pos2); + + // Check for binary & side compatibility & file/dir compatibility + if (!::AreItemsOpenable(ctxt, *pdi[0], *pdi[1], *pdi[1]) && + !::AreItemsOpenable(ctxt, *pdi[0], *pdi[0], *pdi[1])) + { + return false; + } + // Ensure that pdi[0] is on left (swap if needed) - if (pdi[0]->diffcode.isExists(0) && pdi[0]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(2)) ++ if (pdi[0]->diffcode.exists(0) && pdi[0]->diffcode.exists(1) && pdi[1]->diffcode.exists(2)) + { + pdi[2] = pdi[1]; + pdi[1] = pdi[0]; + sel3 = sel2; + sel2 = sel1; + } - else if (pdi[0]->diffcode.isExists(0) && pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(1)) ++ else if (pdi[0]->diffcode.exists(0) && pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(1)) + { + pdi[2] = pdi[0]; + sel3 = sel1; + } - else if (pdi[0]->diffcode.isExists(1) && pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(0)) ++ else if (pdi[0]->diffcode.exists(1) && pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(0)) + { + std::swap(pdi[0], pdi[1]); + std::swap(sel1, sel2); + pdi[2] = pdi[1]; + sel3 = sel2; + } - else if (pdi[1]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(1) && pdi[0]->diffcode.isExists(2)) ++ else if (pdi[1]->diffcode.exists(0) && pdi[1]->diffcode.exists(1) && pdi[0]->diffcode.exists(2)) + { + std::swap(pdi[0], pdi[1]); + std::swap(sel1, sel2); + pdi[2] = pdi[1]; + pdi[1] = pdi[0]; + sel3 = sel2; + sel2 = sel1; + } - else if (pdi[1]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(2) && pdi[0]->diffcode.isExists(1)) ++ else if (pdi[1]->diffcode.exists(0) && pdi[1]->diffcode.exists(2) && pdi[0]->diffcode.exists(1)) + { + std::swap(pdi[0], pdi[1]); + std::swap(sel1, sel2); + pdi[2] = pdi[0]; + sel3 = sel1; + } - else if (pdi[1]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(2) && pdi[0]->diffcode.isExists(0)) ++ else if (pdi[1]->diffcode.exists(1) && pdi[1]->diffcode.exists(2) && pdi[0]->diffcode.exists(0)) + { + pdi[2] = pdi[1]; + sel3 = sel2; + } } else { - const DIFFITEM & di = GetDocument()->GetDiffByKey(diffpos); - for (int nIndex = 0; nIndex < GetDocument()->m_nDirs; nIndex++) + // Three items selected, get their info + pdi[0] = &ctxt.GetDiffAt(pos1); + pdi[1] = &ctxt.GetDiffAt(pos2); + pdi[2] = &ctxt.GetDiffAt(pos3); + + // Check for binary & side compatibility & file/dir compatibility + if (!::AreItemsOpenable(ctxt, *pdi[0], *pdi[1], *pdi[2])) + { + return false; + } + // Ensure that pdi[0] is on left (swap if needed) - if (pdi[0]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(1) && pdi[2]->diffcode.isExists(2)) ++ if (pdi[0]->diffcode.exists(0) && pdi[1]->diffcode.exists(1) && pdi[2]->diffcode.exists(2)) + { + } - else if (pdi[0]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(2) && pdi[2]->diffcode.isExists(1)) ++ else if (pdi[0]->diffcode.exists(0) && pdi[1]->diffcode.exists(2) && pdi[2]->diffcode.exists(1)) { - const String relpath = di.diffFileInfo[nIndex].GetFile(); - const String & path = GetDocument()->GetBasePath(nIndex); - paths->SetPath(nIndex, paths_ConcatPath(path, relpath)); + std::swap(pdi[1], pdi[2]); + std::swap(sel2, sel3); } - else if (pdi[0]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(0) && pdi[2]->diffcode.isExists(2)) ++ else if (pdi[0]->diffcode.exists(1) && pdi[1]->diffcode.exists(0) && pdi[2]->diffcode.exists(2)) + { + std::swap(pdi[0], pdi[1]); + std::swap(sel1, sel2); + } - else if (pdi[0]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(2) && pdi[2]->diffcode.isExists(0)) ++ else if (pdi[0]->diffcode.exists(1) && pdi[1]->diffcode.exists(2) && pdi[2]->diffcode.exists(0)) + { + std::swap(pdi[0], pdi[2]); + std::swap(sel1, sel3); + std::swap(pdi[1], pdi[2]); + std::swap(sel2, sel3); + } - else if (pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(0) && pdi[2]->diffcode.isExists(1)) ++ else if (pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(0) && pdi[2]->diffcode.exists(1)) + { + std::swap(pdi[0], pdi[1]); + std::swap(sel1, sel2); + std::swap(pdi[1], pdi[2]); + std::swap(sel2, sel3); + } - else if (pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(1) && pdi[2]->diffcode.isExists(0)) ++ else if (pdi[0]->diffcode.exists(2) && pdi[1]->diffcode.exists(1) && pdi[2]->diffcode.exists(0)) + { + std::swap(pdi[0], pdi[2]); + std::swap(sel1, sel3); + } + } + + // Fill in pathLeft & & pathMiddle & pathRight + PathContext pathsTemp = GetItemFileNames(ctxt, *pdi[0]); + pathLeft = pathsTemp[0]; + pathsTemp = GetItemFileNames(ctxt, *pdi[1]); + pathMiddle = pathsTemp[1]; + pathsTemp = GetItemFileNames(ctxt, *pdi[2]); + pathRight = pathsTemp[2]; + + paths.SetLeft(pathLeft.c_str()); + paths.SetMiddle(pathMiddle.c_str()); + paths.SetRight(pathRight.c_str()); + + if (pdi[0]->diffcode.isDirectory()) + { + isDir = true; + if (GetPairComparability(paths) != IS_EXISTING_DIR) + { + errmsg = _("The selected folder is invalid."); + return false; + } } + + return true; } /** @@@ -945,64 -1471,25 +945,64 @@@ PathContext GetItemFileNames(const CDif } /** - * @brief Apply specified setting for prediffing to all selected items + * @brief Return image index appropriate for this row */ -void CDirView::ApplyPluginPrediffSetting(int newsetting) +int GetColImage(const CDiffContext&ctxt, const DIFFITEM & di) { - // Unlike other group actions, here we don't build an action list - // to execute; we just apply this change directly - int sel=-1; - String slFile, srFile; - while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) + // Must return an image index into image list created above in OnInitDialog + if (di.diffcode.isResultError()) + return DIFFIMG_ERROR; + if (di.diffcode.isResultAbort()) + return DIFFIMG_ABORT; + if (di.diffcode.isResultFiltered()) + return (di.diffcode.isDirectory() ? DIFFIMG_DIRSKIP : DIFFIMG_SKIP); + if (di.diffcode.isSideFirstOnly()) + return (di.diffcode.isDirectory() ? DIFFIMG_LDIRUNIQUE : DIFFIMG_LUNIQUE); + if (di.diffcode.isSideSecondOnly()) + return (ctxt.GetCompareDirs() < 3 ? + (di.diffcode.isDirectory() ? DIFFIMG_RDIRUNIQUE : DIFFIMG_RUNIQUE) : + (di.diffcode.isDirectory() ? DIFFIMG_MDIRUNIQUE : DIFFIMG_MUNIQUE)); + if (di.diffcode.isSideThirdOnly()) + return (di.diffcode.isDirectory() ? DIFFIMG_RDIRUNIQUE : DIFFIMG_RUNIQUE); + if (ctxt.GetCompareDirs() == 3) + { - if (!di.diffcode.isExists(0)) ++ if (!di.diffcode.exists(0)) + return (di.diffcode.isDirectory() ? DIFFIMG_LDIRMISSING : DIFFIMG_LMISSING); - if (!di.diffcode.isExists(1)) ++ if (!di.diffcode.exists(1)) + return (di.diffcode.isDirectory() ? DIFFIMG_MDIRMISSING : DIFFIMG_MMISSING); - if (!di.diffcode.isExists(2)) ++ if (!di.diffcode.exists(2)) + return (di.diffcode.isDirectory() ? DIFFIMG_RDIRMISSING : DIFFIMG_RMISSING); + } + if (di.diffcode.isResultSame()) { - const DIFFITEM& di = GetDiffItem(sel); - if (!di.diffcode.isDirectory() && !di.diffcode.isSideFirstOnly() && - !di.diffcode.isSideSecondOnly()) + if (di.diffcode.isDirectory()) + return DIFFIMG_DIRSAME; + else + { + if (di.diffcode.isText()) + return DIFFIMG_TEXTSAME; + else if (di.diffcode.isBin()) + return DIFFIMG_BINSAME; + else + return DIFFIMG_SAME; + } + } + // diff + if (di.diffcode.isResultDiff()) + { + if (di.diffcode.isDirectory()) + return DIFFIMG_DIRDIFF; + else { - GetItemFileNames(sel, slFile, srFile); - String filteredFilenames = slFile + _T("|") + srFile; - GetDocument()->SetPluginPrediffSetting(filteredFilenames, newsetting); + if (di.diffcode.isText()) + return DIFFIMG_TEXTDIFF; + else if (di.diffcode.isBin()) + return DIFFIMG_BINDIFF; + else + return DIFFIMG_DIFF; } } + return (di.diffcode.isDirectory() ? DIFFIMG_DIR : DIFFIMG_ABORT); } /** diff --cc Src/DirActions.h index 1f6e73279,000000000..4aa4167e0 mode 100644,000000..100644 --- a/Src/DirActions.h +++ b/Src/DirActions.h @@@ -1,792 -1,0 +1,792 @@@ +#pragma once + +#include "UnicodeString.h" +#include "DiffContext.h" +#include "FileActionScript.h" +#include "paths.h" +#include "IntToIntMap.h" +#include + +struct DIFFITEM; +class CDiffContext; +class PathContext; +class PluginManager; +class FileActionScript; +class CTempPathContext; + +/** + * @brief Folder compare icon indexes. + * This enum defines indexes for imagelist used for folder compare icons. + * Note that this enum must be in synch with code in OnInitialUpdate() and + * GetColImage(). Also remember that icons are in resource file... + */ +enum +{ + DIFFIMG_LUNIQUE, + DIFFIMG_MUNIQUE, + DIFFIMG_RUNIQUE, + DIFFIMG_LMISSING, + DIFFIMG_MMISSING, + DIFFIMG_RMISSING, + DIFFIMG_DIFF, + DIFFIMG_SAME, + DIFFIMG_BINSAME, + DIFFIMG_BINDIFF, + DIFFIMG_LDIRUNIQUE, + DIFFIMG_MDIRUNIQUE, + DIFFIMG_RDIRUNIQUE, + DIFFIMG_LDIRMISSING, + DIFFIMG_MDIRMISSING, + DIFFIMG_RDIRMISSING, + DIFFIMG_SKIP, + DIFFIMG_DIRSKIP, + DIFFIMG_DIRDIFF, + DIFFIMG_DIRSAME, + DIFFIMG_DIR, + DIFFIMG_ERROR, + DIFFIMG_DIRUP, + DIFFIMG_DIRUP_DISABLE, + DIFFIMG_ABORT, + DIFFIMG_TEXTDIFF, + DIFFIMG_TEXTSAME, +}; + +typedef enum { + SIDE_LEFT = 1, + SIDE_MIDDLE, + SIDE_RIGHT +} SIDE_TYPE; + +typedef enum { + SELECTIONTYPE_NORMAL, + SELECTIONTYPE_LEFT1LEFT2, + SELECTIONTYPE_RIGHT1RIGHT2, + SELECTIONTYPE_LEFT1RIGHT2, + SELECTIONTYPE_LEFT2RIGHT1 +} SELECTIONTYPE; + +typedef enum { + UPDATEITEM_NONE, + UPDATEITEM_UPDATE, + UPDATEITEM_REMOVE +} UPDATEITEM_TYPE; + +struct ViewCustomFlags +{ + enum + { + // We use extra bits so that no valid values are 0 + // and each set of flags is in a different hex digit + // to make debugging easier + // These can always be packed down in the future + INVALID_CODE = 0, + VISIBILITY = 0x3, VISIBLE = 0x1, HIDDEN = 0x2, EXPANDED = 0x4 + }; +}; + +struct AllowUpwardDirectory +{ + enum ReturnCode + { + Never, + No, + ParentIsRegularPath, + ParentIsTempPath + }; +}; + + +struct DirViewFilterSettings +{ + template + DirViewFilterSettings(GetOptionBool getoptbool) + { + show_skipped = getoptbool(OPT_SHOW_SKIPPED); + show_unique_left = getoptbool(OPT_SHOW_UNIQUE_LEFT); + show_unique_right = getoptbool(OPT_SHOW_UNIQUE_RIGHT); + show_binaries = getoptbool(OPT_SHOW_BINARIES); + show_identical = getoptbool(OPT_SHOW_IDENTICAL); + show_different = getoptbool(OPT_SHOW_DIFFERENT); + tree_mode = getoptbool(OPT_TREE_MODE); + }; + bool show_skipped; + bool show_unique_left; + bool show_unique_right; + bool show_binaries; + bool show_identical; + bool show_different; + bool tree_mode; +}; + +typedef std::map DirViewTreeState; + +String NumToStr(int n); +String FormatFilesAffectedString(int nFilesAffected, int nFilesTotal); +String FormatMenuItemString(SIDE_TYPE src, int count, int total); +String FormatMenuItemString(SIDE_TYPE src, SIDE_TYPE dst, int count, int total); +String FormatMenuItemStringBoth(int count, int total); +String FormatMenuItemString(const String& fmt1, const String& fmt2, int count, int total); +String FormatMenuItemStringTo(SIDE_TYPE src, int count, int total); + +void ConfirmActionList(const CDiffContext& ctxt, const FileActionScript & actionList); +UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContext& ctxt, DIFFITEM &di); + +uintptr_t FindItemFromPaths(const CDiffContext& ctxt, const String& pathLeft, const String& pathRight); + +bool IsItemCopyable(const DIFFITEM & di, int index); +bool IsItemDeletable(const DIFFITEM & di, int index); +bool IsItemDeletableOnBoth(const CDiffContext& ctxt, const DIFFITEM & di); +bool IsItemOpenable(const CDiffContext& ctxt, const DIFFITEM & di, bool treemode); +bool AreItemsOpenable(const CDiffContext& ctxt, SELECTIONTYPE selectionType, const DIFFITEM & di1, const DIFFITEM & di2); +bool AreItemsOpenable(const CDiffContext& ctxt, const DIFFITEM & di1, const DIFFITEM & di2, const DIFFITEM & di3); +bool IsItemOpenableOn(const DIFFITEM & di, int index); +bool IsItemOpenableOnWith(const DIFFITEM & di, int index); +bool IsItemCopyableToOn(const DIFFITEM & di, int index); +bool IsItemNavigableDiff(const CDiffContext& ctxt, const DIFFITEM & di); +bool IsItemExistAll(const CDiffContext& ctxt, const DIFFITEM & di); +bool IsShowable(const CDiffContext& ctxt, const DIFFITEM & di, const DirViewFilterSettings& filter); + +bool GetOpenOneItem(const CDiffContext& ctxt, uintptr_t pos1, const DIFFITEM *pdi[3], + PathContext &paths, int & sel1, bool & isDir, int nPane[3], String& errmsg); +bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, uintptr_t pos1, uintptr_t pos2, const DIFFITEM *pdi[3], + PathContext &paths, int & sel1, int & sel2, bool & isDir, int nPane[3], String& errmsg); +bool GetOpenThreeItems(const CDiffContext& ctxt, uintptr_t pos1, uintptr_t pos2, uintptr_t pos3, const DIFFITEM *pdi[3], + PathContext &paths, int & sel1, int & sel2, int & sel3, bool & isDir, int nPane[3], String& errmsg); + +void GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM& di, String& strLeft, String& strRight); +PathContext GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM& di); +String GetItemFileName(const CDiffContext& ctx, const DIFFITEM & di, int index); +int GetColImage(const CDiffContext&ctxt, const DIFFITEM & di); + +void SetDiffStatus(DIFFITEM& di, unsigned diffcode, unsigned mask); +void SetDiffCompare(DIFFITEM& di, unsigned diffcode); +void SetDiffSide(DIFFITEM& di, unsigned diffcode); +void SetDiffCounts(DIFFITEM& di, unsigned diffs, unsigned ignored); +void SetItemViewFlag(DIFFITEM& di, unsigned flag, unsigned mask); +void SetItemViewFlag(CDiffContext& ctxt, unsigned flag, unsigned mask); +void MarkForRescan(DIFFITEM& di); + +bool RenameOnSameDir(const String& szOldFileName, const String& szNewFileName); + +void ExpandSubdirs(CDiffContext& ctxt, DIFFITEM& dip); +void ExpandAllSubdirs(CDiffContext &ctxt); +void CollapseAllSubdirs(CDiffContext &ctxt); +DirViewTreeState *SaveTreeState(const CDiffContext& ctxt); +void RestoreTreeState(CDiffContext &ctxt, DirViewTreeState *pTreeState); + +AllowUpwardDirectory::ReturnCode +CheckAllowUpwardDirectory(const CDiffContext& ctxt, const CTempPathContext *pTempPathContext, PathContext &pathsParent); + +inline int SideToIndex(const CDiffContext& ctxt, SIDE_TYPE stype) +{ + switch (stype) + { + case SIDE_MIDDLE: return ctxt.GetCompareDirs() == 3 ? 1 : -1; + case SIDE_RIGHT: return ctxt.GetCompareDirs() - 1; + default: return 0; + } +} + +struct ConfirmationNeededException +{ + String m_caption; + String m_question; + String m_fromText; + String m_toText; + String m_fromPath; + String m_toPath; +}; + +struct ContentsChangedException +{ + ContentsChangedException(const String& failpath); + String m_msg; +}; + +struct DirActions +{ + typedef bool (DirActions::*method_type2)(const DIFFITEM& di) const; + typedef FileActionScript *(DirActions::*method_type)(FileActionScript *, const std::pair it) const; + + DirActions(const CDiffContext& ctxt, const bool RO[], method_type func = NULL, method_type2 func2 = NULL) : + m_ctxt(ctxt), m_RO(RO), m_cur_method(func), m_cur_method2(func2) {} + + template + bool IsItemCopyableOnTo(const DIFFITEM& di) const + { + return (di.diffcode.diffcode != 0 && !m_RO[SideToIndex(m_ctxt, dst)] && ::IsItemCopyable(di, SideToIndex(m_ctxt, src))); + } + + template + bool IsItemCopyableToOn(const DIFFITEM& di) const + { + return (di.diffcode.diffcode != 0 && ::IsItemCopyableToOn(di, SideToIndex(m_ctxt, src))); + } + + template + bool IsItemMovableToOn(const DIFFITEM& di) const + { + const int idx = SideToIndex(m_ctxt, src); + return (di.diffcode.diffcode != 0 && !m_RO[idx] && IsItemDeletable(di, idx) && ::IsItemCopyableToOn(di, idx)); + } + + template + bool IsItemDeletableOn(const DIFFITEM& di) const + { + const int idx = SideToIndex(m_ctxt, src); + return (di.diffcode.diffcode != 0 && !m_RO[idx] && IsItemDeletable(di, idx)); + } + bool IsItemDeletableOnBoth(const DIFFITEM& di) const + { + if (di.diffcode.diffcode != 0) + { + int i; + for (i = 0; i < m_ctxt.GetCompareDirs(); ++i) + { + if (m_RO[i] || !IsItemDeletable(di, i)) + break; + } + return (i == m_ctxt.GetCompareDirs()); + } + return false; + } + bool IsItemDeletableOnEitherOrBoth(const DIFFITEM& di) const + { + if (di.diffcode.diffcode != 0) + { + int i; + for (i = 0; i < m_ctxt.GetCompareDirs(); ++i) + { + if (!m_RO[i] && IsItemDeletable(di, i)) + break; + } + return (i < m_ctxt.GetCompareDirs()); + } + return false; + } + + template + bool IsItemOpenanbleOn(const DIFFITEM& di) const + { + return (di.diffcode.diffcode != 0 && IsItemOpenableOn(di, SideToIndex(m_ctxt, src))); + } + + template + bool IsItemOpenanbleOnWith(const DIFFITEM& di) const + { + return (di.diffcode.diffcode != 0 && IsItemOpenableOnWith(di, SideToIndex(m_ctxt, src))); + } + + bool IsItemFile(const DIFFITEM& di) const + { + return (di.diffcode.diffcode != 0 && di.diffcode.isDirectory()); + } + + template + bool IsItemExist(const DIFFITEM& di) const + { - return (di.diffcode.diffcode != 0 && di.diffcode.isExists(SideToIndex(m_ctxt, src))); ++ return (di.diffcode.diffcode != 0 && di.diffcode.exists(SideToIndex(m_ctxt, src))); + } + + template + bool IsItemEditableEncoding(const DIFFITEM& di) const + { + const int index = SideToIndex(m_ctxt, src); - return (di.diffcode.diffcode != 0 && di.diffcode.isExists(index) && di.diffFileInfo[index].IsEditableEncoding()); ++ return (di.diffcode.diffcode != 0 && di.diffcode.exists(index) && di.diffFileInfo[index].IsEditableEncoding()); + } + + bool IsItemNavigableDiff(const DIFFITEM& di) const + { + return ::IsItemNavigableDiff(m_ctxt, di); + } + + FileActionScript *CopyItem(FileActionScript *pscript, const std::pair& it, SIDE_TYPE src, SIDE_TYPE dst) const + { + const DIFFITEM& di = *it.second; + const int srcidx = SideToIndex(m_ctxt, src); + const int dstidx = SideToIndex(m_ctxt, dst); + if (di.diffcode.diffcode != 0 && !m_RO[dstidx] && IsItemCopyable(di, srcidx)) + { + FileActionItem act; + act.src = GetItemFileName(m_ctxt, di, srcidx); + act.dest = GetItemFileName(m_ctxt, di, dstidx); + + // We must check that paths still exists + if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST) + throw ContentsChangedException(act.src); + + act.context = it.first; + act.dirflag = di.diffcode.isDirectory(); + act.atype = FileAction::ACT_COPY; + act.UIResult = FileActionItem::UI_SYNC; + act.UIOrigin = srcidx; + act.UIDestination = dstidx; + pscript->AddActionItem(act); + } + return pscript; + } + + template + FileActionScript *Copy(FileActionScript *pscript, const std::pair it) const + { + return CopyItem(pscript, it, src, to); + } + + FileActionScript *DeleteItem(FileActionScript *pscript, const std::pair& it, SIDE_TYPE src) const + { + const DIFFITEM& di = *it.second; + const int index = SideToIndex(m_ctxt, src); + if (di.diffcode.diffcode != 0 && !m_RO[index] && IsItemDeletable(di, index)) + { + FileActionItem act; + act.src = GetItemFileName(m_ctxt, di, index); + + // We must check that path still exists + if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST) + throw ContentsChangedException(act.src); + + act.context = it.first; + act.dirflag = di.diffcode.isDirectory(); + act.atype = FileAction::ACT_DEL; + act.UIOrigin = index; + act.UIResult = FileActionItem::UI_DEL; + pscript->AddActionItem(act); + } + return pscript; + } + + template + FileActionScript *DeleteOn(FileActionScript *pscript, const std::pair it) const + { + return DeleteItem(pscript, it, src); + } + + FileActionScript *DeleteOnBoth(FileActionScript *pscript, const std::pair it) const + { + const DIFFITEM& di = *it.second; + + if (di.diffcode.diffcode != 0 && IsItemDeletableOnBoth(di) && + (std::count(m_RO, m_RO + m_ctxt.GetCompareDirs(), true) == 0)) + { + for (int i = 0; i < m_ctxt.GetCompareDirs(); ++i) + { + FileActionItem act; + act.src = GetItemFileName(m_ctxt, di, i); + // We must first check that paths still exists + if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST) + throw ContentsChangedException(act.src); + act.context = it.first; + act.dirflag = di.diffcode.isDirectory(); + act.atype = FileAction::ACT_DEL; + act.UIOrigin = i; + act.UIResult = FileActionItem::UI_DEL; + pscript->AddActionItem(act); + } + } + return pscript; + } + + FileActionScript *DeleteOnEitherOrBoth(FileActionScript *pscript, const std::pair it) const + { + const DIFFITEM& di = *it.second; + if (di.diffcode.diffcode != 0) + { + for (int i = 0; i < m_ctxt.GetCompareDirs(); ++i) + { + if (IsItemDeletable(di, i) && !m_RO[i]) + { + FileActionItem act; + act.src = GetItemFileName(m_ctxt, di, i); + act.UIResult = FileActionItem::UI_DEL; + act.dirflag = di.diffcode.isDirectory(); + act.context = it.first; + act.UIOrigin = i; + act.atype = FileAction::ACT_DEL; + pscript->AddActionItem(act); + } + } + } + return pscript; + } + + FileActionScript *CopyOrMoveItemTo(FileActionScript *pscript, const std::pair& it, FileAction::ACT_TYPE atype, SIDE_TYPE src) const + { + const int index = SideToIndex(m_ctxt, src); + const DIFFITEM& di = *it.second; + + if (di.diffcode.diffcode != 0 && IsItemCopyable(di, index) && + (atype == FileAction::ACT_MOVE ? (!m_RO[index] && IsItemDeletable(di, index)) : true)) + { + FileActionItem act; + act.src = GetItemFileName(m_ctxt, di, index); + + // We must check that path still exists + if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST) + throw ContentsChangedException(act.src); + + act.dest = paths_ConcatPath(pscript->m_destBase, di.diffFileInfo[index].filename); + act.dirflag = di.diffcode.isDirectory(); + act.context = it.first; + act.atype = atype; + act.UIResult = (atype == FileAction::ACT_COPY) ? FileActionItem::UI_DONT_CARE : FileActionItem::UI_DEL; + act.UIOrigin = index; + pscript->AddActionItem(act); + } + return pscript; + } + + template + FileActionScript *CopyTo(FileActionScript *pscript, const std::pair it) const + { + return CopyOrMoveItemTo(pscript, it, FileAction::ACT_COPY, src); + } + + template + FileActionScript *MoveTo(FileActionScript *pscript, const std::pair it) const + { + return CopyOrMoveItemTo(pscript, it, FileAction::ACT_MOVE, src); + } + + FileActionScript *operator()(FileActionScript *pscript, const std::pair it) const + { + return ((*this).*m_cur_method)(pscript, it); + } + + bool operator()(const DIFFITEM & di) const + { + return ((*this).*m_cur_method2)(di); + } + + method_type m_cur_method; + method_type2 m_cur_method2; + const CDiffContext& m_ctxt; + const bool *m_RO; +}; + +struct Counts { + Counts() : count(0), total(0) {} + Counts(int c, int t): count(c), total(t) {} + int count; + int total; +}; + +template +Counts Count(const InputIterator& begin, const InputIterator& end, const Predicate& pred) +{ + int count = 0, total = 0; + for (InputIterator it = begin; it != end; ++it) + { + if (pred(*it)) + ++count; + ++total; + } + return Counts(count, total); +} + +struct ContextMenuCounts { + int nTotal; // total #items (includes files & directories, either side) + int nCopyable[3]; + int nDeletable[3]; + int nDeletableOnBoth; + int nOpenable[3]; + int nOpenableOnBoth; + int nOpenableOnWith[3]; + int nDiffItems; +}; + +template +ContextMenuCounts CountForContextMenu(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt) +{ + ContextMenuCounts counts = {0}; + int nDirs = ctxt.GetCompareDirs(); + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + if (di.diffcode.diffcode == 0) // Invalid value, this must be special item + continue; + int nOpenablePerItem = 0; + for (int j = 0; j < nDirs; ++j) + { + if (IsItemCopyable(di, j)) + ++counts.nCopyable[j]; + if (IsItemDeletable(di, j)) + ++counts.nDeletable[j]; + if (IsItemOpenableOn(di, j)) + { + ++nOpenablePerItem; + ++counts.nOpenable[j]; + } + if (IsItemOpenableOnWith(di, j)) + ++counts.nOpenableOnWith[j]; + } + if (IsItemDeletableOnBoth(ctxt, di)) + ++counts.nDeletableOnBoth; + + if (IsItemNavigableDiff(ctxt, di)) + ++counts.nDiffItems; + + if (nOpenablePerItem == nDirs) + ++counts.nOpenableOnBoth; + + ++counts.nTotal; + } + return counts; +} + + +/** + * @brief Rename selected item on both left and right sides. + * + * @param szNewItemName [in] New item name. + * + * @return true if at least one file was renamed successfully. + */ +template +bool DoItemRename(InputIterator& it, const CDiffContext& ctxt, const String& szNewItemName) +{ + PathContext paths; + int nDirs = ctxt.GetCompareDirs(); + + assert(it != InputIterator()); + + // We must check that paths still exists + String failpath; + DIFFITEM &di = *it; + paths = ::GetItemFileNames(ctxt, di); + for (int i = 0; i < paths.GetSize(); ++i) + { + if (paths_DoesPathExist(paths[i]) == DOES_NOT_EXIST) + throw ContentsChangedException(failpath); + } + + bool bRename[3] = {false}; + int index; + for (index = 0; index < nDirs; index++) + { - if (di.diffcode.isExists(index)) ++ if (di.diffcode.exists(index)) + bRename[index] = RenameOnSameDir(paths[index], szNewItemName); + } + + int nSuccessCount = 0; + for (index = 0; index < nDirs; index++) + nSuccessCount += bRename[index] ? 1 : 0; + + if (nSuccessCount > 0) + { + for (index = 0; index < nDirs; index++) + { + if (bRename[index]) + di.diffFileInfo[index].filename = szNewItemName; + else + di.diffFileInfo[index].filename = _T(""); + } + } + + return (bRename[0] || bRename[1] || (nDirs > 2 && bRename[2])); +} + +template +OutputIterator CopyPathnames(const InputIterator& begin, const InputIterator& end, OutputIterator result, SIDE_TYPE stype, const CDiffContext& ctxt) +{ + const int index = SideToIndex(ctxt, stype); + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; - if (di.diffcode.isExists(index)) ++ if (di.diffcode.exists(index)) + { + *result = GetItemFileName(ctxt, di, index); + ++result; + } + } + return result; +} + +template +OutputIterator CopyBothPathnames(const InputIterator& begin, const InputIterator& end, OutputIterator result, const CDiffContext& ctxt) +{ + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + for (int i = 0; i < ctxt.GetCompareDirs(); ++i) + { - if (di.diffcode.isExists(i)) ++ if (di.diffcode.exists(i)) + { + *result = GetItemFileName(ctxt, di, i); + ++result; + } + } + } + return result; +} + +template +OutputIterator CopyFilenames(const InputIterator& begin, const InputIterator& end, OutputIterator result) +{ + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + if (!di.diffcode.isDirectory()) + { + *result = di.diffFileInfo[0].filename; + ++result; + } + } + return result; +} + +template +OutputIterator CopyPathnamesForDragAndDrop(const InputIterator& begin, const InputIterator& end, OutputIterator result, const CDiffContext& ctxt) +{ + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + + // check for special items (e.g not "..") + if (di.diffcode.diffcode == 0) + continue; + + if (!IsItemExistAll(ctxt, di) || di.diffcode.isResultDiff()) + { + for (int i = 0; i < ctxt.GetCompareDirs(); ++i) + { - if (di.diffcode.isExists(i)) ++ if (di.diffcode.exists(i)) + { + *result = GetItemFileName(ctxt, di, i); + ++result; + } + } + } + else + { + *result = GetItemFileName(ctxt, di, 0); + ++result; + } + } + return result; +} + +template +void ApplyFolderNameAndFileName(const InputIterator& begin, const InputIterator& end, SIDE_TYPE stype, + const CDiffContext& ctxt, BinaryFunction func) +{ + int index = SideToIndex(ctxt, stype); + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + if (di.diffcode.diffcode == 0) // Invalid value, this must be special item + continue; + String filename = di.diffFileInfo[index].filename; + String currentDir = di.getFilepath(index, ctxt.GetNormalizedPath(index)); + func(currentDir, filename); + } +} + +/** + * @brief Apply specified setting for prediffing to all selected items + */ +template +void ApplyPluginPrediffSetting(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt, int newsetting) +{ + // Unlike other group actions, here we don't build an action list + // to execute; we just apply this change directly + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + if (!di.diffcode.isDirectory()) + { + String filteredFilenames; + for (int i = 0; i < ctxt.GetCompareDirs(); ++i) + { - if (di.diffcode.isExists(i)) ++ if (di.diffcode.exists(i)) + { + if (!filteredFilenames.empty()) filteredFilenames += _T("|"); + filteredFilenames += ::GetItemFileName(ctxt, di, i); + } + } + PackingInfo * infoUnpacker = 0; + PrediffingInfo * infoPrediffer = 0; + const_cast(ctxt).FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer); + infoPrediffer->Initialize(newsetting); + } + } +} + +/** + * @brief Updates just before displaying plugin context view in list + */ +template +std::pair CountPredifferYesNo(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt) +{ + int nPredifferYes = 0; + int nPredifferNo = 0; + + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + if (di.diffcode.diffcode == 0) // Invalid value, this must be special item + continue; + + // note the prediffer flag for 'files present on both sides and not skipped' + if (!di.diffcode.isDirectory() && !di.diffcode.isBin() && IsItemExistAll(ctxt, di) + && !di.diffcode.isResultFiltered()) + { + PathContext files = GetItemFileNames(ctxt, di); + String filteredFilenames = string_join(files.begin(), files.end(), _T("|")); + PackingInfo * unpacker; + PrediffingInfo * prediffer; + const_cast(ctxt).FetchPluginInfos(filteredFilenames, &unpacker, &prediffer); + if (prediffer->bToBeScanned == 1 || prediffer->pluginName.empty() == false) + nPredifferYes ++; + else + nPredifferNo ++; + } + } + return std::make_pair(nPredifferYes, nPredifferNo); +} + +template +IntToIntMap CountCodepages(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt) +{ + IntToIntMap map; + for (InputIterator it = begin; it != end; ++it) + { + const DIFFITEM& di = *it; + for (int i = 0; i < ctxt.GetCompareDirs(); ++i) + { - if (di.diffcode.diffcode != 0 && di.diffcode.isExists(i)) ++ if (di.diffcode.diffcode != 0 && di.diffcode.exists(i)) + map.Increment(di.diffFileInfo[i].encoding.m_codepage); + } + } + return map; +} + +template +void ApplyCodepage(const InputIterator& begin, const InputIterator& end, CDiffContext& ctxt, const bool affect[3], int nCodepage) +{ + for (InputIterator it = begin; it != end; ++it) + { + DIFFITEM& di = *it; + if (di.diffcode.diffcode == 0) // Invalid value, this must be special item + continue; + if (di.diffcode.isDirectory()) + continue; + + for (int i = 0; i < ctxt.GetCompareDirs(); ++i) + { + // Does it exist on left? (ie, right or both) - if (affect[i] && di.diffcode.isExists(i) && di.diffFileInfo[i].IsEditableEncoding()) ++ if (affect[i] && di.diffcode.exists(i) && di.diffFileInfo[i].IsEditableEncoding()) + { + di.diffFileInfo[i].encoding.SetCodepage(nCodepage); + } + } + } +} + +/// get file name on specified side for first selected item +template +String GetSelectedFileName(InputIterator& it, SIDE_TYPE stype, const CDiffContext& ctxt) +{ + if (it == InputIterator()) + return _T(""); + return GetItemFileName(ctxt, *it, SideToIndex(ctxt, stype)); +} diff --cc Src/DirDoc.cpp index 1b0d6c857,cd849d91a..1781d8507 --- a/Src/DirDoc.cpp +++ b/Src/DirDoc.cpp @@@ -328,18 -489,27 +326,17 @@@ CDirView * CDirDoc::GetMainView() cons } /** - * @brief Update in-memory diffitem status from disk. - * @param [in] diffPos POSITION of item in UI list. - * @param [in] idx index to update. - */ -void CDirDoc::UpdateStatusFromDisk(uintptr_t diffPos, int idx) -{ - m_pCtxt->UpdateStatusFromDisk(diffPos, idx); -} - -/** * @brief Update in-memory diffitem status from disk and update view. * @param [in] diffPos POSITION of item in UI list. - * @param [in] bLeft If TRUE left-side item is updated. - * @param [in] bRight If TRUE right-side item is updated. + * @param [in] idx index to reload. * @note Do not call this function from DirView code! This function * calls slow DirView functions to get item position and to update GUI. * Use UpdateStatusFromDisk() function instead. */ - void CDirDoc::ReloadItemStatus(uintptr_t diffPos, int index) + void CDirDoc::ReloadItemStatus(uintptr_t diffPos, int idx) { // in case just copied (into existence) or modified - m_pCtxt->UpdateStatusFromDisk(diffPos, index); - UpdateStatusFromDisk(diffPos, idx); ++ m_pCtxt->UpdateStatusFromDisk(diffPos, idx); int nIdx = m_pDirView->GetItemIndex(diffPos); if (nIdx != -1) diff --cc Src/DirDoc.h index b1e99c322,c0691b3e7..a9e77e0a1 --- a/Src/DirDoc.h +++ b/Src/DirDoc.h @@@ -97,9 -97,13 +97,9 @@@ public void CompareReady(); void UpdateChangedItem(PathContext & paths, UINT nDiffs, UINT nTrivialDiffs, BOOL bIdentical); - uintptr_t FindItemFromPaths(const String& pathLeft, const String& pathRight); - void SetDiffSide(UINT diffcode, int idx); - void SetDiffCompare(UINT diffcode, int idx); void UpdateResources(); void InitStatusStrings(); - void ReloadItemStatus(uintptr_t diffPos, int index); - void UpdateStatusFromDisk(uintptr_t diffPos, int idx); + void ReloadItemStatus(uintptr_t diffPos, int idx); void Redisplay(); virtual ~CDirDoc(); void SetDirView( CDirView *newView ); // TODO Perry diff --cc Src/DirView.cpp index 5c144c3f3,47cec688e..476da3b85 --- a/Src/DirView.cpp +++ b/Src/DirView.cpp @@@ -1341,15 -1802,15 +1341,15 @@@ void CDirView::OpenSelection(SELECTIONT { theApp.m_strDescriptions[0].erase(); theApp.m_strDescriptions[1].erase(); - if (pdi[0] == pdi[1] && !pdi[0]->diffcode.isExists(0)) + if (pdi[0] == pdi[1] && !pdi[0]->diffcode.exists(0)) { paths[0] = _T(""); - theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE); + theApp.m_strDescriptions[0] = _("Untitled left"); } - if (pdi[0] == pdi[1] && !pdi[0]->diffcode.isExists(1)) + if (pdi[0] == pdi[1] && !pdi[0]->diffcode.exists(1)) { paths[1] = _T(""); - theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE); + theApp.m_strDescriptions[1] = _("Untitled right"); } } else @@@ -1357,20 -1818,20 +1357,20 @@@ theApp.m_strDescriptions[0].erase(); theApp.m_strDescriptions[1].erase(); theApp.m_strDescriptions[2].erase(); - if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.isExists(0)) + if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(0)) { paths[0] = _T(""); - theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE); + theApp.m_strDescriptions[0] = _("Untitled left"); } - if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.isExists(1)) + if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(1)) { paths[1] = _T(""); - theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_MIDDLE_FILE); + theApp.m_strDescriptions[1] = _("Untitled middle"); } - if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.isExists(2)) + if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.exists(2)) { paths[2] = _T(""); - theApp.m_strDescriptions[2] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE); + theApp.m_strDescriptions[2] = _("Untitled right"); } } @@@ -2926,10 -3965,10 +2923,11 @@@ afx_msg void CDirView::OnEndLabelEdit(N */ void CDirView::OnMarkedRescan() { - UINT items = MarkSelectedForRescan(); - if (items > 0) + std::for_each(SelBegin(), SelEnd(), MarkForRescan); + if (std::distance(SelBegin(), SelEnd()) > 0) { - m_pSavedTreeState.reset(SaveTreeState()); ++ m_pSavedTreeState.reset(SaveTreeState(GetDiffContext())); + GetDocument()->SetMarkedRescan(); GetDocument()->Rescan(); } } diff --cc Src/DirViewColItems.cpp index 5375cc42b,409e01443..556a47858 --- a/Src/DirViewColItems.cpp +++ b/Src/DirViewColItems.cpp @@@ -367,23 -313,23 +367,23 @@@ static String ColStatusGet(const CDiffC } else if (di.diffcode.isSideThirdOnly()) { - s = theApp.LoadString(IDS_RIGHT_ONLY_IN_FMT); - string_replace(s, _T("%1"), di.getFilepath(2, pCtxt->GetNormalizedRight())); + s = string_format_string1(_("Right only: %1"), + di.getFilepath(2, pCtxt->GetNormalizedRight())); } - else if (nDirs > 2 && !di.diffcode.isExistsFirst()) + else if (nDirs > 2 && !di.diffcode.existsFirst()) { - s = theApp.LoadString(IDS_DOES_NOT_EXIST_IN_FMT); - string_replace(s, _T("%1"), pCtxt->GetNormalizedLeft()); + s = string_format_string1(_("Does not exist in %1"), + pCtxt->GetNormalizedLeft()); } - else if (nDirs > 2 && !di.diffcode.isExistsSecond()) + else if (nDirs > 2 && !di.diffcode.existsSecond()) { - s = theApp.LoadString(IDS_DOES_NOT_EXIST_IN_FMT); - string_replace(s, _T("%1"), pCtxt->GetNormalizedMiddle()); + s = string_format_string1(_("Does not exist in %1"), + pCtxt->GetNormalizedMiddle()); } - else if (nDirs > 2 && !di.diffcode.isExistsThird()) + else if (nDirs > 2 && !di.diffcode.existsThird()) { - s = theApp.LoadString(IDS_DOES_NOT_EXIST_IN_FMT); - string_replace(s, _T("%1"), pCtxt->GetNormalizedRight()); + s = string_format_string1(_("Does not exist in %1"), + pCtxt->GetNormalizedRight()); } else if (di.diffcode.isResultSame()) { @@@ -619,19 -565,19 +619,19 @@@ static String ColStatusAbbrGet(const CD } else if (di.diffcode.isSideThirdOnly()) { - id = IDS_RIGHTONLY; + id = N_("Right Only"); } - else if (nDirs > 2 && !di.diffcode.isExistsFirst()) + else if (nDirs > 2 && !di.diffcode.existsFirst()) { - id = IDS_NOITEMLEFT; + id = N_("No item in left"); } - else if (nDirs > 2 && !di.diffcode.isExistsSecond()) + else if (nDirs > 2 && !di.diffcode.existsSecond()) { - id = IDS_NOITEMMIDDLE; + id = N_("No item in middle"); } - else if (nDirs > 2 && !di.diffcode.isExistsThird()) + else if (nDirs > 2 && !di.diffcode.existsThird()) { - id = IDS_NOITEMRIGHT; + id = N_("No item in right"); } else if (di.diffcode.isResultSame()) { diff --cc Translations/WinMerge/English.pot index 6c68a343e,f68140a95..7b0a02e4b --- a/Translations/WinMerge/English.pot +++ b/Translations/WinMerge/English.pot @@@ -8,7 -8,7 +8,7 @@@ msgid " msgstr "" "Project-Id-Version: WinMerge\n" "Report-Msgid-Bugs-To: http://bugs.winmerge.org/\n" - "POT-Creation-Date: 2015-02-13 21:36+0000\n" -"POT-Creation-Date: 2015-02-15 15:37+0000\n" ++"POT-Creation-Date: 2015-02-17 15:14+0000\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: English \n"