-/////////////////////////////////////////////////////////////////////////////
-// 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 <afxinet.h>
-#include <shlwapi.h>
-#include <paths.h>
-#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<Merge7z::Format *(*)(const String& path)> 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<class TYPE, size_t SIZE> 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<TCHAR,INTERNET_MAX_PATH_LENGTH> 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<TCHAR,2600> cText;
- CRawString<TCHAR,80> 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;
-}
-
+/////////////////////////////////////////////////////////////////////////////\r
+// WinMerge: an interactive diff/merge utility\r
+// Copyright (C) 1997-2000 Thingamahoochie Software\r
+// Author: Dean Grimm\r
+//\r
+// This program is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+/* 7zCommon.cpp: Implement 7z related classes and functions\r
+ * Copyright (c) 2003 Jochen Tucht\r
+ *\r
+ * Remarks: Different versions of 7-Zip are interfaced through specific\r
+ * versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.)\r
+ * WinMerge can either use an installed copy of the 7-Zip software,\r
+ * or fallback to a local set of 7-Zip DLLs, which are to be included\r
+ * in the WinMerge binary distribution.\r
+ *\r
+ * Fallback policies are as follows:\r
+ *\r
+ * 1. Detect 7-Zip version installed (XXX).\r
+ * 2. If there is a Merge7zXXX.dll, be happy to use it\r
+ * 3. Detect 7-Zip version from WinMerge distribution (YYY).\r
+ * 4. If there is a Merge7zYYY.dll, be happy to use it.\r
+ * 5. Sorry, no way.\r
+ *\r
+ * These rules can be customized by setting a registry variable\r
+ * *Merge7z/Enable* of type DWORD to one of the following values:\r
+ *\r
+ * 0 - Entirely disable 7-Zip integration.\r
+ * 1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip.\r
+ * 2 - Always use local 7-Zip.\r
+ *\r
+\r
+Please mind 2. a) of the GNU General Public License, and log your changes below.\r
+\r
+DATE: BY: DESCRIPTION:\r
+========== ================== ================================================\r
+2003-12-09 Jochen Tucht Created\r
+2003-12-16 Jochen Tucht Properly generate .tar.gz and .tar.bz2\r
+2003-12-16 Jochen Tucht Obtain long path to temporary folder\r
+2004-01-20 Jochen Tucht Complain only once if Merge7z*.dll is missing\r
+2004-01-25 Jochen Tucht Fix bad default for OPENFILENAME::nFilterIndex\r
+2004-03-15 Jochen Tucht Fix Visual Studio 2003 build issue\r
+2004-04-13 Jochen Tucht Avoid StrNCat to get away with shlwapi 4.70\r
+2004-08-25 Jochen Tucht More explicit error message\r
+2004-10-17 Jochen Tucht Leave decision whether to recurse into folders\r
+ to enumerator (Mask.Recurse)\r
+2004-11-03 Jochen Tucht FIX [1048997] as proposed by Kimmo 2004-11-02\r
+2005-01-15 Jochen Tucht Read 7-Zip version from 7zip_pad.xml\r
+ Set Merge7z UI language if DllBuild_Merge7z >= 9\r
+2005-01-22 Jochen Tucht Better explain what's present/missing/outdated\r
+2005-02-05 Jochen Tucht Fall back to IDD_MERGE7ZMISMATCH template from\r
+ .exe if .lang file isn't up to date.\r
+2005-02-26 Jochen Tucht Add download link to error message\r
+2005-02-26 Jochen Tucht Use WinAPI to obtain ISO language/region codes\r
+2005-02-27 Jochen Tucht FIX [1152375]\r
+2005-04-24 Kimmo Varis Don't use DiffContext exported from DirView\r
+2005-06-08 Kimmo Varis Use DIFFITEM, not reference to it (hopefully only\r
+ temporarily, to sort out new directory compare)\r
+2005-06-22 Jochen Tucht Change recommended version of 7-Zip to 4.20\r
+ Remove noise from Nagbox\r
+2005-07-03 Jochen Tucht DIFFITEM has changed due to RFE [ 1205516 ]\r
+2005-07-04 Jochen Tucht New global ArchiveGuessFormat() checks for\r
+ formats to be handled by external command line\r
+ tools. These take precedence over Merge7z\r
+ internal handlers.\r
+2005-07-05 Jochen Tucht Move to Merge7z::Format::GetDefaultName() to\r
+ build intermediate filenames for multi-step\r
+ compression.\r
+2005-07-15 Jochen Tucht Remove external command line tool integration\r
+ for now. Rethink about it after 2.4 branch.\r
+2005-08-20 Jochen Tucht Option to guess archive format by signature\r
+ Map extensions through ExternalArchiveFormat.ini\r
+2005-08-23 Jochen Tucht Option to entirely disable 7-Zip integration\r
+2007-01-04 Kimmo Varis Convert using COptionsMgr for options.\r
+2007-06-16 Jochen Neubeck FIX [1723263] "Zip --> Both" operation...\r
+2007-12-22 Jochen Neubeck Fix Merge7z UI lang for new translation system\r
+ Change recommended version of 7-Zip to 4.57\r
+2010-05-16 Jochen Neubeck Read 7-Zip version from 7z.dll (which has long\r
+ ago replaced the various format and codec DLLs)\r
+ Change recommended version of 7-Zip to 4.65\r
+*/\r
+\r
+// ID line follows -- this is updated by SVN\r
+// $Id: 7zCommon.cpp 7169 2010-05-16 14:44:19Z jtuc $\r
+\r
+#include "stdafx.h"\r
+#include "7zCommon.h"\r
+#include <afxinet.h>\r
+#include <shlwapi.h>\r
+#include "OptionsDef.h"\r
+#include "OptionsMgr.h"\r
+#include "Merge.h" // DirDocFilter theApp GetOptionsMgr()\r
+#include "resource.h"\r
+#include "DirView.h"\r
+#include "DirDoc.h"\r
+#include "DirActions.h"\r
+//#include "ExternalArchiveFormat.h"\r
+#include "version.h"\r
+#include "paths.h"\r
+#include "Environment.h"\r
+#include "Merge7zFormatRegister.h"\r
+\r
+#ifdef _DEBUG\r
+#define new DEBUG_NEW\r
+#undef THIS_FILE\r
+static char THIS_FILE[] = __FILE__;\r
+#endif\r
+\r
+/**\r
+ * @brief Proxy for Merge7z\r
+ */\r
+static __declspec(thread) Merge7z::Proxy m_Merge7z =\r
+{\r
+ { 0, 0, DllBuild_Merge7z, },\r
+ "Merge7z%u%02u"DECORATE_U".dll",\r
+ "Merge7z",\r
+ NULL\r
+};\r
+\r
+std::vector<Merge7z::Format *(*)(const String& path)> Merge7zFormatRegister::optionalFormats;\r
+\r
+/**\r
+ * @brief assign BSTR to String, and return BSTR for optional SysFreeString()\r
+ */\r
+inline BSTR Assign(CString &dst, BSTR src)\r
+{\r
+ dst = src;\r
+ return src;\r
+}\r
+\r
+bool IsArchiveFile(const String& pszFile)\r
+{\r
+ try {\r
+ Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile);\r
+ if (piHandler)\r
+ return TRUE;\r
+ else\r
+ return FALSE;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->Delete();\r
+ return FALSE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ * @brief Wrap Merge7z::GuessFormat() to allow for some customizing:\r
+ * - Check if 7-Zip integration is enabled.\r
+ * - Check for filename extension mappings.\r
+ */\r
+Merge7z::Format *ArchiveGuessFormat(const String& path)\r
+{\r
+ if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0)\r
+ return NULL;\r
+ if (paths_IsDirectory(path))\r
+ return NULL;\r
+ String path2(path);\r
+ // Map extensions through ExternalArchiveFormat.ini\r
+ static TCHAR null[] = _T("");\r
+ static const TCHAR section[] = _T("extensions");\r
+ String entry = paths_FindExtension(path);\r
+ TCHAR value[20];\r
+ static LPCTSTR filename = NULL;\r
+ if (filename == NULL)\r
+ {\r
+ TCHAR cPath[INTERNET_MAX_PATH_LENGTH];\r
+ DWORD cchPath = SearchPath(NULL, _T("ExternalArchiveFormat.ini"), NULL,\r
+ INTERNET_MAX_PATH_LENGTH, cPath, NULL);\r
+ filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? StrDup(cPath) : null;\r
+ }\r
+ if (*filename &&\r
+ GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) &&\r
+ *value == '.')\r
+ {\r
+ // Remove end-of-line comments (in string returned from GetPrivateProfileString)\r
+ // that is, remove semicolon & whatever follows it\r
+ if (LPTSTR p = StrChr(value, ';'))\r
+ {\r
+ *p = '\0';\r
+ StrTrim(value, _T(" \t"));\r
+ }\r
+ path2 = value;\r
+ }\r
+\r
+ // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues\r
+ // command line integration portion is not yet applied\r
+ // so following code not yet valid, so temporarily commented out\r
+ // Look for command line tool first\r
+ /*Merge7z::Format *pFormat;\r
+ if (CExternalArchiveFormat::GuessFormat(path, pFormat))\r
+ {\r
+ return pFormat;\r
+ }*/\r
+ // Default to Merge7z*.dll\r
+\r
+ try\r
+ {\r
+ Merge7z::Format *pFormat = m_Merge7z->GuessFormat(path2.c_str());\r
+ if (!pFormat)\r
+ pFormat = Merge7zFormatRegister::GuessFormat(path2);\r
+ return pFormat;\r
+ }\r
+ catch (...)\r
+ {\r
+ Merge7z::Format *pFormat = Merge7zFormatRegister::GuessFormat(path2);\r
+ if (pFormat)\r
+ return pFormat;\r
+ throw;\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Self-initializing raw C character buffer class.\r
+ */\r
+template<class TYPE, size_t SIZE> struct CRawString\r
+{\r
+ enum { Size = SIZE };\r
+ TYPE Data[SIZE];\r
+ CRawString()\r
+ {\r
+ Data[0] = 0;\r
+ }\r
+};\r
+\r
+/**\r
+ * @brief Exception class for more explicit error message.\r
+ */\r
+class C7ZipMismatchException : public CException\r
+{\r
+public:\r
+ C7ZipMismatchException(DWORD dwVer7zInstalled, DWORD dwVer7zLocal, CException *pCause)\r
+ {\r
+ m_dwVer7zInstalled = dwVer7zInstalled;\r
+ m_dwVer7zLocal = dwVer7zLocal;\r
+ m_pCause = pCause;\r
+ }\r
+ ~C7ZipMismatchException()\r
+ {\r
+ if (m_pCause)\r
+ m_pCause->Delete();\r
+ }\r
+ virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);\r
+protected:\r
+ DWORD m_dwVer7zInstalled;\r
+ DWORD m_dwVer7zLocal;\r
+ CException *m_pCause;\r
+ BOOL m_bShowAllways;\r
+ static const DWORD m_dwVer7zRecommended;\r
+ static const TCHAR m_strRegistryKey[];\r
+ static const TCHAR m_strDownloadURL[];\r
+ static INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);\r
+ static DWORD FormatVersion(LPTSTR, LPTSTR, DWORD);\r
+};\r
+\r
+/**\r
+ * @brief Recommended version of 7-Zip.\r
+ */\r
+const DWORD C7ZipMismatchException::m_dwVer7zRecommended = DWORD MAKELONG(65,4);\r
+\r
+/**\r
+ * @brief Registry key for C7ZipMismatchException's ReportError() popup.\r
+ */\r
+const TCHAR C7ZipMismatchException::m_strRegistryKey[] = _T("7ZipMismatch");\r
+\r
+/**\r
+ * @brief Download URL for C7ZipMismatchException's ReportError() popup.\r
+ */\r
+const TCHAR C7ZipMismatchException::m_strDownloadURL[] = _T("https://sourceforge.net/project/showfiles.php?group_id=13216&package_id=143957");\r
+\r
+/**\r
+ * @brief Retrieve build number of given DLL.\r
+ */\r
+static DWORD NTAPI GetDllBuild(LPCTSTR cPath)\r
+{\r
+ HMODULE hModule = LoadLibrary(cPath);\r
+ DLLVERSIONINFO dvi;\r
+ dvi.cbSize = sizeof dvi;\r
+ dvi.dwBuildNumber = ~0UL;\r
+ if (hModule)\r
+ {\r
+ dvi.dwBuildNumber = 0UL;\r
+ DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hModule, "DllGetVersion");\r
+ if (DllGetVersion)\r
+ {\r
+ DllGetVersion(&dvi);\r
+ }\r
+ FreeLibrary(hModule);\r
+ }\r
+ return dvi.dwBuildNumber;\r
+}\r
+\r
+/**\r
+ * @brief Format Merge7z version number and plugin name, and retrieve DllBuild.\r
+ */\r
+DWORD C7ZipMismatchException::FormatVersion(LPTSTR pcVersion, LPTSTR pcPluginName, DWORD dwVersion)\r
+{\r
+ *pcVersion = '\0';\r
+ *pcPluginName = '\0';\r
+ if (dwVersion)\r
+ {\r
+ wsprintf\r
+ (\r
+ pcVersion, _T("%u.%02u"),\r
+ UINT HIWORD(dwVersion),\r
+ UINT LOWORD(dwVersion)\r
+ );\r
+ wsprintf\r
+ (\r
+ pcPluginName,\r
+ sizeof(TCHAR) == 1 ? _T("Merge7z%u%02u.dll") : _T("Merge7z%u%02uU.dll"),\r
+ UINT HIWORD(dwVersion),\r
+ UINT LOWORD(dwVersion)\r
+ );\r
+ }\r
+ return GetDllBuild(pcPluginName);\r
+}\r
+\r
+/**\r
+ * @brief Populate ListBox with names/revisions of DLLs matching given pattern.\r
+ */\r
+static void NTAPI DlgDirListDLLs(HWND hWnd, LPTSTR cPattern, int nIDListBox)\r
+{\r
+ HDC hDC = GetDC(hWnd);\r
+ HFONT hFont = (HFONT)SendDlgItemMessage(hWnd, nIDListBox, WM_GETFONT, 0, 0);\r
+ int cxView = (int)SendDlgItemMessage(hWnd, nIDListBox, LB_GETHORIZONTALEXTENT, 0, 0) - 8;\r
+ HGDIOBJ hObject = SelectObject(hDC, hFont);\r
+ WIN32_FIND_DATA ff;\r
+ HANDLE h = FindFirstFile(cPattern, &ff);\r
+ if (h != INVALID_HANDLE_VALUE)\r
+ {\r
+ do\r
+ {\r
+ PathRemoveFileSpec(cPattern);\r
+ PathAppend(cPattern, ff.cFileName);\r
+ wsprintf(ff.cFileName, _T(" (dllbuild %04u)"), GetDllBuild(cPattern));\r
+ lstrcat(cPattern, ff.cFileName);\r
+ int cxText = (int)(WORD)GetTabbedTextExtent(hDC, cPattern, lstrlen(cPattern), 0, 0);\r
+ if (cxView < cxText)\r
+ cxView = cxText;\r
+ SendDlgItemMessage(hWnd, nIDListBox, LB_ADDSTRING, 0, (LPARAM)cPattern);\r
+ } while (FindNextFile(h, &ff));\r
+ FindClose(h);\r
+ }\r
+ SelectObject(hDC, hObject);\r
+ ReleaseDC(hWnd, hDC);\r
+ SendDlgItemMessage(hWnd, nIDListBox, LB_SETHORIZONTALEXTENT, cxView + 8, 0);\r
+}\r
+\r
+/**\r
+ * @brief OwnerDraw states from recent SDK.\r
+ */\r
+#define ODS_NOACCEL 0x0100\r
+#define ODS_NOFOCUSRECT 0x0200\r
+\r
+/**\r
+ * @brief WM_DRAWITEM notification handlers.\r
+ */\r
+struct CDrawItemStruct : DRAWITEMSTRUCT\r
+{\r
+ typedef CDrawItemStruct *From;\r
+ void DrawWebLinkButton();\r
+};\r
+\r
+void CDrawItemStruct::DrawWebLinkButton()\r
+{\r
+ CRawString<TCHAR,INTERNET_MAX_PATH_LENGTH> cText;\r
+ int cchText = ::GetWindowText(hwndItem, cText.Data, cText.Size);\r
+ COLORREF clrText = RGB(0,0,255);\r
+ if (::GetWindowLong(hwndItem, GWL_STYLE) & BS_LEFTTEXT)\r
+ {\r
+ clrText = RGB(128,0,128);\r
+ }\r
+ RECT rcText = rcItem;\r
+ ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT|DT_CALCRECT);\r
+ ::OffsetRect(&rcText, 1, 0);\r
+ rcItem.right = rcText.right + 1;\r
+ rcItem.bottom = rcText.bottom + 1;\r
+ switch (itemAction)\r
+ {\r
+ case ODA_DRAWENTIRE:\r
+ ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, 0, 0, 0);\r
+ ::SetBkMode(hDC, TRANSPARENT);\r
+ ::SetTextColor(hDC, clrText);\r
+ ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT);\r
+ rcText.top = rcText.bottom - 1;\r
+ ::SetBkColor(hDC, clrText);\r
+ ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0);\r
+ if (itemState & ODS_FOCUS)\r
+ {\r
+ case ODA_FOCUS:\r
+ if (!(itemState & ODS_NOFOCUSRECT))\r
+ {\r
+ ::SetTextColor(hDC, 0);\r
+ ::SetBkColor(hDC, RGB(255,255,255));\r
+ ::SetBkMode(hDC, OPAQUE);\r
+ DrawFocusRect(hDC, &rcItem);\r
+ }\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Load a cursor from COMCTL32.DLL.\r
+ */\r
+HCURSOR NTAPI CommCtrl_LoadCursor(LPCTSTR lpCursorName)\r
+{\r
+ HMODULE hModule = GetModuleHandle(_T("COMCTL32.DLL"));\r
+ return hModule ? LoadCursor(hModule, lpCursorName) : NULL;\r
+}\r
+\r
+/**\r
+ * @brief DLGPROC for C7ZipMismatchException's ReportError() popup.\r
+ */\r
+INT_PTR CALLBACK C7ZipMismatchException::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+{\r
+ switch (uMsg)\r
+ {\r
+ case WM_INITDIALOG:\r
+ {\r
+ theApp.TranslateDialog(hWnd);\r
+ if (GetDlgItem(hWnd, 9001) == NULL)\r
+ {\r
+ // Dialog template isn't up to date. Give it a second chance.\r
+ EndDialog(hWnd, -1);\r
+ return FALSE;\r
+ }\r
+ C7ZipMismatchException *pThis = (C7ZipMismatchException *)lParam;\r
+ CRawString<TCHAR,2600> cText;\r
+ CRawString<TCHAR,80> cPresent, cMissing, cOutdated, cNone, cPlugin;\r
+ if (pThis->m_pCause)\r
+ {\r
+ pThis->m_pCause->GetErrorMessage(cText.Data, cText.Size);\r
+ SetDlgItemText(hWnd, 107, cText.Data);\r
+ }\r
+ else\r
+ {\r
+ GetDlgItemText(hWnd, 107, cText.Data, cText.Size);\r
+ switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))\r
+ {\r
+ case 0:\r
+ lstrcat(cText.Data, _("\nNote: 7-Zip integration is disabled in WinMerge settings.").c_str());\r
+ break;\r
+ case 2:\r
+ lstrcat(cText.Data, _("\nNote: 7-Zip integration is restricted to standalone operation in WinMerge settings.").c_str());\r
+ break;\r
+ }\r
+ SetDlgItemText(hWnd, 107, cText.Data);\r
+ }\r
+ GetDlgItemText(hWnd, 112, cPresent.Data, cPresent.Size);\r
+ GetDlgItemText(hWnd, 122, cMissing.Data, cMissing.Size);\r
+ GetDlgItemText(hWnd, 132, cOutdated.Data, cOutdated.Size);\r
+ GetDlgItemText(hWnd, 120, cNone.Data, cNone.Size);\r
+ GetDlgItemText(hWnd, 102, cPlugin.Data, cPlugin.Size);\r
+ wsprintf(cText.Data, cPlugin.Data, DllBuild_Merge7z);\r
+ SetDlgItemText(hWnd, 102, cText.Data);\r
+ SetDlgItemText\r
+ (\r
+ hWnd, 109,\r
+ (\r
+ pThis->m_dwVer7zRecommended == pThis->m_dwVer7zInstalled\r
+ || pThis->m_dwVer7zRecommended == pThis->m_dwVer7zLocal\r
+ ) ? cPresent.Data : cMissing.Data\r
+ );\r
+ DWORD dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zRecommended);\r
+ SetDlgItemText(hWnd, 110, *cText.Data ? cText.Data : cNone.Data);\r
+ SetDlgItemText(hWnd, 111, cPlugin.Data);\r
+ SetDlgItemText(hWnd, 112, *cPlugin.Data == '\0' ? cPlugin.Data :\r
+ dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
+ dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zInstalled);\r
+ SetDlgItemText(hWnd, 120, *cText.Data ? cText.Data : cNone.Data);\r
+ SetDlgItemText(hWnd, 121, cPlugin.Data);\r
+ SetDlgItemText(hWnd, 122, *cPlugin.Data == '\0' ? cPlugin.Data :\r
+ dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
+ dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zLocal);\r
+ SetDlgItemText(hWnd, 130, *cText.Data ? cText.Data : cNone.Data);\r
+ SetDlgItemText(hWnd, 131, cPlugin.Data);\r
+ SetDlgItemText(hWnd, 132, *cPlugin.Data == '\0' ? cPlugin.Data :\r
+ dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
+ GetModuleFileName(0, cText.Data, MAX_PATH);\r
+ PathRemoveFileSpec(cText.Data);\r
+ PathAppend(cText.Data, _T("Merge7z*.dll"));\r
+ DlgDirListDLLs(hWnd, cText.Data, 105);\r
+ if (DWORD cchPath = GetEnvironmentVariable(_T("path"), 0, 0))\r
+ {\r
+ static const TCHAR cSep[] = _T(";");\r
+ LPTSTR pchPath = new TCHAR[cchPath];\r
+ GetEnvironmentVariable(_T("PATH"), pchPath, cchPath);\r
+ LPTSTR pchItem = pchPath;\r
+ while (int cchItem = StrCSpn(pchItem += StrSpn(pchItem, cSep), cSep))\r
+ {\r
+ if (cchItem < MAX_PATH)\r
+ {\r
+ CopyMemory(cText.Data, pchItem, cchItem*sizeof*pchItem);\r
+ cText.Data[cchItem] = 0;\r
+ PathAppend(cText.Data, _T("Merge7z*.dll"));\r
+ DlgDirListDLLs(hWnd, cText.Data, 105);\r
+ }\r
+ pchItem += cchItem;\r
+ }\r
+ delete[] pchPath;\r
+ }\r
+ if (SendDlgItemMessage(hWnd, 105, LB_GETCOUNT, 0, 0) == 0)\r
+ {\r
+ SendDlgItemMessage(hWnd, 105, LB_ADDSTRING, 0, (LPARAM) cNone.Data);\r
+ }\r
+ HICON hIcon = LoadIcon(0, IDI_EXCLAMATION);\r
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);\r
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);\r
+ if (pThis->m_bShowAllways)\r
+ {\r
+ ShowWindow(GetDlgItem(hWnd, 106), SW_HIDE);\r
+ }\r
+ } return TRUE;\r
+ case WM_DRAWITEM:\r
+ {\r
+ switch (wParam)\r
+ {\r
+ case 108:\r
+ CDrawItemStruct::From(lParam)->DrawWebLinkButton();\r
+ break;\r
+ }\r
+ } return TRUE;\r
+ case WM_SETCURSOR:\r
+ {\r
+ HCURSOR hCursor = 0;\r
+ switch (GetDlgCtrlID((HWND)wParam))\r
+ {\r
+ case 108:\r
+ hCursor = CommCtrl_LoadCursor(MAKEINTRESOURCE(108));\r
+ break;\r
+ }\r
+ if (hCursor)\r
+ {\r
+ SetCursor(hCursor);\r
+ SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 1);\r
+ return TRUE;\r
+ }\r
+ } return FALSE;\r
+ case WM_COMMAND:\r
+ {\r
+ switch (wParam)\r
+ {\r
+ case IDOK:\r
+ case IDCANCEL:\r
+ {\r
+ LRESULT nDontShowAgain = SendDlgItemMessage(hWnd, 106, BM_GETCHECK, 0, 0);\r
+ EndDialog(hWnd, MAKEWORD(IDOK, nDontShowAgain));\r
+ } break;\r
+ case 108:\r
+ {\r
+ HINSTANCE h = ShellExecute(hWnd, _T("open"), m_strDownloadURL, 0, 0, SW_SHOWNORMAL);\r
+ if ((UINT)h > 32)\r
+ {\r
+ LONG lStyle = ::GetWindowLong((HWND)lParam, GWL_STYLE);\r
+ ::SetWindowLong((HWND)lParam, GWL_STYLE, lStyle|BS_LEFTTEXT);\r
+ ::InvalidateRect((HWND)lParam, 0, TRUE);\r
+ }\r
+ else\r
+ {\r
+ MessageBeep(0);\r
+ }\r
+ } break;\r
+ }\r
+ } return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ * @brief Tell user what went wrong and how she can help.\r
+ */\r
+int C7ZipMismatchException::ReportError(UINT nType, UINT nMessageID)\r
+{\r
+ UINT_PTR response = -1;\r
+ m_bShowAllways = nMessageID;\r
+ if (!m_bShowAllways)\r
+ {\r
+ // Suppress error message in case 7-Zip is not installed.\r
+ response =\r
+ (\r
+ m_dwVer7zInstalled || m_dwVer7zLocal\r
+ ? (INT_PTR)(int)theApp.GetProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, -1)\r
+ : IDOK\r
+ );\r
+ }\r
+ if (response == -1)\r
+ {\r
+ HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();\r
+ response = DialogBoxParam(AfxGetResourceHandle(), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);\r
+ if (response == -1)\r
+ {\r
+ response = DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);\r
+ ASSERT(response != -1);\r
+ }\r
+ if (HIBYTE(response) == 1)\r
+ {\r
+ theApp.WriteProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, response = int LOBYTE(response));\r
+ }\r
+ }\r
+ return response;\r
+}\r
+\r
+/**\r
+ * @brief Check whether archive support is available.\r
+ */\r
+int NTAPI HasZipSupport()\r
+{\r
+ static int HasZipSupport = -1;\r
+ if (HasZipSupport == -1)\r
+ {\r
+ try\r
+ {\r
+ m_Merge7z.operator->();\r
+ HasZipSupport = 1;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->Delete();\r
+ HasZipSupport = 0;\r
+ }\r
+ }\r
+ return HasZipSupport;\r
+}\r
+\r
+/**\r
+ * @brief Tell user why archive support is not available.\r
+ */\r
+void NTAPI Recall7ZipMismatchError()\r
+{\r
+ try\r
+ {\r
+ m_Merge7z.operator->();\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->ReportError(MB_ICONEXCLAMATION, TRUE);\r
+ e->Delete();\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Delete head of temp path context list, and return its parent context.\r
+ */\r
+CTempPathContext *CTempPathContext::DeleteHead()\r
+{\r
+ CTempPathContext *pParent = m_pParent;\r
+ delete this;\r
+ return pParent;\r
+}\r
+\r
+BOOL NTAPI IsMerge7zEnabled()\r
+{\r
+ return AfxGetApp()->GetProfileInt(_T("Merge7z"), _T("Enable"), 0);\r
+}\r
+\r
+/**\r
+ * @brief Return installed or local version of 7-Zip.\r
+ */\r
+DWORD NTAPI VersionOf7z(BOOL bLocal)\r
+{\r
+ TCHAR path[MAX_PATH];\r
+ if (bLocal)\r
+ {\r
+ GetModuleFileName(0, path, sizeof path/sizeof*path);\r
+ PathRemoveFileSpec(path);\r
+ }\r
+ else\r
+ {\r
+ static const TCHAR szSubKey[] = _T("Software\\7-Zip");\r
+ static const TCHAR szValue[] = _T("Path");\r
+ DWORD type = 0;\r
+ DWORD size = sizeof path;\r
+ SHGetValue(HKEY_LOCAL_MACHINE, szSubKey, szValue, &type, path, &size);\r
+ }\r
+ PathAppend(path, _T("7z.dll"));\r
+ unsigned versionMS = 0;\r
+ unsigned versionLS = 0;\r
+ CVersionInfo(path).GetFixedFileVersion(versionMS, versionLS);\r
+ return versionMS;\r
+}\r
+\r
+/**\r
+ * @brief Access dll functions through proxy.\r
+ */\r
+interface Merge7z *Merge7z::Proxy::operator->()\r
+{\r
+ // As long as the Merge7z*.DLL has not yet been loaded, Merge7z\r
+ // [0] points to the name of the DLL (with placeholders for 7-\r
+ // Zip major and minor version numbers). Once the DLL has been\r
+ // loaded successfully, Merge7z[0] is set to NULL, causing the\r
+ // if to fail on subsequent calls.\r
+\r
+ if (const char *format = Merge7z[0])\r
+ {\r
+ // Merge7z has not yet been loaded\r
+\r
+ char name[MAX_PATH];\r
+ DWORD flags = ~0;\r
+ CException *pCause = NULL;\r
+ switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))\r
+ {\r
+ case 1: //Use installed 7-Zip if present. Otherwise, use local 7-Zip.\r
+ if (DWORD ver = VersionOf7z(FALSE))\r
+ {\r
+ flags = Initialize::Default;\r
+ try\r
+ {\r
+ wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));\r
+ Merge7z[0] = name;\r
+ stub.Load();\r
+ break;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ Merge7z[0] = format;\r
+ pCause = e;\r
+ }\r
+ }\r
+ case 2: //Always use local 7-Zip.\r
+ if (DWORD ver = VersionOf7z(TRUE))\r
+ {\r
+ flags = Initialize::Default | Initialize::Local7z;\r
+ try\r
+ {\r
+ wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));\r
+ Merge7z[0] = name;\r
+ stub.Load();\r
+ break;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ Merge7z[0] = format;\r
+ if (pCause) pCause->Delete();\r
+ pCause = e;\r
+ }\r
+ }\r
+ default:\r
+ throw new C7ZipMismatchException\r
+ (\r
+ VersionOf7z(FALSE),\r
+ VersionOf7z(TRUE),\r
+ pCause\r
+ );\r
+ }\r
+ LANGID wLangID = (LANGID)GetThreadLocale();\r
+ flags |= wLangID << 16;\r
+ if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE))\r
+ {\r
+ flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension;\r
+ }\r
+ if (Merge7z[1])\r
+ ((interface Merge7z *)Merge7z[1])->Initialize(flags);\r
+ }\r
+ return ((interface Merge7z *)Merge7z[1]);\r
+}\r
+\r
+/**\r
+ * @brief Tell Merge7z we are going to enumerate just 1 item.\r
+ */\r
+UINT SingleItemEnumerator::Open()\r
+{\r
+ return 1;\r
+}\r
+\r
+/**\r
+ * @brief Pass information about the item to Merge7z.\r
+ */\r
+Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item)\r
+{\r
+ item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse;\r
+ item.Name = Name;\r
+ item.FullPath = FullPath;\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * @brief SingleFileEnumerator constructor.\r
+ */\r
+SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name)\r
+: FullPath(FullPath)\r
+, Name(Name)\r
+{\r
+}\r
+\r
+/**\r
+ * @brief Construct a DirItemEnumerator.\r
+ *\r
+ * Argument *nFlags* controls operation as follows:\r
+ * LVNI_ALL: Enumerate all items.\r
+ * LVNI_SELECTED: Enumerate selected items only.\r
+ * Original: Set folder prefix for first iteration to "original"\r
+ * Altered: Set folder prefix for second iteration to "altered"\r
+ * BalanceFolders: Ensure that all nonempty folders on either side have a\r
+ * corresponding folder on the other side, even if it is\r
+ * empty (DirScan doesn't recurse into folders which\r
+ * appear only on one side).\r
+ * DiffsOnly: Enumerate diffs only.\r
+ */\r
+DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags)\r
+: m_pView(pView)\r
+, m_nFlags(nFlags)\r
+{\r
+ if (m_nFlags & Original)\r
+ {\r
+ m_rgFolderPrefix.push_back(_T("original"));\r
+ }\r
+ if (m_nFlags & Altered)\r
+ {\r
+ m_rgFolderPrefix.push_back(_T("altered"));\r
+ }\r
+ if (m_nFlags & BalanceFolders)\r
+ {\r
+ const CDiffContext& ctxt = pView->GetDiffContext();\r
+ // Collect implied folders\r
+ for (UINT i = Open() ; i-- ; )\r
+ {\r
+ const DIFFITEM &di = Next();\r
+ if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))\r
+ {\r
+ continue;\r
+ }\r
+ // Enumerating items\r
- if (di.diffcode.isExists(m_index))\r
++ if (di.diffcode.exists(m_index))\r
+ {\r
+ // Item is present on right side, i.e. folder is implied\r
+ m_rgImpliedFolders[m_index][di.diffFileInfo[m_index].path.get()] = PVOID(1);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Initialize enumerator, return number of items to be enumerated.\r
+ */\r
+UINT DirItemEnumerator::Open()\r
+{\r
+ m_nIndex = -1;\r
+ m_curFolderPrefix = m_rgFolderPrefix.begin();\r
+ m_index = (m_nFlags & Right) != 0 ? 1 : 0;\r
+ size_t nrgFolderPrefix = m_rgFolderPrefix.size();\r
+ if (nrgFolderPrefix)\r
+ {\r
+ m_strFolderPrefix = *m_curFolderPrefix++;\r
+ }\r
+ else\r
+ {\r
+ nrgFolderPrefix = 1;\r
+ }\r
+ return\r
+ (\r
+ m_nFlags & LVNI_SELECTED\r
+ ? pView(m_pView)->GetSelectedCount()\r
+ : pView(m_pView)->GetItemCount()\r
+ ) * nrgFolderPrefix;\r
+}\r
+\r
+/**\r
+ * @brief Return next item.\r
+ */\r
+const DIFFITEM &DirItemEnumerator::Next()\r
+{\r
+ enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED};\r
+ while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1)\r
+ {\r
+ m_strFolderPrefix = *m_curFolderPrefix++;\r
+ m_index = 1;\r
+ }\r
+ return m_pView->GetDiffItem(m_nIndex);\r
+}\r
+\r
+/**\r
+ * @brief Pass information about an item to Merge7z.\r
+ *\r
+ * Information is passed through struct Merge7z::DirItemEnumerator::Item.\r
+ * The *mask* member denotes which of the other members contain valid data.\r
+ * If *mask* is zero upon return, which will be the case if Enum() decides to\r
+ * leave the struct untouched, Merge7z will ignore the item.\r
+ * If Enum() allocates temporary storage for string members, it must also\r
+ * allocate an Envelope, providing a Free() method to free the temporary\r
+ * storage, along with the Envelope itself. The Envelope pointer is passed to\r
+ * Merge7z as the return value of the function. It is not meant to be a success\r
+ * indicator, so if no temporary storage is required, it is perfectly alright\r
+ * to return NULL.\r
+ */\r
+Merge7z::Envelope *DirItemEnumerator::Enum(Item &item)\r
+{\r
+ const CDiffContext& ctxt = m_pView->GetDiffContext();\r
+ const DIFFITEM &di = Next();\r
+\r
+ if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))\r
+ {\r
+ return 0;\r
+ }\r
+\r
- bool isSideOnly = !di.diffcode.isExists(m_index);\r
++ bool isSideOnly = !di.diffcode.exists(m_index);\r
+\r
+ Envelope *envelope = new Envelope;\r
+\r
+ const String &sFilename = di.diffFileInfo[m_index].filename;\r
+ const String &sSubdir = di.diffFileInfo[m_index].path;\r
+ if (sSubdir.length())\r
+ envelope->Name = paths_ConcatPath(sSubdir, sFilename);\r
+ else\r
+ envelope->Name = sFilename;\r
+ envelope->FullPath = paths_ConcatPath(\r
+ di.getFilepath(m_index, ctxt.GetNormalizedPath(m_index)),\r
+ sFilename);\r
+\r
+ UINT32 Recurse = item.Mask.Recurse;\r
+\r
+ if (m_nFlags & BalanceFolders)\r
+ {\r
+ // Enumerating items on right side\r
+ if (isSideOnly)\r
+ {\r
+ // Item is missing on right side\r
+ PVOID &implied = m_rgImpliedFolders[m_index][di.diffFileInfo[1-m_index].path.get()];\r
+ if (!implied)\r
+ {\r
+ // Folder is not implied by some other file, and has\r
+ // not been enumerated so far, so enumerate it now!\r
+ envelope->Name = di.diffFileInfo[1-m_index].path;\r
+ envelope->FullPath = di.getFilepath(1-m_index, ctxt.GetNormalizedPath(1-m_index));\r
+ implied = PVOID(2); // Don't enumerate same folder twice!\r
+ isSideOnly = false;\r
+ Recurse = 0;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (isSideOnly)\r
+ {\r
+ return envelope;\r
+ }\r
+\r
+ if (m_strFolderPrefix.length())\r
+ {\r
+ if (envelope->Name.length())\r
+ envelope->Name.insert(0, _T("\\"));\r
+ envelope->Name.insert(0, m_strFolderPrefix);\r
+ }\r
+\r
+ item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse;\r
+ item.Name = envelope->Name.c_str();\r
+ item.FullPath = envelope->FullPath.c_str();\r
+ return envelope;\r
+}\r
+\r
+/**\r
+ * @brief Apply appropriate handlers from left to right.\r
+ */\r
+bool DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path)\r
+{\r
+ DeleteFile(path);\r
+ Merge7z::Format *piHandler = ArchiveGuessFormat(path);\r
+ if (piHandler)\r
+ {\r
+ HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();\r
+ CString pathIntermediate;\r
+ SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path)));\r
+ String pathPrepend = path;\r
+ pathPrepend.resize(pathPrepend.rfind('\\') + 1);\r
+ pathIntermediate.Insert(0, pathPrepend.c_str());\r
+ bool bDone = MultiStepCompressArchive(pathIntermediate);\r
+ if (bDone)\r
+ {\r
+ piHandler->CompressArchive(hwndOwner, path,\r
+ &SingleItemEnumerator(path, pathIntermediate));\r
+ DeleteFile(pathIntermediate);\r
+ }\r
+ else\r
+ {\r
+ piHandler->CompressArchive(hwndOwner, path, this);\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+}\r
+\r
+/**\r
+ * @brief Generate archive from DirView items.\r
+ */\r
+void DirItemEnumerator::CompressArchive(LPCTSTR path)\r
+{\r
+ String strPath;\r
+ if (path == 0)\r
+ {\r
+ // No path given, so prompt for path!\r
+ static const TCHAR _T_Merge7z[] = _T("Merge7z");\r
+ static const TCHAR _T_FilterIndex[] = _T("FilterIndex");\r
+ // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't\r
+ // offer other formats here!\r
+ static const TCHAR _T_Filter[]\r
+ (\r
+ _T("7z|*.7z|")\r
+ //_T("z|*.z|")\r
+ _T("zip|*.zip|")\r
+ _T("jar (zip)|*.jar|")\r
+ _T("ear (zip)|*.ear|")\r
+ _T("war (zip)|*.war|")\r
+ _T("xpi (zip)|*.xpi|")\r
+ //_T("rar|*.rar|")\r
+ _T("tar|*.tar|")\r
+ _T("tar.z|*.tar.z|")\r
+ _T("tar.gz|*.tar.gz|")\r
+ _T("tar.bz2|*.tar.bz2|")\r
+ //_T("tz|*.tz|")\r
+ _T("tgz|*.tgz|")\r
+ _T("tbz2|*.tbz2|")\r
+ //_T("lzh|*.lzh|")\r
+ //_T("cab|*.cab|")\r
+ //_T("arj|*.arj|")\r
+ //_T("deb|*.deb|")\r
+ //_T("rpm|*.rpm|")\r
+ //_T("cpio|*.cpio|")\r
+ //_T("|")\r
+ );\r
+ String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString();\r
+ strFilter.insert(0, _T_Filter);\r
+ strFilter += _T("|");\r
+ CFileDialog dlg\r
+ (\r
+ FALSE,\r
+ 0,\r
+ 0,\r
+ OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN,\r
+ strFilter.c_str()\r
+ );\r
+ dlg.m_ofn.nFilterIndex = AfxGetApp()->GetProfileInt(_T_Merge7z, _T_FilterIndex, 1);\r
+ // Use extension from current filter as default extension:\r
+ if (int i = dlg.m_ofn.nFilterIndex)\r
+ {\r
+ dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter;\r
+ while (*dlg.m_ofn.lpstrDefExt && --i)\r
+ {\r
+ dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;\r
+ dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;\r
+ }\r
+ if (*dlg.m_ofn.lpstrDefExt)\r
+ {\r
+ dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3;\r
+ }\r
+ }\r
+ if (dlg.DoModal() == IDOK)\r
+ {\r
+ strPath = dlg.GetPathName();\r
+ path = strPath.c_str();\r
+ AfxGetApp()->WriteProfileInt(_T_Merge7z, _T_FilterIndex, dlg.m_ofn.nFilterIndex);\r
+ }\r
+ }\r
+ if (path && !MultiStepCompressArchive(path))\r
+ {\r
+ LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION);\r
+ }\r
+}\r
+\r
+\r
+DecompressResult DecompressArchive(HWND hWnd, const PathContext& files)\r
+{\r
+ DecompressResult res(files, NULL, IS_EXISTING_DIR);\r
+ try\r
+ {\r
+ String path;\r
+ USES_CONVERSION;\r
+ // Handle archives using 7-zip\r
+ Merge7z::Format *piHandler;\r
+ if (piHandler = ArchiveGuessFormat(res.files[0].c_str()))\r
+ {\r
+ res.pTempPathContext = new CTempPathContext;\r
+ path = env_GetTempChildPath();\r
+ for (int index = 0; index < res.files.GetSize(); index++)\r
+ res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];\r
+ if (res.files.GetSize() == 2 && res.files[0] == res.files[1])\r
+ res.files[1].erase();\r
+ do\r
+ {\r
+ if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[0].c_str(), path.c_str())))\r
+ break;\r
+ if (res.files[0].find(path) == 0)\r
+ {\r
+ VERIFY(::DeleteFile(res.files[0].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[0].c_str())), false));\r
+ }\r
+ BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[0].c_str());\r
+ res.files[0] = OLE2T(pTmp);\r
+ SysFreeString(pTmp);\r
+ res.files[0].insert(0, _T("\\"));\r
+ res.files[0].insert(0, path);\r
+ } while (piHandler = ArchiveGuessFormat(res.files[0].c_str()));\r
+ res.files[0] = path;\r
+ }\r
+ if (!res.files[1].empty() && (piHandler = ArchiveGuessFormat(res.files[1].c_str())))\r
+ {\r
+ if (!res.pTempPathContext)\r
+ {\r
+ res.pTempPathContext = new CTempPathContext;\r
+ for (int index = 0; index < res.files.GetSize(); index++)\r
+ res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];\r
+ }\r
+ path = env_GetTempChildPath();\r
+ do\r
+ {\r
+ if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[1].c_str(), path.c_str())))\r
+ break;;\r
+ if (res.files[1].find(path) == 0)\r
+ {\r
+ VERIFY(::DeleteFile(res.files[1].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[1].c_str())), false));\r
+ }\r
+ BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());\r
+ res.files[1] = OLE2T(pTmp);\r
+ SysFreeString(pTmp);\r
+ res.files[1].insert(0, _T("\\"));\r
+ res.files[1].insert(0, path);\r
+ } while (piHandler = ArchiveGuessFormat(res.files[1].c_str()));\r
+ res.files[1] = path;\r
+ }\r
+ if (res.files.GetSize() > 2 && (piHandler = ArchiveGuessFormat(res.files[2].c_str())))\r
+ {\r
+ if (!res.pTempPathContext)\r
+ {\r
+ res.pTempPathContext = new CTempPathContext;\r
+ for (int index = 0; index < res.files.GetSize(); index++)\r
+ res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];\r
+ }\r
+ path = env_GetTempChildPath();\r
+ do\r
+ {\r
+ if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[2].c_str(), path.c_str())))\r
+ break;;\r
+ if (res.files[2].find(path) == 0)\r
+ {\r
+ VERIFY(::DeleteFile(res.files[2].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[2].c_str())), false));\r
+ }\r
+ BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());\r
+ res.files[2] = OLE2T(pTmp);\r
+ SysFreeString(pTmp);\r
+ res.files[2].insert(0, _T("\\"));\r
+ res.files[2].insert(0, path);\r
+ } while (piHandler = ArchiveGuessFormat(res.files[2].c_str()));\r
+ res.files[2] = path;\r
+ }\r
+ if (res.files[1].empty())\r
+ {\r
+ // assume Perry style patch\r
+ res.files[1] = path;\r
+ res.files[0] += _T("\\ORIGINAL");\r
+ res.files[1] += _T("\\ALTERED");\r
+ if (!PathFileExists(res.files[0].c_str()) || !PathFileExists(res.files[1].c_str()))\r
+ {\r
+ // not a Perry style patch: diff with itself...\r
+ res.files[0] = res.files[1] = path;\r
+ }\r
+ else\r
+ {\r
+ res.pTempPathContext->m_strDisplayRoot[0] += _T("\\ORIGINAL");\r
+ res.pTempPathContext->m_strDisplayRoot[1] += _T("\\ALTERED");\r
+ }\r
+ }\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->ReportError(MB_ICONSTOP);\r
+ e->Delete();\r
+ }\r
+ return res;\r
+}\r
+\r
+\r
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;
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;
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;
}
/**
}
/**
- * @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);
}
/**
--- /dev/null
- return (di.diffcode.diffcode != 0 && di.diffcode.isExists(SideToIndex(m_ctxt, src)));
+#pragma once
+
+#include "UnicodeString.h"
+#include "DiffContext.h"
+#include "FileActionScript.h"
+#include "paths.h"
+#include "IntToIntMap.h"
+#include <algorithm>
+
+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<class GetOptionBool>
+ 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<String, bool> 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<int, const DIFFITEM *> 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 <SIDE_TYPE src, SIDE_TYPE dst>
+ bool IsItemCopyableOnTo(const DIFFITEM& di) const
+ {
+ return (di.diffcode.diffcode != 0 && !m_RO[SideToIndex(m_ctxt, dst)] && ::IsItemCopyable(di, SideToIndex(m_ctxt, src)));
+ }
+
+ template <SIDE_TYPE src>
+ bool IsItemCopyableToOn(const DIFFITEM& di) const
+ {
+ return (di.diffcode.diffcode != 0 && ::IsItemCopyableToOn(di, SideToIndex(m_ctxt, src)));
+ }
+
+ template <SIDE_TYPE src>
+ 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 <SIDE_TYPE src>
+ 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 <SIDE_TYPE src>
+ bool IsItemOpenanbleOn(const DIFFITEM& di) const
+ {
+ return (di.diffcode.diffcode != 0 && IsItemOpenableOn(di, SideToIndex(m_ctxt, src)));
+ }
+
+ template <SIDE_TYPE src>
+ 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 <SIDE_TYPE src>
+ bool IsItemExist(const DIFFITEM& di) const
+ {
- return (di.diffcode.diffcode != 0 && di.diffcode.isExists(index) && di.diffFileInfo[index].IsEditableEncoding());
++ return (di.diffcode.diffcode != 0 && di.diffcode.exists(SideToIndex(m_ctxt, src)));
+ }
+
+ template <SIDE_TYPE src>
+ bool IsItemEditableEncoding(const DIFFITEM& di) const
+ {
+ const int index = SideToIndex(m_ctxt, src);
- if (di.diffcode.isExists(index))
++ 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<int, const DIFFITEM *>& 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<SIDE_TYPE src, SIDE_TYPE to>
+ FileActionScript *Copy(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
+ {
+ return CopyItem(pscript, it, src, to);
+ }
+
+ FileActionScript *DeleteItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& 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<SIDE_TYPE src>
+ FileActionScript *DeleteOn(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
+ {
+ return DeleteItem(pscript, it, src);
+ }
+
+ FileActionScript *DeleteOnBoth(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> 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<int, const DIFFITEM *> 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<int, const DIFFITEM *>& 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<SIDE_TYPE src>
+ FileActionScript *CopyTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
+ {
+ return CopyOrMoveItemTo(pscript, it, FileAction::ACT_COPY, src);
+ }
+
+ template<SIDE_TYPE src>
+ FileActionScript *MoveTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
+ {
+ return CopyOrMoveItemTo(pscript, it, FileAction::ACT_MOVE, src);
+ }
+
+ FileActionScript *operator()(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> 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<class InputIterator, class Predicate>
+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<class InputIterator>
+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<class InputIterator>
+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<class InputIterator, class OutputIterator>
+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(i))
++ if (di.diffcode.exists(index))
+ {
+ *result = GetItemFileName(ctxt, di, index);
+ ++result;
+ }
+ }
+ return result;
+}
+
+template<class InputIterator, class OutputIterator>
+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<class InputIterator, class OutputIterator>
+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<class InputIterator, class OutputIterator>
+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<class InputIterator, class BinaryFunction>
+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<class InputIterator>
+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.diffcode != 0 && 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<CDiffContext&>(ctxt).FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
+ infoPrediffer->Initialize(newsetting);
+ }
+ }
+}
+
+/**
+ * @brief Updates just before displaying plugin context view in list
+ */
+template<class InputIterator>
+std::pair<int, int> 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<CDiffContext&>(ctxt).FetchPluginInfos(filteredFilenames, &unpacker, &prediffer);
+ if (prediffer->bToBeScanned == 1 || prediffer->pluginName.empty() == false)
+ nPredifferYes ++;
+ else
+ nPredifferNo ++;
+ }
+ }
+ return std::make_pair(nPredifferYes, nPredifferNo);
+}
+
+template<class InputIterator>
+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 (affect[i] && di.diffcode.isExists(i) && di.diffFileInfo[i].IsEditableEncoding())
++ if (di.diffcode.diffcode != 0 && di.diffcode.exists(i))
+ map.Increment(di.diffFileInfo[i].encoding.m_codepage);
+ }
+ }
+ return map;
+}
+
+template<class InputIterator>
+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.exists(i) && di.diffFileInfo[i].IsEditableEncoding())
+ {
+ di.diffFileInfo[i].encoding.SetCodepage(nCodepage);
+ }
+ }
+ }
+}
+
+/// get file name on specified side for first selected item
+template<class InputIterator>
+String GetSelectedFileName(InputIterator& it, SIDE_TYPE stype, const CDiffContext& ctxt)
+{
+ if (it == InputIterator())
+ return _T("");
+ return GetItemFileName(ctxt, *it, SideToIndex(ctxt, stype));
+}