3 Star 9 Fork 7

Gitee 极速下载/WinMerge

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/winmerge/winmerge
克隆/下载
DirActions.cpp 50.26 KB
一键复制 编辑 原始数据 按行查看 历史
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638
// SPDX-License-Identifier: GPL-2.0-or-later
/**
* @file DirActions.cpp
*
* @brief Implementation of methods of CDirView that copy/move/delete files
*/
// It would be nice to make this independent of the UI (CDirView)
// but it needs access to the list of selected items.
// One idea would be to provide an iterator over them.
//
#include "pch.h"
#include "DirActions.h"
#include "MergeApp.h"
#include "UnicodeString.h"
#include "Logger.h"
#include "7zCommon.h"
#include "ShellFileOperations.h"
#include "DiffItem.h"
#include "FileActionScript.h"
#include "locality.h"
#include "FileFilterHelper.h"
#include "DebugNew.h"
static void ThrowConfirmCopy(const CDiffContext& ctxt, int origin, int destination, size_t count,
const String& src, const String& dest, bool destIsSide);
static void ThrowConfirmMove(const CDiffContext& ctxt, int origin, int destination, size_t count,
const String& src, const String& dest, bool destIsSide);
static void ThrowConfirmationNeededException(const CDiffContext& ctxt, const String &caption, const String &question,
int origin, int destination, size_t count,
const String& src, const String& dest, bool destIsSide);
ContentsChangedException::ContentsChangedException(const String& failpath)
: m_msg(strutils::format_string1(
_("Operation aborted!\n\nFolder contents changed, path\n%1\nnot found.\n\nRefresh the compare."),
failpath))
{
}
FileOperationException::FileOperationException(const String& msg)
: m_msg(msg)
{
}
/**
* @brief Ask user a confirmation for copying item(s).
* Shows a confirmation dialog for copy operation. Depending ont item count
* dialog shows full paths to items (single item) or base paths of compare
* (multiple items).
* @param [in] origin Origin side of the item(s).
* @param [in] destination Destination side of the item(s).
* @param [in] count Number of items.
* @param [in] src Source path.
* @param [in] dest Destination path.
* @param [in] destIsSide Is destination path either of compare sides?
* @return true if copy should proceed, false if aborted.
*/
static void ThrowConfirmCopy(const CDiffContext& ctxt, int origin, int destination, size_t count,
const String& src, const String& dest, bool destIsSide)
{
String caption = _("Confirm Copy");
String strQuestion = count == 1 ? _("Are you sure you want to copy?") :
strutils::format(_("Are you sure you want to copy %d items?").c_str(), count);
ThrowConfirmationNeededException(ctxt, caption, strQuestion, origin,
destination, count, src, dest, destIsSide);
}
/**
* @brief Ask user a confirmation for moving item(s).
* Shows a confirmation dialog for move operation. Depending ont item count
* dialog shows full paths to items (single item) or base paths of compare
* (multiple items).
* @param [in] origin Origin side of the item(s).
* @param [in] destination Destination side of the item(s).
* @param [in] count Number of items.
* @param [in] src Source path.
* @param [in] dest Destination path.
* @param [in] destIsSide Is destination path either of compare sides?
* @return true if copy should proceed, false if aborted.
*/
static void ThrowConfirmMove(const CDiffContext& ctxt, int origin, int destination, size_t count,
const String& src, const String& dest, bool destIsSide)
{
String caption = _("Confirm Move");
String strQuestion = count == 1 ? _("Are you sure you want to move?") :
strutils::format(_("Are you sure you want to move %d items?").c_str(), count);
ThrowConfirmationNeededException(ctxt, caption, strQuestion, origin,
destination, count, src, dest, destIsSide);
}
/**
* @brief Show a (copy/move) confirmation dialog.
* @param [in] caption Caption of the dialog.
* @param [in] question Guestion to ask from user.
* @param [in] origin Origin side of the item(s).
* @param [in] destination Destination side of the item(s).
* @param [in] count Number of items.
* @param [in] src Source path.
* @param [in] dest Destination path.
* @param [in] destIsSide Is destination path either of compare sides?
* @return true if copy should proceed, false if aborted.
*/
static void ThrowConfirmationNeededException(const CDiffContext& ctxt, const String &caption, const String &question,
int origin, int destination, size_t count,
const String& src, const String& dest, bool destIsSide)
{
ConfirmationNeededException exp;
String sOrig;
String sDest;
exp.m_caption = caption;
if (origin == 0)
sOrig = _("From left:");
else if (origin == ctxt.GetCompareDirs() - 1)
sOrig = _("From right:");
else
sOrig = _("From middle:");
if (destIsSide)
{
// Copy to left / right
if (destination == 0)
sDest = _("To left:");
else if (destination == ctxt.GetCompareDirs() - 1)
sDest = _("To right:");
else
sDest = _("To middle:");
}
else
{
// Copy left/right to..
sDest = _("To:");
}
String strSrc(src);
if (paths::DoesPathExist(src) == paths::IS_EXISTING_DIR)
strSrc = paths::AddTrailingSlash(src);
String strDest(dest);
if (paths::DoesPathExist(dest) == paths::IS_EXISTING_DIR)
strDest = paths::AddTrailingSlash(dest);
exp.m_question = question;
exp.m_fromText = std::move(sOrig);
exp.m_toText = std::move(sDest);
exp.m_fromPath = std::move(strSrc);
exp.m_toPath = std::move(strDest);
throw exp;
}
/**
* @brief Confirm actions with user as appropriate
* (type, whether single or multiple).
*/
void ConfirmActionList(const CDiffContext& ctxt, const FileActionScript & actionList)
{
// TODO: We need better confirmation for file actions.
// Maybe we should show a list of files with actions done..
FileActionItem item = actionList.GetHeadActionItem();
bool bDestIsSide = true;
// special handling for the single item case, because it is probably the most common,
// and we can give the user exact details easily for it
switch(item.atype)
{
case FileAction::ACT_COPY:
if (item.UIResult == FileActionItem::UI_DONT_CARE)
bDestIsSide = false;
if (actionList.GetActionItemCount() == 1)
{
ThrowConfirmCopy(ctxt, item.UIOrigin, item.UIDestination,
actionList.GetActionItemCount(), item.src, item.dest,
bDestIsSide);
}
else
{
String src = ctxt.GetPath(item.UIOrigin);
String dst;
if (bDestIsSide)
{
dst = ctxt.GetPath(item.UIDestination);
}
else
{
if (!actionList.m_destBase.empty())
dst = actionList.m_destBase;
else
dst = item.dest;
}
ThrowConfirmCopy(ctxt, item.UIOrigin, item.UIDestination,
actionList.GetActionItemCount(), src, dst, bDestIsSide);
}
break;
case FileAction::ACT_DEL:
break;
case FileAction::ACT_MOVE:
if (item.UIResult == FileActionItem::UI_DEL)
bDestIsSide = false;
if (actionList.GetActionItemCount() == 1)
{
ThrowConfirmMove(ctxt, item.UIOrigin, item.UIDestination,
actionList.GetActionItemCount(), item.src, item.dest,
bDestIsSide);
}
else
{
String src = ctxt.GetPath(item.UIOrigin);;
String dst;
if (!actionList.m_destBase.empty())
dst = actionList.m_destBase;
else
dst = item.dest;
ThrowConfirmMove(ctxt, item.UIOrigin, item.UIDestination,
actionList.GetActionItemCount(), src, dst, bDestIsSide);
}
break;
// Invalid operation
default:
RootLogger::Error(_T("Unknown fileoperation in CDirView::ConfirmActionList()"));
throw "Unknown fileoperation in ConfirmActionList()";
break;
}
}
/**
* @brief Update results for FileActionItem.
* This functions is called to update DIFFITEM after FileActionItem.
* @param [in] act Action that was done.
* @param [in] ctxt Compare context: contains difflist, encoding info etc.
* @param [in,out] di Item to update the results.
*/
UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContext& ctxt, DIFFITEM &di)
{
bool bUpdateSrc = false;
bool bUpdateDest = false;
bool bRemoveItem = false;
// Use FileActionItem types for simplicity for now.
// Better would be to use FileAction contained, since it is not
// UI dependent.
switch (act.UIResult)
{
case FileActionItem::UI_COPY:
case FileActionItem::UI_COPY_DIFFITEMS:
bUpdateSrc = true;
bUpdateDest = true;
CopyDiffSideAndProperties(ctxt, di, act.UIOrigin, act.UIDestination, act.UIResult);
if (ctxt.GetCompareDirs() > 2)
SetDiffCompare(di, DIFFCODE::NOCMP);
else
UpdateCompareFlagsAfterSync(di, ctxt.m_bRecursive);
SetDiffCounts(di, 0, 0);
break;
case FileActionItem::UI_MOVE:
bUpdateSrc = true;
bUpdateDest = true;
CopyDiffSideAndProperties(ctxt, di, act.UIOrigin, act.UIDestination, act.UIResult);
UnsetDiffSide(ctxt, di, act.UIOrigin);
SetDiffCompare(di, DIFFCODE::NOCMP);
break;
case FileActionItem::UI_DEL:
if (di.diffcode.isSideOnly(act.UIOrigin))
{
bRemoveItem = true;
}
else
{
UnsetDiffSide(ctxt, di, act.UIOrigin);
SetDiffCompare(di, DIFFCODE::NOCMP);
bUpdateSrc = true;
}
break;
}
if (bRemoveItem)
return UPDATEITEM_REMOVE;
if (bUpdateSrc || bUpdateDest)
return UPDATEITEM_UPDATE;
return UPDATEITEM_NONE;
}
/**
* @brief Find the CDiffContext diffpos of an item from its left & right paths
* @return POSITION to item, `nullptr` if not found.
* @note Filenames must be same, if they differ `nullptr` is returned.
*/
DIFFITEM *FindItemFromPaths(const CDiffContext& ctxt, const PathContext& paths)
{
int nBuffer;
String file[3], path[3], base;
for (nBuffer = 0; nBuffer < paths.GetSize(); ++nBuffer)
{
String p = paths[nBuffer];
file[nBuffer] = paths::FindFileName(p);
if (file[nBuffer].empty())
return 0;
// Path can contain (because of difftools?) '/' and '\'
// so for comparing purposes, convert whole path to use '\\'
path[nBuffer] = paths::ToWindowsPath(String(p, 0, p.length() - file[nBuffer].length())); // include trailing backslash
base = ctxt.GetPath(nBuffer); // include trailing backslash
if (path[nBuffer].compare(0, base.length(), base.c_str()) != 0)
return 0;
path[nBuffer].erase(0, base.length()); // turn into relative path
if (String::size_type length = path[nBuffer].length())
path[nBuffer].resize(length - 1); // remove trailing backslash
}
// Filenames must be identical
if (std::any_of(file, file + paths.GetSize(), [&](auto& it) { return strutils::compare_nocase(it, file[0]) != 0; }))
return 0;
DIFFITEM *pos = ctxt.GetFirstDiffPosition();
if (paths.GetSize() == 2)
{
while (DIFFITEM *currentPos = pos) // Save our current pos before getting next
{
const DIFFITEM &di = ctxt.GetNextDiffPosition(pos);
if (di.diffFileInfo[0].path == path[0] &&
di.diffFileInfo[1].path == path[1] &&
di.diffFileInfo[0].filename == file[0] &&
di.diffFileInfo[1].filename == file[1])
{
return currentPos;
}
}
}
else
{
while (DIFFITEM *currentPos = pos) // Save our current pos before getting next
{
const DIFFITEM &di = ctxt.GetNextDiffPosition(pos);
if (di.diffFileInfo[0].path == path[0] &&
di.diffFileInfo[1].path == path[1] &&
di.diffFileInfo[2].path == path[2] &&
di.diffFileInfo[0].filename == file[0] &&
di.diffFileInfo[1].filename == file[1] &&
di.diffFileInfo[2].filename == file[2])
{
return currentPos;
}
}
}
return 0;
}
bool IsItemCopyable(const DIFFITEM &di, int index, bool copyOnlyDiffItems)
{
if (copyOnlyDiffItems)
{
// 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;
// can't copy skipped items
if (di.diffcode.isResultFiltered()) return false;
}
// impossible if not existing
if (!di.diffcode.exists(index)) return false;
// everything else can be copied to other side
return true;
}
/// is it possible to move item to left ?
bool IsItemMovable(const DIFFITEM &di, int index)
{
// don't let them mess with error items
if (di.diffcode.isResultError()) return false;
// impossible if not existing
if (!di.diffcode.exists(index)) return false;
// everything else can be copied to other side
return true;
}
/// 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 not existing
if (!di.diffcode.exists(index)) return false;
// everything else can be deleted
return true;
}
/// is it possible to delete both items ?
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
for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
if (!di.diffcode.exists(i)) return false;
// everything else can be deleted on both
return true;
}
/// is it possible to compare these two items?
bool AreItemsOpenable(const CDiffContext& ctxt, SELECTIONTYPE selectionType, const DIFFITEM &di1, const DIFFITEM &di2, bool openableForDir /*= true*/)
{
String sLeftBasePath = ctxt.GetPath(0);
String sRightBasePath = ctxt.GetPath(1);
// Must be both directory or neither
if (di1.diffcode.isDirectory() != di2.diffcode.isDirectory()) return false;
if (!openableForDir && di1.diffcode.isDirectory()) return false;
switch (selectionType)
{
case SELECTIONTYPE_NORMAL:
// Must be on different sides, or one on one side & one on both
if (di1.diffcode.isSideFirstOnly() && (di2.diffcode.isSideSecondOnly() ||
di2.diffcode.isSideBoth()))
return true;
if (di1.diffcode.isSideSecondOnly() && (di2.diffcode.isSideFirstOnly() ||
di2.diffcode.isSideBoth()))
return true;
if (di1.diffcode.isSideBoth() && (di2.diffcode.isSideFirstOnly() ||
di2.diffcode.isSideSecondOnly()))
return true;
break;
case SELECTIONTYPE_LEFT1LEFT2:
if (di1.diffcode.exists(0) && di2.diffcode.exists(0))
return true;
break;
case SELECTIONTYPE_RIGHT1RIGHT2:
if (di1.diffcode.exists(1) && di2.diffcode.exists(1))
return true;
break;
case SELECTIONTYPE_LEFT1RIGHT2:
if (di1.diffcode.exists(0) && di2.diffcode.exists(1))
return true;
break;
case SELECTIONTYPE_LEFT2RIGHT1:
if (di1.diffcode.exists(1) && di2.diffcode.exists(0))
return true;
break;
}
// Allow to compare items if left & right path refer to same directory
// (which means there is effectively two files involved). No need to check
// side flags. If files weren't on both sides, we'd have no DIFFITEMs.
if (strutils::compare_nocase(sLeftBasePath, sRightBasePath) == 0)
return true;
return false;
}
/// is it possible to compare these three items?
bool AreItemsOpenable(const CDiffContext& ctxt, const DIFFITEM &di1, const DIFFITEM &di2, const DIFFITEM &di3, bool openableForDir /*= true*/)
{
if (ctxt.GetCompareDirs() < 3)
return false;
String sLeftBasePath = ctxt.GetPath(0);
String sMiddleBasePath = ctxt.GetPath(1);
String sRightBasePath = ctxt.GetPath(2);
// Must be both directory or neither
if (di1.diffcode.isDirectory() != di2.diffcode.isDirectory() || di1.diffcode.isDirectory() != di3.diffcode.isDirectory()) return false;
if (!openableForDir && di1.diffcode.isDirectory()) return false;
// Must be on different sides, or one on one side & one on both
if (di1.diffcode.isSideFirstOnly() && di2.diffcode.isSideSecondOnly() && di3.diffcode.isSideThirdOnly())
return true;
if (di1.diffcode.isSideFirstOnly() && di2.diffcode.isSideThirdOnly() && di3.diffcode.isSideSecondOnly())
return true;
if (di1.diffcode.isSideSecondOnly() && di2.diffcode.isSideFirstOnly() && di3.diffcode.isSideThirdOnly())
return true;
if (di1.diffcode.isSideSecondOnly() && di2.diffcode.isSideThirdOnly() && di3.diffcode.isSideFirstOnly())
return true;
if (di1.diffcode.isSideThirdOnly() && di2.diffcode.isSideFirstOnly() && di3.diffcode.isSideSecondOnly())
return true;
if (di1.diffcode.isSideThirdOnly() && di2.diffcode.isSideSecondOnly() && di3.diffcode.isSideFirstOnly())
return true;
// Allow to compare items if left & right path refer to same directory
// (which means there is effectively two files involved). No need to check
// side flags. If files weren't on both sides, we'd have no DIFFITEMs.
if (strutils::compare_nocase(sLeftBasePath, sMiddleBasePath) == 0 && strutils::compare_nocase(sLeftBasePath, sRightBasePath) == 0)
return true;
return false;
}
/// is it possible to open item ?
bool IsItemOpenableOn(const DIFFITEM &di, int index)
{
if (di.diffcode.diffcode == 0) return false;
// impossible if not existing
if (!di.diffcode.exists(index)) return false;
// everything else can be opened on right
return true;
}
/// is it possible to open left ... item ?
bool IsItemOpenableOnWith(const DIFFITEM &di, int index)
{
return (di.diffcode.diffcode != 0 && !di.diffcode.isDirectory() && IsItemOpenableOn(di, index));
}
/**
* @brief Is it possible to open the parent folder?
* @param [in] di Diff item to check
* @param [in] index Index of the item whose parent folder to be opened.
* @return True if it is possible to open the parent folder.
*/
bool IsParentFolderOpenable(const DIFFITEM& di, int index)
{
return (di.diffcode.diffcode != 0 && di.diffcode.exists(index));
}
/// is it possible to copy to... left item?
bool IsItemCopyableToOn(const DIFFITEM &di, int index)
{
// impossible if only on right
if (!di.diffcode.exists(index)) return false;
// everything else can be copied to from left
return true;
}
// When navigating differences, do we stop at this one ?
bool IsItemNavigableDiff(const CDiffContext& ctxt, const DIFFITEM &di)
{
// Not a valid diffitem, one of special items (e.g "..")
if (di.diffcode.diffcode == 0)
return false;
if (di.diffcode.isResultFiltered() || di.diffcode.isResultError())
return false;
if (!di.diffcode.isResultDiff() && IsItemExistAll(ctxt, di))
return false;
return true;
}
bool IsItemExistAll(const CDiffContext& ctxt, const DIFFITEM &di)
{
// Not a valid diffitem, one of special items (e.g "..")
if (di.diffcode.diffcode == 0)
return false;
return di.diffcode.existAll();
}
/**
* @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.customFlags & 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 (ctxt.GetCompareDirs() < 3)
{
if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right)
return false;
}
else
{
if (di.diffcode.isSideSecondOnly() && !filter.show_unique_middle)
return false;
if (di.diffcode.isSideThirdOnly() && !filter.show_unique_right)
return false;
if (di.diffcode.isMissingFirstOnly() && !filter.show_missing_left_only)
return false;
if (di.diffcode.isMissingSecondOnly() && !filter.show_missing_middle_only)
return false;
if (di.diffcode.isMissingThirdOnly() && !filter.show_missing_right_only)
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 (ctxt.GetCompareDirs() < 3)
{
if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right)
return false;
}
else
{
if (di.diffcode.isSideSecondOnly() && !filter.show_unique_middle)
return false;
if (di.diffcode.isSideThirdOnly() && !filter.show_unique_right)
return false;
if (di.diffcode.isMissingFirstOnly() && !filter.show_missing_left_only)
return false;
if (di.diffcode.isMissingSecondOnly() && !filter.show_missing_middle_only)
return false;
if (di.diffcode.isMissingThirdOnly() && !filter.show_missing_right_only)
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;
bool bShowable = true;
if (ctxt.GetCompareDirs() < 3)
{
if (di.diffcode.isResultDiff() && !filter.show_different)
bShowable = false;
}
else
{
if ((di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) == DIFFCODE::DIFF1STONLY)
{
if (!filter.show_different_left_only)
bShowable = false;
}
else if ((di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) == DIFFCODE::DIFF2NDONLY)
{
if (!filter.show_different_middle_only)
bShowable = false;
}
else if ((di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) == DIFFCODE::DIFF3RDONLY)
{
if (!filter.show_different_right_only)
bShowable = false;
}
else if (di.diffcode.isResultDiff() && !filter.show_different)
bShowable = false;
}
if (!bShowable)
{
DIFFITEM *diffpos = ctxt.GetFirstChildDiffPosition(&di);
while (diffpos != nullptr)
{
const DIFFITEM &dic = ctxt.GetNextSiblingDiffPosition(diffpos);
if (IsShowable(ctxt, dic, filter))
return true;
}
return false;
}
}
}
}
else
{
// left/right filters
if (di.diffcode.isSideFirstOnly() && !filter.show_unique_left)
return false;
if (ctxt.GetCompareDirs() < 3)
{
if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right)
return false;
}
else
{
if (di.diffcode.isSideSecondOnly() && !filter.show_unique_middle)
return false;
if (di.diffcode.isSideThirdOnly() && !filter.show_unique_right)
return false;
if (di.diffcode.isMissingFirstOnly() && !filter.show_missing_left_only)
return false;
if (di.diffcode.isMissingSecondOnly() && !filter.show_missing_middle_only)
return false;
if (di.diffcode.isMissingThirdOnly() && !filter.show_missing_right_only)
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() && false/* && !GetMainFrame()->m_bShowErrors FIXME:*/)
return false;
if (ctxt.GetCompareDirs() < 3)
{
if (di.diffcode.isResultDiff() && !filter.show_different)
return false;
}
else
{
if ((di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) == DIFFCODE::DIFF1STONLY)
{
if (!filter.show_different_left_only)
return false;
}
else if ((di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) == DIFFCODE::DIFF2NDONLY)
{
if (!filter.show_different_middle_only)
return false;
}
else if ((di.diffcode.diffcode & DIFFCODE::COMPAREFLAGS3WAY) == DIFFCODE::DIFF3RDONLY)
{
if (!filter.show_different_right_only)
return false;
}
else if (di.diffcode.isResultDiff() && !filter.show_different)
return false;
}
}
return true;
}
/**
* @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?
* @param [in] openableForDir Are items openable if the items are directories?
* return false if there was error or item was completely processed.
*/
bool GetOpenOneItem(const CDiffContext& ctxt, DIFFITEM *pos1, const DIFFITEM *pdi[3],
PathContext & paths, int & sel1, bool & isdir, int nPane[3], FileTextEncoding encoding[3], String& errmsg, bool openableForDir /*= true*/)
{
pdi[0] = &ctxt.GetDiffAt(pos1);
pdi[1] = pdi[0];
pdi[2] = pdi[0];
if (!openableForDir && pdi[0]->diffcode.isDirectory()) return false;
paths = GetItemFileNames(ctxt, *pdi[0]);
encoding[0] = pdi[0]->diffFileInfo[0].encoding;
encoding[1] = pdi[0]->diffFileInfo[1].encoding;
encoding[2] = pdi[0]->diffFileInfo[2].encoding;
for (int nIndex = 0; nIndex < paths.GetSize(); ++nIndex)
nPane[nIndex] = nIndex;
if (pdi[0]->diffcode.isDirectory())
isdir = true;
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.
paths::PATH_EXISTENCE path1Exists = paths::DoesPathExist(paths[0]);
paths::PATH_EXISTENCE path2Exists = paths::DoesPathExist(paths[1]);
if (path1Exists != paths::IS_EXISTING_DIR || path2Exists != paths::IS_EXISTING_DIR)
{
String invalid = path1Exists == paths::IS_EXISTING_DIR ? paths[0] : paths[1];
errmsg = strutils::format_string1(
_("Operation aborted!\n\nFolder contents changed, path\n%1\nnot found.\n\nRefresh the compare."),
invalid);
return false;
}
}
return true;
}
/**
* @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?
* @param [in] openableForDir Are items openable if the items are directories?
* return false if there was error or item was completely processed.
*/
bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, DIFFITEM *pos1, DIFFITEM *pos2, const DIFFITEM *pdi[3],
PathContext & paths, int & sel1, int & sel2, bool & isDir, int nPane[3], FileTextEncoding encoding[3], String& errmsg, bool openableForDir /*= true*/)
{
// 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], openableForDir))
{
return false;
}
switch (selectionType)
{
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]]);
encoding[0] = pdi[0]->diffFileInfo[nPane[0]].encoding;
encoding[1] = pdi[1]->diffFileInfo[nPane[1]].encoding;
if (pdi[0]->diffcode.isDirectory())
{
isDir = true;
if (paths::GetPairComparability(paths) != paths::IS_EXISTING_DIR)
{
errmsg = _("Selected folder is invalid.");
return false;
}
}
return true;
}
/**
* @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?
* @param [in] openableForDir Are items openable if the items are directories?
* return false if there was error or item was completely processed.
*/
bool GetOpenThreeItems(const CDiffContext& ctxt, DIFFITEM *pos1, DIFFITEM *pos2, DIFFITEM *pos3, const DIFFITEM *pdi[3],
PathContext & paths, int & sel1, int & sel2, int & sel3, bool & isDir, int nPane[3], FileTextEncoding encoding[3], String& errmsg, bool openableForDir /*= true*/)
{
assert(pos1 && pos2 && pos3 && ctxt.GetCompareDirs() > 2);
String pathLeft, pathMiddle, pathRight;
for (int nIndex = 0; nIndex < 3; ++nIndex)
nPane[nIndex] = 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], openableForDir))
{
return false;
}
// Ensure that pdi[0] is on left (swap if needed)
if (pdi[0]->diffcode.exists(0) && pdi[1]->diffcode.exists(1) && pdi[2]->diffcode.exists(2))
{
}
else if (pdi[0]->diffcode.exists(0) && pdi[1]->diffcode.exists(2) && pdi[2]->diffcode.exists(1))
{
std::swap(pdi[1], pdi[2]);
std::swap(nPane[1], nPane[2]);
std::swap(sel2, sel3);
}
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(nPane[0], nPane[1]);
std::swap(sel1, sel2);
}
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(nPane[0], nPane[2]);
std::swap(sel1, sel3);
std::swap(pdi[1], pdi[2]);
std::swap(nPane[1], nPane[2]);
std::swap(sel2, sel3);
}
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(nPane[0], nPane[1]);
std::swap(sel1, sel2);
std::swap(pdi[1], pdi[2]);
std::swap(nPane[1], nPane[2]);
std::swap(sel2, sel3);
}
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(nPane[0], nPane[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);
paths.SetMiddle(pathMiddle);
paths.SetRight(pathRight);
encoding[0] = pdi[0]->diffFileInfo[0].encoding;
encoding[1] = pdi[1]->diffFileInfo[1].encoding;
encoding[2] = pdi[2]->diffFileInfo[2].encoding;
if (pdi[0]->diffcode.isDirectory())
{
isDir = true;
if (paths::GetPairComparability(paths) != paths::IS_EXISTING_DIR)
{
errmsg = _("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.
*/
void GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM &di, String& strLeft, String& strRight)
{
const String leftrelpath = paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename);
const String rightrelpath = paths::ConcatPath(di.diffFileInfo[1].path, di.diffFileInfo[1].filename);
const String & leftpath = ctxt.GetPath(0);
const String & rightpath = ctxt.GetPath(1);
strLeft = paths::ConcatPath(leftpath, leftrelpath);
strRight = paths::ConcatPath(rightpath, rightrelpath);
}
String GetItemFileName(const CDiffContext& ctxt, const DIFFITEM &di, int index)
{
return paths::ConcatPath(ctxt.GetPath(index), paths::ConcatPath(di.diffFileInfo[index].path, di.diffFileInfo[index].filename));
}
PathContext GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM &di)
{
PathContext paths;
for (int nIndex = 0; nIndex < ctxt.GetCompareDirs(); nIndex++)
{
const String relpath = paths::ConcatPath(di.diffFileInfo[nIndex].path, di.diffFileInfo[nIndex].filename);
const String & path = ctxt.GetPath(nIndex);
paths.SetPath(nIndex, paths::ConcatPath(path, relpath));
}
return paths;
}
/**
* @brief Return image index appropriate for this row
*/
int GetColImage(const DIFFITEM &di)
{
// 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 ((di.diffcode.diffcode & DIFFCODE::THREEWAY) == 0 ?
(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 ((di.diffcode.diffcode & DIFFCODE::THREEWAY) != 0)
{
if (!di.diffcode.exists(0))
return (di.diffcode.isDirectory() ? DIFFIMG_LDIRMISSING : DIFFIMG_LMISSING);
if (!di.diffcode.exists(1))
return (di.diffcode.isDirectory() ? DIFFIMG_MDIRMISSING : DIFFIMG_MMISSING);
if (!di.diffcode.exists(2))
return (di.diffcode.isDirectory() ? DIFFIMG_RDIRMISSING : DIFFIMG_RMISSING);
}
if (di.diffcode.isResultSame())
{
if (di.diffcode.isDirectory())
return DIFFIMG_DIRSAME;
else
{
if (di.diffcode.isText())
return DIFFIMG_TEXTSAME;
else if (di.diffcode.isBin())
return DIFFIMG_BINSAME;
else if (di.diffcode.isImage())
return DIFFIMG_IMAGESAME;
else
return DIFFIMG_SAME;
}
}
// diff
if (di.diffcode.isResultDiff())
{
if (di.diffcode.isDirectory())
return DIFFIMG_DIRDIFF;
else
{
if (di.diffcode.isText())
return DIFFIMG_TEXTDIFF;
else if (di.diffcode.isBin())
return DIFFIMG_BINDIFF;
else if (di.diffcode.isImage())
return DIFFIMG_IMAGEDIFF;
else
return DIFFIMG_DIFF;
}
}
return (di.diffcode.isDirectory() ? DIFFIMG_DIR : DIFFIMG_FILE );
}
/**
* @brief Copy side status of diffitem
* @note This does not update UI - ReloadItemStatus() does
* @sa CDirDoc::ReloadItemStatus()
*/
void CopyDiffSideAndProperties(CDiffContext& ctxt, DIFFITEM& di, int src, int dst, int action)
{
if (IsItemCopyable(di, src, action == FileActionItem::UI_COPY_DIFFITEMS))
{
di.diffcode.diffcode |= (DIFFCODE::FIRST << dst);
// copy file properties other than ctime
di.diffFileInfo[dst].encoding = di.diffFileInfo[src].encoding;
di.diffFileInfo[dst].m_textStats = di.diffFileInfo[src].m_textStats;
di.diffFileInfo[dst].version = di.diffFileInfo[src].version;
di.diffFileInfo[dst].size = di.diffFileInfo[src].size;
di.diffFileInfo[dst].mtime = di.diffFileInfo[src].mtime;
di.diffFileInfo[dst].flags = di.diffFileInfo[src].flags;
}
if (di.HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
CopyDiffSideAndProperties(ctxt, *pdic, src, dst, action);
}
}
/**
* @brief Unset side status of diffitem
* @note This does not update UI - ReloadItemStatus() does
* @sa CDirDoc::ReloadItemStatus()
*/
void UnsetDiffSide(const CDiffContext& ctxt, DIFFITEM& di, int index)
{
di.diffcode.diffcode &= ~(DIFFCODE::FIRST << index);
di.diffFileInfo[index].ClearPartial();
// https://github.com/WinMerge/winmerge/issues/2599
for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
{
if (di.diffcode.exists(i))
{
di.diffFileInfo[index].filename = di.diffFileInfo[i].filename;
break;
}
}
di.nidiffs = CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE;
di.nsdiffs = CDiffContext::DIFFS_UNKNOWN_QUICKCOMPARE;
if (di.HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
UnsetDiffSide(ctxt, *pdic, index);
}
}
/**
* @brief Set compare status of diffitem
* @note This does not update UI - ReloadItemStatus() does
* @sa CDirDoc::ReloadItemStatus()
*/
void SetDiffCompare(DIFFITEM& di, unsigned diffcode)
{
SetDiffStatus(di, diffcode, DIFFCODE::COMPAREFLAGS);
}
/**
* @brief Set status for diffitem
* @param diffcode New code
* @param mask Defines allowed set of flags to change
* @param idx Item's index to list in UI
*/
void SetDiffStatus(DIFFITEM& di, unsigned diffcode, unsigned mask)
{
// TODO: Why is the update broken into these pieces ?
// Someone could figure out these pieces and probably simplify this.
// Update DIFFITEM code (comparison result)
assert( ((~mask) & diffcode) == 0 ); // make sure they only set flags in their mask
di.diffcode.diffcode &= (~mask); // remove current data
di.diffcode.diffcode |= diffcode; // add new data
if (di.HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
SetDiffStatus(*pdic, diffcode, mask);
}
// update DIFFITEM time (and other disk info), and tell views
}
void UpdateStatusFromDisk(CDiffContext& ctxt, DIFFITEM& di, int index)
{
ctxt.UpdateStatusFromDisk(&di, index);
if (di.HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
UpdateStatusFromDisk(ctxt, *pdic, index);
}
}
/**
* @brief Update compare flags recursively after sync.
* @param [in,out] di Item to update the compare flag.
* @param [in] bRecursive `true` if tree mode is on
* @return number of diff items
*/
int UpdateCompareFlagsAfterSync(DIFFITEM& di, bool bRecursive)
{
// Do not update compare flags for filtered items.
if (di.diffcode.isResultFiltered())
return 0;
int res = 0;
if (di.HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
{
int ndiff = UpdateCompareFlagsAfterSync(*pdic, bRecursive);
if (ndiff > 0)
{
res += ndiff;
}
}
// Update compare flags for items that exist on both sides.
// (Do not update compare flags for items that exist on only one side.)
if (di.diffcode.existAll())
{
di.diffcode.diffcode &= (~DIFFCODE::COMPAREFLAGS);
unsigned flag = (res > 0) ? DIFFCODE::DIFF : DIFFCODE::SAME;
di.diffcode.diffcode |= flag;
}
}
else
{
// Update compare flags for files and directories in tree mode.
// (Do not update directory compare flags when not in tree mode.)
if (!di.diffcode.isDirectory() || bRecursive)
{
if (di.diffcode.existAll())
{
di.diffcode.diffcode &= (~DIFFCODE::COMPAREFLAGS);
di.diffcode.diffcode |= DIFFCODE::SAME;
}
else
{
res++;
}
}
}
return res;
}
/**
* @brief Update the paths of the diff items recursively.
* @param[in] nDirs Number of directories to compare.
* @param[in,out] di Item to update the path.
*/
void UpdatePaths(int nDirs, DIFFITEM& di)
{
assert(nDirs == 2 || nDirs == 3);
if (di.HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
{
for (int i = 0; i < nDirs; i++)
pdic->diffFileInfo[i].path = paths::ConcatPath(di.diffFileInfo[i].path, di.diffFileInfo[i].filename);
UpdatePaths(nDirs, *pdic);
}
}
}
void SetDiffCounts(DIFFITEM& di, unsigned diffs, unsigned ignored)
{
di.nidiffs = ignored; // see StoreDiffResult() in DirScan.cpp
di.nsdiffs = diffs;
}
/**
* @brief Set item's view-flag.
* @param [in] key Item fow which flag is set.
* @param [in] flag Flag value to set.
* @param [in] mask Mask for possible flag values.
*/
void SetItemViewFlag(DIFFITEM& di, unsigned flag, unsigned mask)
{
unsigned curFlags = di.customFlags;
curFlags &= ~mask; // Zero bits masked
curFlags |= flag;
di.customFlags = curFlags;
}
/**
* @brief Set all item's view-flag.
* @param [in] flag Flag value to set.
* @param [in] mask Mask for possible flag values.
*/
void SetItemViewFlag(CDiffContext& ctxt, unsigned flag, unsigned mask)
{
DIFFITEM *pos = ctxt.GetFirstDiffPosition();
while (pos != nullptr)
{
unsigned curFlags = ctxt.GetCustomFlags1(pos);
curFlags &= ~mask; // Zero bits masked
curFlags |= flag;
ctxt.SetCustomFlags1(pos, curFlags);
ctxt.GetNextDiffPosition(pos);
}
}
/**
* @brief Mark selected items as needing for rescan.
* @return Count of items to rescan.
*/
void MarkForRescan(DIFFITEM &di)
{
SetDiffStatus(di, 0, DIFFCODE::TEXTFLAGS | DIFFCODE::SIDEFLAGS | DIFFCODE::COMPAREFLAGS);
SetDiffStatus(di, DIFFCODE::NEEDSCAN, DIFFCODE::SCANFLAGS);
}
/**
* @brief Return string such as "15 of 30 Files Affected" or "30 Files Affected"
*/
String FormatFilesAffectedString(int nFilesAffected, int nFilesTotal)
{
if (nFilesAffected == nFilesTotal)
return strutils::format_string1(_("(%1 Files Affected)"), NumToStr(nFilesTotal));
else
return strutils::format_string2(_("(%1 of %2 Files Affected)"), NumToStr(nFilesAffected), NumToStr(nFilesTotal));
}
String FormatMenuItemString(const String& fmt1, const String& fmt2, int count, int total)
{
if (count == total)
return strutils::format_string1(fmt1, NumToStr(total));
else
return strutils::format_string2(fmt2, NumToStr(count), NumToStr(total));
}
String FormatMenuItemString(SIDE_TYPE src, SIDE_TYPE dst, int count, int total)
{
String fmt1, fmt2;
if (src == SIDE_LEFT && dst == SIDE_RIGHT)
{
fmt1 = _("Left to Right (%1)");
fmt2 = _("Left to Right (%1 of %2)");
}
else if (src == SIDE_LEFT && dst == SIDE_MIDDLE)
{
fmt1 = _("Left to Middle (%1)");
fmt2 = _("Left to Middle (%1 of %2)");
}
else if (src == SIDE_MIDDLE && dst == SIDE_LEFT)
{
fmt1 = _("Middle to Left (%1)");
fmt2 = _("Middle to Left (%1 of %2)");
}
else if (src == SIDE_MIDDLE && dst == SIDE_RIGHT)
{
fmt1 = _("Middle to Right (%1)");
fmt2 = _("Middle to Right (%1 of %2)");
}
else if (src == SIDE_RIGHT && dst == SIDE_LEFT)
{
fmt1 = _("Right to Left (%1)");
fmt2 = _("Right to Left (%1 of %2)");
}
else if (src == SIDE_RIGHT && dst == SIDE_MIDDLE)
{
fmt1 = _("Right to Middle (%1)");
fmt2 = _("Right to Middle (%1 of %2)");
}
return FormatMenuItemString(fmt1, fmt2, count, total);
}
String FormatMenuItemString(SIDE_TYPE src, int count, int total)
{
String fmt1, fmt2;
if (src == SIDE_LEFT)
{
fmt1 = _("Left (%1)");
fmt2 = _("Left (%1 of %2)");
}
else if (src == SIDE_MIDDLE)
{
fmt1 = _("Middle (%1)");
fmt2 = _("Middle (%1 of %2)");
}
else if (src == SIDE_RIGHT)
{
fmt1 = _("Right (%1)");
fmt2 = _("Right (%1 of %2)");
}
return FormatMenuItemString(fmt1, fmt2, count, total);
}
String FormatMenuItemStringAll(int nDirs, int count, int total)
{
if (nDirs < 3)
return FormatMenuItemString(_("Both (%1)"), _("Both (%1 of %2)"), count, total);
else
return FormatMenuItemString(_("All (%1)"), _("All (%1 of %2)"), count, total);
}
String FormatMenuItemStringTo(SIDE_TYPE src, int count, int total)
{
String fmt1, fmt2;
if (src == SIDE_LEFT)
{
fmt1 = _("Left to... (%1)");
fmt2 = _("Left to... (%1 of %2)");
}
else if (src == SIDE_MIDDLE)
{
fmt1 = _("Middle to... (%1)");
fmt2 = _("Middle to... (%1 of %2)");
}
else if (src == SIDE_RIGHT)
{
fmt1 = _("Right to... (%1)");
fmt2 = _("Right to... (%1 of %2)");
}
return FormatMenuItemString(fmt1, fmt2, count, total);
}
String FormatMenuItemStringAllTo(int nDirs, int count, int total)
{
if (nDirs < 3)
return FormatMenuItemString(_("Both to... (%1)"), _("Both to... (%1 of %2)"), count, total);
else
return FormatMenuItemString(_("All to... (%1)"), _("All to... (%1 of %2)"), count, total);
}
String FormatMenuItemStringDifferencesTo(int count, int total)
{
return FormatMenuItemString(_("Differences to... (%1)"), _("Differences to... (%1 of %2)"), count, total);
}
/**
* @brief Rename a file without moving it to different directory.
*
* @param szOldFileName [in] Full path of file to rename.
* @param szNewFileName [in] New file name (without the path).
*
* @return true if file was renamed successfully.
*/
bool RenameOnSameDir(const String& szOldFileName, const String& szNewFileName)
{
bool bSuccess = false;
if (paths::DOES_NOT_EXIST != paths::DoesPathExist(szOldFileName))
{
String sFullName = paths::ConcatPath(paths::GetPathOnly(szOldFileName), szNewFileName);
// No need to rename if new file already exist.
if ((sFullName != szOldFileName) ||
(paths::DOES_NOT_EXIST == paths::DoesPathExist(sFullName)))
{
ShellFileOperations fileOp;
fileOp.SetOperation(FO_RENAME, 0);
fileOp.AddSourceAndDestination(szOldFileName, sFullName);
bSuccess = fileOp.Run();
}
else
{
bSuccess = true;
}
}
return bSuccess;
}
/**
* @brief Convert number to string.
* Converts number to string, with commas between digits in
* locale-appropriate manner.
*/
String NumToStr(int n)
{
return locality::NumToLocaleStr(n);
}
void ExpandSubdirs(CDiffContext& ctxt, DIFFITEM& dip)
{
dip.customFlags |= ViewCustomFlags::EXPANDED;
DIFFITEM *diffpos = ctxt.GetFirstChildDiffPosition(&dip);
while (diffpos != nullptr)
{
DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
if (!di.IsAncestor(&dip))
break;
if (di.HasChildren())
di.customFlags |= ViewCustomFlags::EXPANDED;
}
}
void ExpandAllSubdirs(CDiffContext& ctxt)
{
DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
while (diffpos != nullptr)
{
DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
if (di.diffcode.isDirectory())
di.customFlags |= ViewCustomFlags::EXPANDED;
}
}
void ExpandDifferentSubdirs(CDiffContext& ctxt)
{
DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
while (diffpos != nullptr)
{
DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
if (di.diffcode.isDirectory() && (di.diffcode.isResultDiff() || !di.diffcode.existAll()))
di.customFlags |= ViewCustomFlags::EXPANDED;
}
}
void ExpandIdenticalSubdirs(CDiffContext& ctxt)
{
DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
while (diffpos != nullptr)
{
DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
if (di.diffcode.isDirectory() && di.diffcode.isResultSame())
di.customFlags |= ViewCustomFlags::EXPANDED;
}
}
void CollapseAllSubdirs(CDiffContext& ctxt)
{
DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
while (diffpos != nullptr)
{
DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
di.customFlags &= ~ViewCustomFlags::EXPANDED;
}
}
DirViewTreeState *SaveTreeState(const CDiffContext& ctxt)
{
DirViewTreeState *pTreeState = new DirViewTreeState();
DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
while (diffpos != nullptr)
{
const DIFFITEM &di = ctxt.GetNextDiffPosition(diffpos);
if (di.HasChildren())
{
String relpath = paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename);
pTreeState->insert(std::pair<String, bool>(relpath, !!(di.customFlags & ViewCustomFlags::EXPANDED)));
}
}
return pTreeState;
}
void RestoreTreeState(CDiffContext& ctxt, DirViewTreeState *pTreeState)
{
DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
while (diffpos != nullptr)
{
DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
if (di.HasChildren())
{
String relpath = paths::ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename);
std::map<String, bool>::iterator p = pTreeState->find(relpath);
if (p != pTreeState->end())
{
di.customFlags &= ~ViewCustomFlags::EXPANDED;
di.customFlags |= (p->second ? ViewCustomFlags::EXPANDED : 0);
}
}
}
}
/**
* @brief Tell if user may use ".." and move to parents directory.
* This function checks if current compare's parent folders should
* be allowed to open. I.e. if current compare folders are:
* - C:\Work\Project1 and
* - C:\Work\Project2
* we check if C:\Work and C:\Work should be allowed to opened.
* For regular folders we allow opening if both folders exist.
* @param [out] leftParent Left parent folder to open.
* @param [out] rightParent Right parent folder to open.
* @return Info if opening parent folders should be enabled:
* - No : upward RESTRICTED
* - ParentIsRegularPath : upward ENABLED
* - ParentIsTempPath : upward ENABLED
*/
AllowUpwardDirectory::ReturnCode
CheckAllowUpwardDirectory(const CDiffContext& ctxt, const CTempPathContext *pTempPathContext, PathContext &pathsParent)
{
std::vector<String> path(ctxt.GetCompareDirs());
for (int i = 0; i < static_cast<int>(path.size()); ++i)
path[i] = ctxt.GetNormalizedPath(i);
// If we have temp context it means we are comparing archives
if (pTempPathContext != nullptr)
{
std::vector<String> name(path.size());
for (int i = 0; i < static_cast<int>(path.size()); ++i)
name[i] = paths::FindFileName(path[i]);
String::size_type cchLeftRoot = pTempPathContext->m_strRoot[0].length();
if (path[0].length() <= cchLeftRoot)
{
pathsParent.SetSize(ctxt.GetCompareDirs());
if (pTempPathContext->m_pParent)
{
for (int i = 0; i < static_cast<int>(path.size()); ++i)
pathsParent[i] = pTempPathContext->m_pParent->m_strRoot[i];
if (paths::GetPairComparability(pathsParent) != paths::IS_EXISTING_DIR)
return AllowUpwardDirectory::Never;
return AllowUpwardDirectory::ParentIsTempPath;
}
for (int i = 0; i < static_cast<int>(path.size()); ++i)
pathsParent[i] = pTempPathContext->m_strDisplayRoot[i];
if (pathsParent.GetSize() < 3)
{
if (!ctxt.m_piFilterGlobal->includeFile(pathsParent[0], pathsParent[1]))
return AllowUpwardDirectory::Never;
}
else
{
if (!ctxt.m_piFilterGlobal->includeFile(pathsParent[0], pathsParent[1], pathsParent[2]))
return AllowUpwardDirectory::Never;
}
if (path.size() == 2 &&
strutils::compare_nocase(name[0], _T("ORIGINAL")) == 0 &&
strutils::compare_nocase(name[1], _T("ALTERED")) == 0)
{
for (int i = 0; i < static_cast<int>(path.size()); ++i)
pathsParent[i] = paths::GetParentPath(pathsParent[i]);
for (int i = 0; i < static_cast<int>(path.size()); ++i)
name[i] = paths::FindFileName(pathsParent[i]);
if (strutils::compare_nocase(name[0], name[1]) == 0)
{
if (paths::GetPairComparability(pathsParent) != paths::IS_EXISTING_DIR)
return AllowUpwardDirectory::Never;
return AllowUpwardDirectory::ParentIsTempPath;
}
}
return AllowUpwardDirectory::No;
}
}
// If regular parent folders exist, allow opening them
pathsParent.SetSize(ctxt.GetCompareDirs());
for (int i = 0; i < static_cast<int>(path.size()); ++i)
pathsParent[i] = paths::GetParentPath(path[i]);
if (paths::GetPairComparability(pathsParent) != paths::IS_EXISTING_DIR)
return AllowUpwardDirectory::Never;
return AllowUpwardDirectory::ParentIsRegularPath;
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/mirrors/WinMerge.git
git@gitee.com:mirrors/WinMerge.git
mirrors
WinMerge
WinMerge
master

搜索帮助