Skip to content

Commit

Permalink
Add a Prompt to Copy Only Differences in Folder Comparison (#2622)
Browse files Browse the repository at this point in the history
  • Loading branch information
sdottaka authored Jan 22, 2025
1 parent 2b2ad2a commit 3863b78
Show file tree
Hide file tree
Showing 47 changed files with 220 additions and 38 deletions.
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

0 comments on commit 3863b78

Please sign in to comment.