Skip to content

Add a Prompt to Copy Only Differences in Folder Comparison #2622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jan 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions Src/DirActions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,11 @@ UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContex
// UI dependent.
switch (act.UIResult)
{
case FileActionItem::UI_SYNC:
case FileActionItem::UI_COPY:
case FileActionItem::UI_COPY_DIFFITEMS:
bUpdateSrc = true;
bUpdateDest = true;
CopyDiffSideAndProperties(di, act.UIOrigin, act.UIDestination);
CopyDiffSideAndProperties(ctxt, di, act.UIOrigin, act.UIDestination, act.UIResult);
if (ctxt.GetCompareDirs() > 2)
SetDiffCompare(di, DIFFCODE::NOCMP);
else
Expand All @@ -266,7 +267,7 @@ UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContex
case FileActionItem::UI_MOVE:
bUpdateSrc = true;
bUpdateDest = true;
CopyDiffSideAndProperties(di, act.UIOrigin, act.UIDestination);
CopyDiffSideAndProperties(ctxt, di, act.UIOrigin, act.UIDestination, act.UIResult);
UnsetDiffSide(ctxt, di, act.UIOrigin);
SetDiffCompare(di, DIFFCODE::NOCMP);
break;
Expand Down Expand Up @@ -356,13 +357,15 @@ DIFFITEM *FindItemFromPaths(const CDiffContext& ctxt, const PathContext& paths)
return 0;
}

/// is it possible to copy item to left ?
bool IsItemCopyable(const DIFFITEM &di, int index)
bool IsItemCopyable(const DIFFITEM &di, int index, bool 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;
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;
}
// impossible if not existing
if (!di.diffcode.exists(index)) return false;
// everything else can be copied to other side
Expand Down Expand Up @@ -1068,9 +1071,9 @@ int GetColImage(const DIFFITEM &di)
* @note This does not update UI - ReloadItemStatus() does
* @sa CDirDoc::ReloadItemStatus()
*/
void CopyDiffSideAndProperties(DIFFITEM& di, int src, int dst)
void CopyDiffSideAndProperties(CDiffContext& ctxt, DIFFITEM& di, int src, int dst, int action)
{
if (di.diffcode.exists(src))
if (IsItemCopyable(di, src, action == FileActionItem::UI_COPY_DIFFITEMS))
{
di.diffcode.diffcode |= (DIFFCODE::FIRST << dst);
// copy file properties other than ctime
Expand All @@ -1084,7 +1087,7 @@ void CopyDiffSideAndProperties(DIFFITEM& di, int src, int dst)
if (di.HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
CopyDiffSideAndProperties(*pdic, src, dst);
CopyDiffSideAndProperties(ctxt, *pdic, src, dst, action);
}
}

Expand Down Expand Up @@ -1192,7 +1195,8 @@ int UpdateCompareFlagsAfterSync(DIFFITEM& di, bool bRecursive)
di.diffcode.diffcode |= flag;
}
}
else {
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)
Expand Down
38 changes: 31 additions & 7 deletions Src/DirActions.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContex

DIFFITEM *FindItemFromPaths(const CDiffContext& ctxt, const PathContext& paths);

bool IsItemCopyable(const DIFFITEM &di, int index);
bool IsItemCopyable(const DIFFITEM &di, int index, bool copyOnlyDiffItems);
bool IsItemMovable(const DIFFITEM &di, int index);
bool IsItemDeletable(const DIFFITEM &di, int index);
bool IsItemDeletableOnBoth(const CDiffContext& ctxt, const DIFFITEM &di);
Expand All @@ -166,7 +166,7 @@ int GetColImage(const DIFFITEM &di);

void SetDiffStatus(DIFFITEM& di, unsigned diffcode, unsigned mask);
void SetDiffCompare(DIFFITEM& di, unsigned diffcode);
void CopyDiffSideAndProperties(DIFFITEM& di, int src, int dst);
void CopyDiffSideAndProperties(CDiffContext& ctxt, DIFFITEM& di, int src, int dst, int action);
void UnsetDiffSide(const CDiffContext& ctxt, DIFFITEM& di, int index);
void UpdateStatusFromDisk(CDiffContext& ctxt, DIFFITEM& di, int index);
int UpdateCompareFlagsAfterSync(DIFFITEM& di, bool bRecursive);
Expand Down Expand Up @@ -232,7 +232,7 @@ struct DirActions
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)));
return (di.diffcode.diffcode != 0 && !m_RO[SideToIndex(m_ctxt, dst)] && ::IsItemCopyable(di, SideToIndex(m_ctxt, src), false));
}

template <SIDE_TYPE src>
Expand Down Expand Up @@ -361,13 +361,31 @@ struct DirActions
return ::IsItemNavigableDiff(m_ctxt, di);
}

FileActionScript *CopyItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, SIDE_TYPE src, SIDE_TYPE dst) const
bool IsItemIdenticalOrSkipped(const DIFFITEM& di) const
{
if (!di.HasChildren())
return (di.diffcode.diffcode != 0 && (di.diffcode.isResultSame() || di.diffcode.isResultFiltered() || di.diffcode.isResultError()));
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
{
if (IsItemIdenticalOrSkipped(*pdic))
return true;
}
return false;
}

FileActionScript *CopyItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, bool copyOnlyDiffItems, 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))
if (di.diffcode.diffcode != 0 && !m_RO[dstidx] && IsItemCopyable(di, srcidx, copyOnlyDiffItems))
{
if (it.second->HasChildren())
{
for (DIFFITEM* pdic = di.GetFirstChild(); pdic; pdic = pdic->GetFwdSiblingLink())
CopyItem(pscript, { it.first, pdic }, copyOnlyDiffItems, src, dst);
return pscript;
}
FileActionItem act;
act.src = GetItemFileName(m_ctxt, di, srcidx);
act.dest = GetItemFileName(m_ctxt, di, dstidx);
Expand All @@ -379,18 +397,24 @@ struct DirActions
act.context = it.first;
act.dirflag = di.diffcode.isDirectory();
act.atype = FileAction::ACT_COPY;
act.UIResult = FileActionItem::UI_SYNC;
act.UIResult = copyOnlyDiffItems ? FileActionItem::UI_COPY_DIFFITEMS : FileActionItem::UI_COPY;
act.UIOrigin = srcidx;
act.UIDestination = dstidx;
pscript->AddActionItem(act);
}
return pscript;
}

template<SIDE_TYPE src, SIDE_TYPE to>
FileActionScript *CopyDiffItems(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it) const
{
return CopyItem(pscript, it, true, src, to);
}

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);
return CopyItem(pscript, it, false, src, to);
}

FileActionScript *MoveItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, SIDE_TYPE src, SIDE_TYPE dst) const
Expand Down
34 changes: 23 additions & 11 deletions Src/DirView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -710,10 +710,7 @@ static void NTAPI FormatContextMenu(BCMenu *pPopup, UINT uIDItem, int n1, int n2
*/
static void NTAPI CheckContextMenu(BCMenu *pPopup, UINT uIDItem, BOOL bCheck)
{
if (bCheck)
pPopup->CheckMenuItem(uIDItem, MF_CHECKED);
else
pPopup->CheckMenuItem(uIDItem, MF_UNCHECKED);
pPopup->CheckMenuItem(uIDItem, bCheck ? MF_CHECKED : MF_UNCHECKED);
}

/**
Expand Down Expand Up @@ -831,9 +828,9 @@ void CDirView::OnDirCopy(UINT id)
if (GetDocument()->m_nDirs < 3)
{
if (to_right)
DoDirAction(&DirActions::Copy<SIDE_LEFT, SIDE_RIGHT>, _("Copying files..."));
OnCtxtDirCopy<SIDE_LEFT, SIDE_RIGHT>();
else
DoDirAction(&DirActions::Copy<SIDE_RIGHT, SIDE_LEFT>, _("Copying files..."));
OnCtxtDirCopy<SIDE_RIGHT, SIDE_LEFT>();
}
else
{
Expand All @@ -842,11 +839,11 @@ void CDirView::OnDirCopy(UINT id)
switch (m_nActivePane)
{
case 0:
DoDirAction(&DirActions::Copy<SIDE_LEFT, SIDE_MIDDLE>, _("Copying files..."));
OnCtxtDirCopy<SIDE_LEFT, SIDE_MIDDLE>();
break;
case 1:
case 2:
DoDirAction(&DirActions::Copy<SIDE_MIDDLE, SIDE_RIGHT>, _("Copying files..."));
OnCtxtDirCopy<SIDE_MIDDLE, SIDE_RIGHT>();
break;
}
}
Expand All @@ -856,10 +853,10 @@ void CDirView::OnDirCopy(UINT id)
{
case 0:
case 1:
DoDirAction(&DirActions::Copy<SIDE_MIDDLE, SIDE_LEFT>, _("Copying files..."));
OnCtxtDirCopy<SIDE_MIDDLE, SIDE_LEFT>();
break;
case 2:
DoDirAction(&DirActions::Copy<SIDE_RIGHT, SIDE_MIDDLE>, _("Copying files..."));
OnCtxtDirCopy<SIDE_RIGHT, SIDE_MIDDLE>();
break;
}
}
Expand All @@ -870,7 +867,20 @@ void CDirView::OnDirCopy(UINT id)
template<SIDE_TYPE srctype, SIDE_TYPE dsttype>
void CDirView::OnCtxtDirCopy()
{
DoDirAction(&DirActions::Copy<srctype, dsttype>, _("Copying files..."));
bool copyOnlyDiffItems = true;
Counts counts = Count(&DirActions::IsItemIdenticalOrSkipped);
if (counts.count > 0)
{
int ans = AfxMessageBox(_("Some selected items are identical or skipped.\nDo you want to copy only the items with differences?").c_str(),
MB_YESNOCANCEL | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_COPY_ONLYDIFFITEMS);
if (ans == IDCANCEL)
return;
copyOnlyDiffItems = (ans == IDYES);
}
if (copyOnlyDiffItems)
DoDirAction(&DirActions::CopyDiffItems<srctype, dsttype>, _("Copying files..."));
else
DoDirAction(&DirActions::Copy<srctype, dsttype>, _("Copying files..."));
}

/// User chose (context menu) Copy left to...
Expand Down Expand Up @@ -941,6 +951,7 @@ void CDirView::DoDirAction(DirActions::method_type func, const String& status_me
FileActionScript *rsltScript;
rsltScript = std::accumulate(begin, end, &actionScript, MakeDirActions(func));
ASSERT(rsltScript == &actionScript);
actionScript.RemoveDuplicates();
// Now we prompt, and execute actions
ConfirmAndPerformActions(actionScript);
} catch (ContentsChangedException& e) {
Expand Down Expand Up @@ -978,6 +989,7 @@ void CDirView::DoDirActionTo(SIDE_TYPE stype, DirActions::method_type func, cons
FileActionScript *rsltScript;
rsltScript = std::accumulate(begin, end, &actionScript, MakeDirActions(func));
ASSERT(rsltScript == &actionScript);
actionScript.RemoveDuplicates();
// Now we prompt, and execute actions
ConfirmAndPerformActions(actionScript);
} catch (ContentsChangedException& e) {
Expand Down
1 change: 1 addition & 0 deletions Src/DirView.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class CDirView : public CListView
void DoOpenWithEditor(SIDE_TYPE stype);
void DoOpenParentFolder(SIDE_TYPE stype);
void DoUpdateOpen(SELECTIONTYPE selectionType, CCmdUI* pCmdUI, bool openableForDir = true);
void RemoveDuplicatedActions(FileActionScript & actions);
void ConfirmAndPerformActions(FileActionScript & actions);
void PerformActionList(FileActionScript & actions);
void UpdateAfterFileScript(FileActionScript & actionList);
Expand Down
18 changes: 18 additions & 0 deletions Src/FileActionScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ FileActionItem FileActionScript::RemoveTailActionItem()
return item;
}

/**
* @brief Removes duplicate entries from a vector of FileActionItem objects.
*/
void FileActionScript::RemoveDuplicates()
{
auto compare = [](const FileActionItem& a, const FileActionItem& b) {
return std::tie(a.src, a.dest, a.atype) < std::tie(b.src, b.dest, b.atype);
};

auto equal = [](const FileActionItem& a, const FileActionItem& b) {
return std::tie(a.src, a.dest, a.atype) == std::tie(b.src, b.dest, b.atype);
};

std::sort(m_actions.begin(), m_actions.end(), compare);

m_actions.erase(std::unique(m_actions.begin(), m_actions.end(), equal), m_actions.end());
}

/**
* @brief Create ShellFileOperations operation lists from our scripts.
*
Expand Down
12 changes: 7 additions & 5 deletions Src/FileActionScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ struct FileActionItem : public FileAction
*/
enum UI_RESULT
{
UI_SYNC = 1, /**< Make items identical (synchronized). */
UI_MOVE, /**< Move items. */
UI_DEL, /**< Remove left item. */
UI_DONT_CARE, /**< Ignore the GUI change. */
UI_RENAME /**< Rename item. */
UI_COPY = 1, /**< Copy items. */
UI_COPY_DIFFITEMS, /**< Copy diff items. */
UI_MOVE, /**< Move items. */
UI_DEL, /**< Remove left item. */
UI_DONT_CARE, /**< Ignore the GUI change. */
UI_RENAME /**< Rename item. */
};

/**
Expand Down Expand Up @@ -109,6 +110,7 @@ class FileActionScript
void AddActionItem(FileActionItem & item) { m_actions.push_back(item); }

FileActionItem RemoveTailActionItem();
void RemoveDuplicates();

/**
* Get first action item in the list.
Expand Down
1 change: 1 addition & 0 deletions Src/Merge.rc
Original file line number Diff line number Diff line change
Expand Up @@ -3809,6 +3809,7 @@ BEGIN
IDS_COPY_BOTH_TO2 "Both to... (%1 of %2)"
IDS_COPY_ALL_TO2 "All to... (%1 of %2)"
IDS_COPY_DIFFERENCES_TO2 "Differences to... (%1 of %2)"
IDS_COPY_ONLYDIFFITEMS "Some selected items are identical or skipped.\nDo you want to copy only the items with differences?"
END

// DIRECTORY DIFFING : FILE COPY/DELETE (WITHOUT/WITH NUMBER MARK) (2)
Expand Down
1 change: 1 addition & 0 deletions Src/PropMessageBoxes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ static struct MessageBox
{ IDS_MOVE_TO_LASTFILE, IDS_MOVE_TO_LASTFILE, nullptr, MB_YESNO | MB_DONT_ASK_AGAIN },
{ IDS_MOVE_TO_NEXTPAGE, IDS_MOVE_TO_NEXTPAGE, nullptr, MB_YESNO | MB_DONT_ASK_AGAIN },
{ IDS_MOVE_TO_PREVPAGE, IDS_MOVE_TO_PREVPAGE, nullptr, MB_YESNO | MB_DONT_ASK_AGAIN },
{ IDS_COPY_ONLYDIFFITEMS, IDS_COPY_ONLYDIFFITEMS, nullptr, MB_YESNOCANCEL | MB_ICONWARNING | MB_DONT_ASK_AGAIN },
// report dialog
{ IDS_REPORT_FILEOVERWRITE, IDS_REPORT_FILEOVERWRITE, nullptr, MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN },
// patch dialog
Expand Down
1 change: 1 addition & 0 deletions Src/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -1801,6 +1801,7 @@
#define IDS_COPY_GRANULARITY_LINE 44643
#define IDS_COPY_GRANULARITY_Character 44644
#define IDS_VIEW_MENU_BAR 44645
#define IDS_COPY_ONLYDIFFITEMS 44646

// Next default values for new objects
//
Expand Down
3 changes: 3 additions & 0 deletions Translations/WinMerge/Arabic.po
Original file line number Diff line number Diff line change
Expand Up @@ -2956,6 +2956,9 @@ msgstr "الكل إلى... (%1)"
msgid "Differences to... (%1)"
msgstr "الاختلافات إلى... (%1)"

msgid "Some selected items are identical or skipped.\nDo you want to copy only the items with differences?"
msgstr ""

#, c-format
msgid "Left (%1)"
msgstr "اليسار (%1)"
Expand Down
3 changes: 3 additions & 0 deletions Translations/WinMerge/Basque.po
Original file line number Diff line number Diff line change
Expand Up @@ -3478,6 +3478,9 @@ msgstr ""
msgid "Differences to... (%1)"
msgstr ""

msgid "Some selected items are identical or skipped.\nDo you want to copy only the items with differences?"
msgstr ""

#, c-format
msgid "Left (%1)"
msgstr "Ezkerra (%1)"
Expand Down
3 changes: 3 additions & 0 deletions Translations/WinMerge/Brazilian.po
Original file line number Diff line number Diff line change
Expand Up @@ -2766,6 +2766,9 @@ msgstr "De Todos pra... (%1)"
msgid "Differences to... (%1)"
msgstr "Das Diferenças pra... (%1)"

msgid "Some selected items are identical or skipped.\nDo you want to copy only the items with differences?"
msgstr ""

#, c-format
msgid "Left (%1)"
msgstr "Esquerda (%1)"
Expand Down
3 changes: 3 additions & 0 deletions Translations/WinMerge/Bulgarian.po
Original file line number Diff line number Diff line change
Expand Up @@ -2975,6 +2975,9 @@ msgstr "Всички към… (%1)"
msgid "Differences to... (%1)"
msgstr "Различия към… (%1)"

msgid "Some selected items are identical or skipped.\nDo you want to copy only the items with differences?"
msgstr ""

#, c-format
msgid "Left (%1)"
msgstr "Ляво (%1)"
Expand Down
3 changes: 3 additions & 0 deletions Translations/WinMerge/Catalan.po
Original file line number Diff line number Diff line change
Expand Up @@ -3538,6 +3538,9 @@ msgstr "Tots a... (%1)"
msgid "Differences to... (%1)"
msgstr "Diferències a... (%1)"

msgid "Some selected items are identical or skipped.\nDo you want to copy only the items with differences?"
msgstr ""

#, c-format
msgid "Left (%1)"
msgstr "Esquerra (%1)"
Expand Down
3 changes: 3 additions & 0 deletions Translations/WinMerge/ChineseSimplified.po
Original file line number Diff line number Diff line change
Expand Up @@ -3000,6 +3000,9 @@ msgstr "全部到... (%1)"
msgid "Differences to... (%1)"
msgstr "差异到... (%1)"

msgid "Some selected items are identical or skipped.\nDo you want to copy only the items with differences?"
msgstr ""

#, c-format
msgid "Left (%1)"
msgstr "左侧 (%1)"
Expand Down
Loading
Loading