From b1d9c234468cc39b24d99d86b480ac3442efade7 Mon Sep 17 00:00:00 2001 From: Christian List Date: Wed, 15 Jan 2003 15:09:00 +0000 Subject: [PATCH] Patch [ 651689 ] Add OpenLeft, OpenLeftWith, ... context --- Src/Diff.cpp | 1 + Src/DiffContext.cpp | 54 ++++- Src/DiffContext.h | 25 ++- Src/DirActions.cpp | 549 +++++++++++++++++++++++++++++++++++++++++++++++++ Src/DirDoc.cpp | 89 ++++---- Src/DirView.cpp | 580 ++++++++++++++++++++-------------------------------- Src/DirView.h | 102 +++++++-- Src/MainFrm.cpp | 108 +++++----- Src/MainFrm.h | 14 +- Src/Merge.dsp | 12 ++ Src/Merge.rc | 51 ++++- Src/OutputDlg.cpp | 61 ++++++ Src/OutputDlg.h | 52 +++++ Src/resource.h | 66 ++++-- 14 files changed, 1255 insertions(+), 509 deletions(-) create mode 100644 Src/DirActions.cpp create mode 100644 Src/OutputDlg.cpp create mode 100644 Src/OutputDlg.h diff --git a/Src/Diff.cpp b/Src/Diff.cpp index f89196431..650903439 100644 --- a/Src/Diff.cpp +++ b/Src/Diff.cpp @@ -435,6 +435,7 @@ compare_files (LPCTSTR dir0, LPCTSTR name0, { /* Both exist and neither is a directory. */ int o_binary = always_text_flag ? O_BINARY : 0; + o_binary = O_BINARY; // Perry 2002-12-08 CTRL-Z screws things up in text mode if (!ignore_eol_diff) o_binary = O_BINARY; /* Open the files and record their descriptors. */ diff --git a/Src/DiffContext.cpp b/Src/DiffContext.cpp index 61ef60d7d..0c6778768 100644 --- a/Src/DiffContext.cpp +++ b/Src/DiffContext.cpp @@ -50,6 +50,12 @@ CDiffContext::CDiffContext(LPCTSTR pszLeft /*=NULL*/, LPCTSTR pszRight /*=NULL*/ CDiffContext::CDiffContext(LPCTSTR pszLeft, LPCTSTR pszRight, CDiffContext& src) { + // This is used somehow in recursive comparisons + // I think that it is only used during rescan to copy into temporaries + // which modify the original (because pointers are copied below) + // and then the temporary goes away + // so the temporary never exists while the user is interacting with the GUI + m_bRecurse=src.m_bRecurse; m_strLeft = pszLeft; m_strRight = pszRight; @@ -80,10 +86,14 @@ void CDiffContext::AddDiff(LPCTSTR pszFilename, LPCTSTR pszLeftDir, LPCTSTR pszR di.ltime = ltime; di.rtime = rtime; di.code = code; + AddDiff(di); +} - // BSP - Capture the extension; from the end of the file name to the last '.' +void CDiffContext::AddDiff(DIFFITEM di) +{ + // BSP - Capture the extension; from the end of the file name to the last '.' int j = 0; - TCHAR *pDest = _tcsrchr(pszFilename, _T('.') ); + TCHAR *pDest = _tcsrchr(di.filename, _T('.') ); if(pDest) // handle no extensions case. { @@ -99,7 +109,12 @@ void CDiffContext::AddDiff(LPCTSTR pszFilename, LPCTSTR pszLeftDir, LPCTSTR pszR m_pList->AddTail(di); if (m_piStatus) - m_piStatus->rptFile(code); + m_piStatus->rptFile(di.code); +} + +void CDiffContext::RemoveDiff(POSITION diffpos) +{ + m_pList->RemoveAt(diffpos); } void CDiffContext::SetRegExp(LPCTSTR pszExp) @@ -107,3 +122,36 @@ void CDiffContext::SetRegExp(LPCTSTR pszExp) m_strRegExp = pszExp; m_rgx.RegComp( pszExp ); } + +void CDiffContext::RemoveAll() +{ + m_pList->RemoveAll(); +} + +POSITION CDiffContext::GetFirstDiffPosition() +{ + return m_pList->GetHeadPosition(); +} + +DIFFITEM CDiffContext::GetNextDiffPosition(POSITION & diffpos) +{ + return m_pList->GetNext(diffpos); +} + +DIFFITEM CDiffContext::GetDiffAt(POSITION diffpos) +{ + return m_pList->GetAt(diffpos); +} + +BYTE CDiffContext::GetDiffStatus(POSITION diffpos) +{ + return m_pList->GetAt(diffpos).code; +} + + +void CDiffContext::UpdateStatusCode(POSITION diffpos, BYTE status) +{ + DIFFITEM & di = m_pList->GetAt(diffpos); + di.code = status; +} + diff --git a/Src/DiffContext.h b/Src/DiffContext.h index f111ba01d..f1e4a7fd0 100644 --- a/Src/DiffContext.h +++ b/Src/DiffContext.h @@ -43,13 +43,29 @@ public: class CDiffContext { public: - void SetRegExp(LPCTSTR pszExp); - void AddDiff(LPCTSTR pszFilename, LPCTSTR pszLeftDir, LPCTSTR pszRightDir, long ltime, long rtime, BYTE code); CDiffContext(LPCTSTR pszLeft, LPCTSTR pszRight, IDiffStatus * piStatus); CDiffContext(LPCTSTR pszLeft, LPCTSTR pszRight, CDiffContext& src); virtual ~CDiffContext(); - CList m_dirlist, *m_pList; + void SetRegExp(LPCTSTR pszExp); + + // add & remove differences + void AddDiff(LPCTSTR pszFilename, LPCTSTR pszLeftDir, LPCTSTR pszRightDir, long ltime, long rtime, BYTE code); + void AddDiff(DIFFITEM di); + void RemoveDiff(POSITION diffpos); + void RemoveAll(); + + // to iterate over all differences on list + POSITION GetFirstDiffPosition(); + DIFFITEM GetNextDiffPosition(POSITION & diffpos); + DIFFITEM GetDiffAt(POSITION diffpos); + BYTE GetDiffStatus(POSITION diffpos); + + // change an existing difference + void UpdateStatusCode(POSITION diffpos, BYTE status); + void ClearStatus() { m_piStatus=0; } + + BOOL m_bRecurse; CString m_strLeft; CString m_strRight; @@ -61,6 +77,9 @@ public: struct dirdata ddLeft, ddRight; char *pNamesLeft; char *pNamesRight; + +private: + CList m_dirlist, *m_pList; // master list of differences }; #endif // !defined(AFX_DIFFCONTEXT_H__D3CC86BE_F11E_11D2_826C_00A024706EDC__INCLUDED_) diff --git a/Src/DirActions.cpp b/Src/DirActions.cpp new file mode 100644 index 000000000..37e9109bc --- /dev/null +++ b/Src/DirActions.cpp @@ -0,0 +1,549 @@ +///////////////////////////////////////////////////////////////////////////// +// see Merge.cpp for license (GPLv2+) statement +// +///////////////////////////////////////////////////////////////////////////// +// DirActions.cpp : implementation file +// 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 "stdafx.h" +#include "Merge.h" +#include "DirView.h" +#include "DirDoc.h" +#include "MainFrm.h" +#include "coretools.h" +#include "OutputDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +// Prompt user to confirm a multiple item copy +static BOOL ConfirmMultipleCopy(int count, int total) +{ + CString s; + ASSERT(count>1); + AfxFormatString2(s, IDS_CONFIRM_COPY2DIR, NumToStr(count), NumToStr(total)); + int rtn = AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION); + return (rtn==IDYES); +} + +// Prompt user to confirm a single item copy +static BOOL ConfirmSingleCopy(LPCTSTR src, LPCTSTR dest) +{ + CString s; + AfxFormatString2(s, IDS_CONFIRM_COPY_SINGLE, src, dest); + int rtn = AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION); + return (rtn==IDYES); +} + +// Prompt user to confirm a multiple item delete +static BOOL ConfirmMultipleDelete(int count, int total) +{ + CString s; + AfxFormatString2(s, IDS_CONFIRM_DELETE_ITEMS, NumToStr(count), NumToStr(total)); + int rtn = AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION); + return (rtn==IDYES); +} + +// Prompt user to confirm a single item delete +static BOOL ConfirmSingleDelete(LPCTSTR filepath) +{ + CString s; + AfxFormatString1(s, IDS_CONFIRM_DELETE_SINGLE, filepath); + int rtn = AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION); + return (rtn==IDYES); +} + +static BOOL +IsItemCodeDir(int code) +{ + return code==FILE_LDIRUNIQUE || code==FILE_RDIRUNIQUE; +} + +// Prompt & copy item from right to left, if legal +void CDirView::DoCopyFileToLeft() +{ + ActionList actionList(ACT_COPY); + int sel=-1; + CString slFile, srFile; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) + { + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemCopyableToLeft(di.code)) + { + GetItemFileNames(sel, slFile, srFile); + action act; + act.src = srFile; + act.dest = slFile; + act.idx = sel; + act.dirflag = IsItemCodeDir(di.code); + actionList.actions.AddTail(act); + } + ++actionList.selcount; + } + + ConfirmAndPerformActions(actionList); +} +// Prompt & copy item from left to right, if legal +void CDirView::DoCopyFileToRight() +{ + ActionList actionList(ACT_COPY); + int sel=-1; + CString slFile, srFile; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) + { + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemCopyableToRight(di.code)) + { + GetItemFileNames(sel, slFile, srFile); + action act; + act.src = slFile; + act.dest = srFile; + act.dirflag = IsItemCodeDir(di.code); + act.idx = sel; + actionList.actions.AddTail(act); + } + ++actionList.selcount; + } + + ConfirmAndPerformActions(actionList); +} + +// Prompt & delete left, if legal +void CDirView::DoDelLeft() +{ + ActionList actionList(ACT_DEL_LEFT); + int sel=-1; + CString slFile, srFile; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) + { + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemDeletableOnLeft(di.code)) + { + GetItemFileNames(sel, slFile, srFile); + action act; + act.src = slFile; + act.dirflag = IsItemCodeDir(di.code); + act.idx = sel; + actionList.actions.AddTail(act); + } + ++actionList.selcount; + } + + ConfirmAndPerformActions(actionList); +} +// Prompt & delete right, if legal +void CDirView::DoDelRight() +{ + ActionList actionList(ACT_DEL_RIGHT); + int sel=-1; + CString slFile, srFile; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) + { + const DIFFITEM& di = GetDiffItem(sel); + + if (IsItemDeletableOnRight(di.code)) + { + GetItemFileNames(sel, slFile, srFile); + action act; + act.src = srFile; + act.dirflag = IsItemCodeDir(di.code); + act.idx = sel; + actionList.actions.AddTail(act); + } + ++actionList.selcount; + } + + ConfirmAndPerformActions(actionList); +} +// Prompt & delete both, if legal +void CDirView::DoDelBoth() +{ + ActionList actionList(ACT_DEL_BOTH); + int sel=-1; + CString slFile, srFile; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) + { + const DIFFITEM& di = GetDiffItem(sel); + + if (IsItemDeletableOnBoth(di.code)) + { + GetItemFileNames(sel, slFile, srFile); + action act; + act.src = srFile; + act.dest = slFile; + act.dirflag = IsItemCodeDir(di.code); + act.idx = sel; + actionList.actions.AddTail(act); + } + ++actionList.selcount; + } + + ConfirmAndPerformActions(actionList); +} + +// Confirm with user, then perform the action list +void CDirView::ConfirmAndPerformActions(ActionList & actionList) +{ + if (!actionList.selcount) // Not sure it is possible to get right-click menu without + return; // any selected items, but may as well be safe + + ASSERT(actionList.actions.GetCount()>0); // Or else the update handler got it wrong + + if (!ConfirmActionList(actionList)) + return; + + PerformActionList(actionList); +} + +// Confirm actions with user as appropriate (type, whether single or multiple) +BOOL CDirView::ConfirmActionList(const ActionList & actionList) +{ + // 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(actionList.atype) + { + case ACT_COPY: + if (actionList.GetCount()==1) + { + action & act = actionList.actions.GetHead(); + if (!ConfirmSingleCopy(act.src, act.dest)) + return FALSE; + } + else + { + if (!ConfirmMultipleCopy(actionList.GetCount(), actionList.selcount)) + return FALSE; + } + break; + default: // deletes + if (actionList.GetCount()==1 && actionList.atype!=ACT_DEL_BOTH) + { + action & act = actionList.actions.GetHead(); + if (!ConfirmSingleDelete(act.src)) + return FALSE; + } + else + { + if (!ConfirmMultipleDelete(actionList.GetCount(), actionList.selcount)) + return FALSE; + } + break; + } + return TRUE; +} + +// Perform an array of actions +void CDirView::PerformActionList(ActionList & actionList) +{ + while (actionList.GetCount()>0) + { + PerformAndRemoveTopAction(actionList); + } + + // must process deleted items in reverse order + // ie, work our way *up* the display list + // so we don't invalidate indices of items not done yet + while (!actionList.deletedItems.IsEmpty()) + { + int idx = actionList.deletedItems.RemoveTail(); + POSITION diffpos = GetItemKey(idx); + GetDiffContext()->RemoveDiff(diffpos); + m_pList->DeleteItem(idx); + } + + if (!actionList.errors.IsEmpty()) + { + CString sTitle, sText; + AfxFormatString1(sTitle, IDS_ERRORS_TITLE, NumToStr(actionList.errors.GetCount())); + while (!actionList.errors.IsEmpty()) + { + sText += actionList.errors.RemoveHead() + _T("\r\n\r\n"); + } + OutputBox(sTitle, sText); + } +} + +static void NormalizeFilepath(CString * pstr) +{ + pstr->Replace('/', '\\'); +} + +void CDirView::PerformAndRemoveTopAction(ActionList & actionList) +{ + action act = actionList.actions.RemoveHead(); + if (actionList.atype == ACT_COPY) + { + // copy + if (act.dirflag) + { + // TODO: copy directory + // add new entries to an append list to be added to list at end + } + else + { + CString s; + // copy single file, and update status immediately + if (mf->SyncFiles(act.src, act.dest, &s)) + { + mf->UpdateCurrentFileStatus(FILE_SAME, act.idx); + } + else + { + actionList.errors.AddTail(s); + } + } + } + else + { + // delete + if (act.dirflag) + { + // delete directory + CString s; + if (DeleteDirSilently(act.src, &s)) + { + ASSERT(actionList.atype != ACT_DEL_BOTH); // directories are all unique + actionList.deletedItems.AddTail(act.idx); + } + else + { + actionList.errors.AddTail(s); + } + } + else + { + // delete file + CString s; + if (actionList.atype==ACT_DEL_LEFT || actionList.atype==ACT_DEL_BOTH) + { + CString sFile = act.src; + if (DeleteFileSilently(sFile, &s)) + { + // figure out if copy on right + POSITION diffpos = GetItemKey(act.idx); + BYTE code = GetDiffContext()->GetDiffStatus(diffpos); + if (IsItemLeftOnly(code)) + actionList.deletedItems.AddTail(act.idx); + else + mf->UpdateCurrentFileStatus(FILE_RUNIQUE, act.idx); + } + else + { + actionList.errors.AddTail(s); + } + } + s=_T(""); + if (actionList.atype==ACT_DEL_RIGHT || actionList.atype==ACT_DEL_BOTH) + { + CString sFile = act.dest.IsEmpty() ? act.src : act.dest; + if (DeleteFileSilently(sFile, &s)) + { + // figure out if copy on right + POSITION diffpos = GetItemKey(act.idx); + BYTE code = GetDiffContext()->GetDiffStatus(diffpos); + if (IsItemRightOnly(code)) + actionList.deletedItems.AddTail(act.idx); + else + mf->UpdateCurrentFileStatus(FILE_LUNIQUE, act.idx); + } + else + { + actionList.errors.AddTail(s); + } + } + } + } +} + +// Get directories of first selected item +BOOL CDirView::GetSelectedDirNames(CString& strLeft, CString& strRight) const +{ + BOOL bResult = GetSelectedFileNames(strLeft, strRight); + + if (bResult) + { + TCHAR path[MAX_PATH]; + split_filename(strLeft, path, NULL, NULL); + strLeft = path; + + split_filename(strRight, path, NULL, NULL); + strRight = path; + } + return bResult; +} + +// Does item exist only on the left ? +BOOL CDirView::IsItemLeftOnly(int code) +{ + return code==FILE_LUNIQUE || code==FILE_LDIRUNIQUE; +} +// Does item exist only on the right ? +BOOL CDirView::IsItemRightOnly(int code) +{ + return code==FILE_RUNIQUE || code==FILE_RDIRUNIQUE; +} + +// is it possible to copy item to left ? +BOOL CDirView::IsItemCopyableToLeft(int code) +{ + switch(code) + { + case FILE_RUNIQUE: + case FILE_DIFF: + case FILE_BINDIFF: + return TRUE; + // no point in allowing FILE_SAME + // TODO: FILE_RDIRUNIQUE: add code for directory copy + } + return FALSE; +} +// is it possible to copy item to right ? +BOOL CDirView::IsItemCopyableToRight(int code) +{ + switch(code) + { + case FILE_LUNIQUE: + case FILE_DIFF: + case FILE_BINDIFF: + return TRUE; + // no point in allowing FILE_SAME + // TODO: FILE_LDIRUNIQUE: add code for directory copy + } + return FALSE; +} +// is it possible to delete left item ? +BOOL CDirView::IsItemDeletableOnLeft(int code) +{ + switch(code) + { + case FILE_LUNIQUE: + case FILE_DIFF: + case FILE_BINDIFF: + case FILE_SAME: + case FILE_LDIRUNIQUE: + return TRUE; + } + return FALSE; +} +// is it possible to delete right item ? +BOOL CDirView::IsItemDeletableOnRight(int code) +{ + switch(code) + { + case FILE_RUNIQUE: + case FILE_DIFF: + case FILE_BINDIFF: + case FILE_SAME: + case FILE_RDIRUNIQUE: + return TRUE; + } + return FALSE; +} +// is it possible to delete both items ? +BOOL CDirView::IsItemDeletableOnBoth(int code) +{ + switch(code) + { + case FILE_DIFF: + case FILE_BINDIFF: + case FILE_SAME: + return TRUE; + } + return FALSE; +} + +// is it possible to open left item ? +BOOL CDirView::IsItemOpenableOnLeft(int code) +{ + switch(code) + { + case FILE_LUNIQUE: + case FILE_DIFF: + case FILE_BINDIFF: + case FILE_SAME: + return TRUE; + } + return FALSE; +} +// is it possible to open right item ? +BOOL CDirView::IsItemOpenableOnRight(int code) +{ + switch(code) + { + case FILE_RUNIQUE: + case FILE_DIFF: + case FILE_BINDIFF: + case FILE_SAME: + return TRUE; + } + return FALSE; +} + +// get the file names on both sides for first selected item +BOOL CDirView::GetSelectedFileNames(CString& strLeft, CString& strRight) const +{ + int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); + if (sel == -1) + return FALSE; + GetItemFileNames(sel, strLeft, strRight); + return TRUE; +} +// get file name on specified side for first selected item +CString CDirView::GetSelectedFileName(SIDE_TYPE stype) const +{ + CString left, right; + if (!GetSelectedFileNames(left, right)) return _T(""); + return stype==SIDE_LEFT ? left : right; +} + +// get the file names on both sides for specified item +void CDirView::GetItemFileNames(int sel, CString& strLeft, CString& strRight) const +{ + const CDirDoc *pd = GetDocument(); + CString name, pathex; + name = m_pList->GetItemText(sel, DV_NAME); + pathex = m_pList->GetItemText(sel, DV_PATH); + if (pathex.Left(2) == _T(".\\") || pathex.Left(2) == _T("./")) + { + strLeft.Format(_T("%s\\%s\\%s"), pd->m_pCtxt->m_strLeft, pathex.Right(pathex.GetLength()-2), name); + strRight.Format(_T("%s\\%s\\%s"), pd->m_pCtxt->m_strRight, pathex.Right(pathex.GetLength()-2), name); + } + else + { + strLeft.Format(_T("%s\\%s"), pd->m_pCtxt->m_strLeft, name); + strRight.Format(_T("%s\\%s"), pd->m_pCtxt->m_strRight, name); + } +} + +// Open selected file on specified side +void CDirView::DoOpen(SIDE_TYPE stype) +{ + int sel = GetSingleSelectedItem(); + if (sel == -1) return; + CString file = GetSelectedFileName(stype); + if (file.IsEmpty()) return; + int rtn = (int)ShellExecute(::GetDesktopWindow(), _T("open"), file, 0, 0, SW_SHOWNORMAL); + if (rtn==SE_ERR_NOASSOC) + DoOpenWith(stype); + +} + +// Open with dialog for file on selected side +void CDirView::DoOpenWith(SIDE_TYPE stype) +{ + int sel = GetSingleSelectedItem(); + if (sel == -1) return; + CString file = GetSelectedFileName(stype); + if (file.IsEmpty()) return; + CString sysdir; + if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH)) return; + sysdir.ReleaseBuffer(); + CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + file; + ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg, sysdir, SW_SHOWNORMAL); +} diff --git a/Src/DirDoc.cpp b/Src/DirDoc.cpp index 97c6472da..db77954c3 100644 --- a/Src/DirDoc.cpp +++ b/Src/DirDoc.cpp @@ -190,7 +190,7 @@ void CDirDoc::Rescan() if (gWriteLog) gLog.Write(_T("Starting directory scan:\r\n\tLeft: %s\r\n\tRight: %s\r\n"), m_pCtxt->m_strLeft, m_pCtxt->m_strRight); - m_pCtxt->m_dirlist.RemoveAll(); + m_pCtxt->RemoveAll(); compare_files (0, (char const *)(LPCTSTR)m_pCtxt->m_strLeft, 0, (char const *)(LPCTSTR)m_pCtxt->m_strRight, m_pCtxt, 0); @@ -212,16 +212,19 @@ void CDirDoc::Redisplay() CString s,s2; UINT cnt=0; - LPCTSTR p=NULL; int llen = m_pCtxt->m_strLeft.GetLength(); int rlen = m_pCtxt->m_strRight.GetLength(); - m_pView->m_pList->DeleteAllItems(); - POSITION pos = m_pCtxt->m_dirlist.GetHeadPosition(), curpos; - while (pos != NULL) + m_pView->DeleteAllDisplayItems(); + + POSITION diffpos = m_pCtxt->GetFirstDiffPosition(); + while (diffpos) { - curpos = pos; - DIFFITEM di = m_pCtxt->m_dirlist.GetNext(pos); + POSITION curdiffpos = diffpos; + DIFFITEM di = m_pCtxt->GetNextDiffPosition(diffpos); + + LPCTSTR p=NULL; + BOOL leftside = (di.code==FILE_LUNIQUE || di.code==FILE_LDIRUNIQUE); BOOL rightside = (di.code==FILE_RUNIQUE || di.code==FILE_RDIRUNIQUE); switch (di.code) @@ -230,32 +233,14 @@ void CDirDoc::Redisplay() if (mf->m_bShowDiff && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT))) { - m_pView->AddItem(cnt, DV_NAME, di.filename); - m_pView->AddItem(cnt, DV_EXT, di.extension); // BSP - Add the current file extension - p = _tcsninc(di.lpath, llen); - s = _T("."); - s += p; - m_pView->AddItem(cnt, DV_PATH, s); - - m_pView->m_pList->SetItemData(cnt, (DWORD)curpos); - UpdateItemStatus(cnt); - cnt++; } break; case FILE_BINDIFF: if (mf->m_bShowDiff && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT))) { - m_pView->AddItem(cnt, DV_NAME, di.filename); - m_pView->AddItem(cnt, DV_EXT, di.extension); // BSP - Add the current file extension p = _tcsninc(di.lpath, llen); - s = _T("."); - s += p; - m_pView->AddItem(cnt, DV_PATH, s); - m_pView->m_pList->SetItemData(cnt, (DWORD)curpos); - UpdateItemStatus(cnt); - cnt++; } break; case FILE_LUNIQUE: @@ -265,47 +250,35 @@ void CDirDoc::Redisplay() if (((mf->m_bShowUniqueLeft && leftside) || (mf->m_bShowUniqueRight && rightside)) && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT))) { - m_pView->AddItem(cnt, DV_NAME, di.filename); - m_pView->AddItem(cnt, DV_EXT, di.extension); // BSP - Add the current file extension - if (di.code==FILE_LUNIQUE || di.code==FILE_LDIRUNIQUE) p = _tcsninc(di.lpath, llen); else p = _tcsninc(di.rpath, rlen); - s2 = _T("."); - s2 += p; - m_pView->AddItem(cnt, DV_PATH, s2); - m_pView->m_pList->SetItemData(cnt, (DWORD)curpos); - UpdateItemStatus(cnt); - cnt++; } break; case FILE_SAME: if (mf->m_bShowIdent && (!mf->m_bHideBak || !FileExtMatches(di.filename,BACKUP_FILE_EXT))) { - m_pView->AddItem(cnt, DV_NAME, di.filename); - m_pView->AddItem(cnt, DV_EXT, di.extension); // BSP - Add the current file extension p = _tcsninc(di.lpath, llen); - s = _T("."); - s += p; - m_pView->AddItem(cnt, DV_PATH, s); - m_pView->m_pList->SetItemData(cnt, (DWORD)curpos); - UpdateItemStatus(cnt); - cnt++; } break; default: // error + p = _tcsninc(di.lpath, llen); + break; + } + if (p) + { m_pView->AddItem(cnt, DV_NAME, di.filename); m_pView->AddItem(cnt, DV_EXT, di.extension); // BSP - Add the current file extension - p = _tcsninc(di.lpath, llen); s = _T("."); s += p; m_pView->AddItem(cnt, DV_PATH, s); - m_pView->m_pList->SetItemData(cnt, (DWORD)curpos); + m_pView->SetItemKey(cnt, curdiffpos); + // This is inefficient, as we have the di in hand right now + // But to improve this, have to figure out the intricacies of UpdateItemStatus UpdateItemStatus(cnt); cnt++; - break; } } @@ -324,15 +297,37 @@ CDirView * CDirDoc::GetMainView() return (CDirView*)GetNextView(ps2); } +static long GetModTime(LPCTSTR szPath) +{ + struct stat mystats; + bzero(&mystats, sizeof(mystats)); + int stat_result = stat(szPath, &mystats); + if (stat_result!=0) + return 0; + return mystats.st_mtime; +} + +static void UpdateTimes(DIFFITEM * pdi) +{ + CString sLeft = (CString)pdi->lpath + _T("\\") + pdi->filename; + pdi->ltime = GetModTime(sLeft); + + CString sRight = (CString)pdi->rpath + _T("\\") + pdi->filename; + pdi->rtime = GetModTime(sRight); +} + void CDirDoc::UpdateItemStatus(UINT nIdx) { CString s,s2; UINT cnt=0; int llen = m_pCtxt->m_strLeft.GetLength(); int rlen = m_pCtxt->m_strRight.GetLength(); - POSITION pos = (POSITION)m_pView->m_pList->GetItemData(nIdx); - DIFFITEM di = m_pCtxt->m_dirlist.GetAt(pos); + POSITION diffpos = m_pView->GetItemKey(nIdx); + DIFFITEM di = m_pCtxt->GetDiffAt(diffpos); TCHAR sTime[80]; + + UpdateTimes(&di); // in case just copied (into existence) or modified + switch (di.code) { case FILE_DIFF: diff --git a/Src/DirView.cpp b/Src/DirView.cpp index 584a6f476..a84040f7f 100644 --- a/Src/DirView.cpp +++ b/Src/DirView.cpp @@ -68,6 +68,16 @@ BEGIN_MESSAGE_MAP(CDirView, CListViewEx) ON_UPDATE_COMMAND_UI(ID_DIR_DEL_LEFT, OnUpdateCtxtDirDelLeft) ON_COMMAND(ID_DIR_DEL_RIGHT, OnCtxtDirDelRight) ON_UPDATE_COMMAND_UI(ID_DIR_DEL_RIGHT, OnUpdateCtxtDirDelRight) + ON_COMMAND(ID_DIR_DEL_BOTH, OnCtxtDirDelBoth) + ON_UPDATE_COMMAND_UI(ID_DIR_DEL_BOTH, OnUpdateCtxtDirDelBoth) + ON_COMMAND(ID_DIR_OPEN_LEFT, OnCtxtDirOpenLeft) + ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT, OnUpdateCtxtDirOpenLeft) + ON_COMMAND(ID_DIR_OPEN_LEFT_WITH, OnCtxtDirOpenLeftWith) + ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITH, OnUpdateCtxtDirOpenLeftWith) + ON_COMMAND(ID_DIR_OPEN_RIGHT, OnCtxtDirOpenRight) + ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT, OnUpdateCtxtDirOpenRight) + ON_COMMAND(ID_DIR_OPEN_RIGHT_WITH, OnCtxtDirOpenRightWith) + ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITH, OnUpdateCtxtDirOpenRightWith) ON_WM_DESTROY() ON_WM_CHAR() //}}AFX_MSG_MAP @@ -104,6 +114,11 @@ CDirDoc* CDirView::GetDocument() // non-debug version is inline } #endif //_DEBUG +CDiffContext * CDirView::GetDiffContext() +{ + return GetDocument()->m_pCtxt; +} + ///////////////////////////////////////////////////////////////////////////// // CDirView message handlers @@ -207,22 +222,23 @@ void CDirView::OnContextMenu(CWnd*, CPoint point) CMenu* pPopup = menu.GetSubMenu(0); ASSERT(pPopup != NULL); - CWnd* pWndPopupOwner = this; // set the menu items with the proper directory names CString s, sl, sr; GetSelectedDirNames(sl, sr); - - ModifyPopup(pPopup, IDS_COPY2DIR_LEFT_FMT, ID_DIR_COPY_FILE_TO_LEFT, sl); - ModifyPopup(pPopup, IDS_COPY2DIR_RIGHT_FMT, ID_DIR_COPY_FILE_TO_RIGHT, sr); - ModifyPopup(pPopup, IDS_DEL_LEFT_FMT, ID_DIR_DEL_LEFT, sl); - ModifyPopup(pPopup, IDS_DEL_RIGHT_FMT, ID_DIR_DEL_RIGHT, sr); - // - + // find non-child ancestor to use as menu parent + CWnd* pWndPopupOwner = this; while (pWndPopupOwner->GetStyle() & WS_CHILD) pWndPopupOwner = pWndPopupOwner->GetParent(); + // TODO: It would be more efficient to set + // all the popup items now with one traverse over selected items + // instead of using updates, in which we make a traverse for every item + // Perry, 2002-12-04 + // + + // invoke context menu // this will invoke all the OnUpdate methods to enable/disable the individual items pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, @@ -230,6 +246,17 @@ void CDirView::OnContextMenu(CWnd*, CPoint point) } } +// Make a string out of a number +// TODO: Ought to introduce commas every three digits, except this is locale-specific +// How to do this with locale sensitivity ? +CString +NumToStr(int n) +{ + CString s; + s.Format(_T("%d"), n); + return s; +} + // Change menu item by using string resource // (Question: Why don't we just remove it from the menu resource entirely & do an Add here ?) void CDirView::ModifyPopup(CMenu * pPopup, int nStringResource, int nMenuId, LPCTSTR szPath) @@ -239,13 +266,6 @@ void CDirView::ModifyPopup(CMenu * pPopup, int nStringResource, int nMenuId, LPC pPopup->ModifyMenu(nMenuId, MF_BYCOMMAND|MF_STRING, nMenuId, s); } -// given index in list control, get position & DIFFITEM reference to its data -const DIFFITEM& CDirView::GetDiffItem(int sel, POSITION & pos) -{ - pos = reinterpret_cast(m_pList->GetItemData(sel)); - const DIFFITEM& di = GetDocument()->m_pCtxt->m_dirlist.GetAt(pos); - return di; -} // User chose (main menu) Copy from right to left void CDirView::OnDirCopyFileToLeft() @@ -269,218 +289,69 @@ void CDirView::OnCtxtDirCopyFileToRight() DoCopyFileToRight(); } -// Prompt & copy item from right to left, if legal -void CDirView::DoCopyFileToLeft() -{ - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) return; - - CString sl, sr, slFile, srFile; - if (!GetSelectedDirNames(sl, sr) || !GetSelectedFileNames(slFile, srFile)) - return; - - CDirDoc *pd = GetDocument(); - CString s; - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - // what we do depends on comparison result for this item - switch(di.code) - { - case FILE_LUNIQUE: - break; - case FILE_RUNIQUE: - case FILE_DIFF: - case FILE_BINDIFF: - AfxFormatString1(s, IDS_CONFIRM_COPY2DIR, sl); - if (AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION)==IDYES) - { - if (mf->SyncFiles(srFile, slFile)) - { - mf->UpdateCurrentFileStatus(FILE_SAME); - } - } - break; - case FILE_LDIRUNIQUE: - case FILE_RDIRUNIQUE: - // TODO: Would be nice to allow copying directory - // but must write code for recursive copy - // and worse, this will make the item display invalid (new items not on it) - // Perry 2002-11-26 - case FILE_SAME: - default: - // Not allowed, and should have been disabled choices anyway - break; - } -} -// Prompt & copy item from left to right, if legal -void CDirView::DoCopyFileToRight() -{ - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) return; - - CString sl, sr, slFile, srFile; - if (!GetSelectedDirNames(sl, sr) || !GetSelectedFileNames(slFile, srFile)) - return; - - CDirDoc *pd = GetDocument(); - CString s; - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - // what we do depends on comparison result for this item - switch(di.code) - { - case FILE_RUNIQUE: - break; - case FILE_LUNIQUE: - case FILE_DIFF: - case FILE_BINDIFF: - AfxFormatString1(s, IDS_CONFIRM_COPY2DIR, sr); - if (AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION)==IDYES) - { - CString left, right; - if (mf->SyncFiles(slFile, srFile)) - { - mf->UpdateCurrentFileStatus(FILE_SAME); - } - } - break; - case FILE_LDIRUNIQUE: - case FILE_RDIRUNIQUE: - // see comments in DoCopyFileToLeft - case FILE_SAME: - default: - // Not allowed, and should have been disabled choices anyway - break; - } -} - // Update context menu Copy Right to Left item void CDirView::OnUpdateCtxtDirCopyFileToLeft(CCmdUI* pCmdUI) { - DoUpdateDirCopyFileToLeft(pCmdUI); + DoUpdateDirCopyFileToLeft(pCmdUI, eContext); } // Update context menu Copy Left to Right item void CDirView::OnUpdateCtxtDirCopyFileToRight(CCmdUI* pCmdUI) { - DoUpdateDirCopyFileToRight(pCmdUI); + DoUpdateDirCopyFileToRight(pCmdUI, eContext); } // Update main menu Copy Right to Left item void CDirView::OnUpdateDirCopyFileToLeft(CCmdUI* pCmdUI) { - DoUpdateDirCopyFileToLeft(pCmdUI); + DoUpdateDirCopyFileToLeft(pCmdUI, eMain); } // Update main menu Copy Left to Right item void CDirView::OnUpdateDirCopyFileToRight(CCmdUI* pCmdUI) { - DoUpdateDirCopyFileToRight(pCmdUI); + DoUpdateDirCopyFileToRight(pCmdUI, eMain); } // Should Copy to Left be enabled or disabled ? (both main menu & context menu use this) -void CDirView::DoUpdateDirCopyFileToLeft(CCmdUI* pCmdUI) +void CDirView::DoUpdateDirCopyFileToLeft(CCmdUI* pCmdUI, eMenuType menuType) { - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) + int sel=-1; + int legalcount=0, selcount=0; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) { - // no item there - pCmdUI->Enable(FALSE); - return; + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemCopyableToLeft(di.code)) + ++legalcount; + ++selcount; } - // found item (normal case) - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - // what we do depends on comparison result for this item - switch(di.code) + pCmdUI->Enable(legalcount>0); + if (menuType==eContext) { - case FILE_LUNIQUE: - pCmdUI->Enable(FALSE); // no right item, so can't copy to left - break; - case FILE_RUNIQUE: - case FILE_DIFF: - case FILE_BINDIFF: - pCmdUI->Enable(TRUE); - break; - case FILE_SAME: - pCmdUI->Enable(FALSE); - break; - case FILE_LDIRUNIQUE: - pCmdUI->Enable(FALSE); // no right item, so can't copy to left - break; - case FILE_RDIRUNIQUE: - // TODO 2002-11-22: Enable unique also, but must write code to do recursive copy - pCmdUI->Enable(TRUE); - break; - default: - pCmdUI->Enable(FALSE); - break; + CString s; + AfxFormatString2(s, IDS_COPY_TO_LEFT, NumToStr(legalcount), NumToStr(selcount)); + pCmdUI->SetText(s); } } // Should Copy to Right be enabled or disabled ? (both main menu & context menu use this) -void CDirView::DoUpdateDirCopyFileToRight(CCmdUI* pCmdUI) +void CDirView::DoUpdateDirCopyFileToRight(CCmdUI* pCmdUI, eMenuType menuType) { - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) + int sel=-1; + int legalcount=0, selcount=0; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) { - // no item there - pCmdUI->Enable(FALSE); - return; + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemCopyableToRight(di.code)) + ++legalcount; + ++selcount; } - // found item (normal case) - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - switch(di.code) + pCmdUI->Enable(legalcount>0); + if (menuType==eContext) { - case FILE_LUNIQUE: - pCmdUI->Enable(TRUE); - break; - case FILE_RUNIQUE: - pCmdUI->Enable(FALSE); // no left item, so can't copy to right - break; - case FILE_DIFF: - case FILE_BINDIFF: - pCmdUI->Enable(TRUE); - break; - case FILE_LDIRUNIQUE: - // TODO 2002-11-22: Enable unique also, but must write code to do recursive copy - pCmdUI->Enable(FALSE); - break; - case FILE_RDIRUNIQUE: - pCmdUI->Enable(FALSE); // no left item, so can't copy to right - break; - case FILE_SAME: - default: - pCmdUI->Enable(FALSE); - break; + CString s; + AfxFormatString2(s, IDS_COPY_TO_RIGHT, NumToStr(legalcount), NumToStr(selcount)); + pCmdUI->SetText(s); } } -BOOL CDirView::GetSelectedFileNames(CString& strLeft, CString& strRight) -{ - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel != -1) - { - CDirDoc *pd = GetDocument(); - CString name, pathex; - name = m_pList->GetItemText(sel, DV_NAME); - pathex = m_pList->GetItemText(sel, DV_PATH); - if (pathex.Left(2) == _T(".\\") || pathex.Left(2) == _T("./")) - { - strLeft.Format(_T("%s\\%s\\%s"), pd->m_pCtxt->m_strLeft, pathex.Right(pathex.GetLength()-2), name); - strRight.Format(_T("%s\\%s\\%s"), pd->m_pCtxt->m_strRight, pathex.Right(pathex.GetLength()-2), name); - } - else - { - strLeft.Format(_T("%s\\%s"), pd->m_pCtxt->m_strLeft, name); - strRight.Format(_T("%s\\%s"), pd->m_pCtxt->m_strRight, name); - } - return TRUE; - } - return FALSE; -} void CDirView::UpdateResources() @@ -509,28 +380,14 @@ void CDirView::UpdateResources() m_pList->SetColumn(DV_RTIME, &lvc); } -BOOL CDirView::GetSelectedDirNames(CString& strLeft, CString& strRight) -{ - BOOL bResult = GetSelectedFileNames(strLeft, strRight); - - if (bResult) - { - TCHAR path[MAX_PATH]; - split_filename(strLeft, path, NULL, NULL); - strLeft = path; - - split_filename(strRight, path, NULL, NULL); - strRight = path; - } - return bResult; -} - int CALLBACK CDirView::CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { // initialize structures to obtain required information CDirView* pView = reinterpret_cast(lParamSort); - DIFFITEM lDi = pView->GetDocument()->m_pCtxt->m_dirlist.GetAt( reinterpret_cast(lParam1) ); - DIFFITEM rDi = pView->GetDocument()->m_pCtxt->m_dirlist.GetAt( reinterpret_cast(lParam2) ); + POSITION diffposl = pView->GetItemKeyFromData(lParam1); + POSITION diffposr = pView->GetItemKeyFromData(lParam2); + DIFFITEM lDi = pView->GetDiffContext()->GetDiffAt(diffposl); + DIFFITEM rDi = pView->GetDiffContext()->GetDiffAt(diffposr); // compare 'left' and 'right' parameters as appropriate int retVal = 0; // initialize for default case @@ -589,6 +446,8 @@ void CDirView::OnColumnClick(NMHDR *pNMHDR, LRESULT *pResult) void CDirView::OnDestroy() { + DeleteAllDisplayItems(); + // save the column widths CListCtrl& ctl = GetListCtrl(); @@ -615,14 +474,16 @@ void CDirView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) CListViewEx::OnChar(nChar, nRepCnt, nFlags); } +// TODO: This just opens first selected item +// should it do something different when multiple items are selected ? +// (Perry, 2002-12-04) void CDirView::OpenSelection() { int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); if (sel != -1) { - CDirDoc *pd = GetDocument(); - POSITION pos = reinterpret_cast(m_pList->GetItemData(sel)); - DIFFITEM di = pd->m_pCtxt->m_dirlist.GetAt(pos); + POSITION diffpos = GetItemKey(sel); + DIFFITEM di = GetDiffContext()->GetDiffAt(diffpos); switch(di.code) { case FILE_DIFF: @@ -684,61 +545,12 @@ void CDirView::OnCtxtDirDelRight() { DoDelRight(); } - -// Prompt & delete left, if legal -void CDirView::DoDelLeft() +// User chose (context menu) delete both +void CDirView::OnCtxtDirDelBoth() { - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) return; - - CString sl, sr, slFile, srFile; - if (!GetSelectedDirNames(sl, sr) || !GetSelectedFileNames(slFile, srFile)) - return; - - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - - // need to know if file or directory - switch(di.code) - { - case FILE_LUNIQUE: - case FILE_DIFF: - case FILE_BINDIFF: - ConfirmAndDeleteFileAndUpdate(slFile, pos, sel); - break; - case FILE_LDIRUNIQUE: - ConfirmAndDeleteDirAndUpdate(slFile, pos, sel); - break; - } + DoDelBoth(); } -// Prompt & delete right, if legal -void CDirView::DoDelRight() -{ - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) return; - - CString sl, sr, slFile, srFile; - if (!GetSelectedDirNames(sl, sr) || !GetSelectedFileNames(slFile, srFile)) - return; - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - - // need to know if file or directory - switch(di.code) - { - case FILE_RUNIQUE: - case FILE_DIFF: - case FILE_BINDIFF: - ConfirmAndDeleteFileAndUpdate(srFile, pos, sel); - break; - case FILE_RDIRUNIQUE: - ConfirmAndDeleteDirAndUpdate(srFile, pos, sel); - break; - } -} // Enable/disable Delete Left menu choice on context menu void CDirView::OnUpdateCtxtDirDelLeft(CCmdUI* pCmdUI) @@ -751,111 +563,173 @@ void CDirView::OnUpdateCtxtDirDelRight(CCmdUI* pCmdUI) { DoUpdateCtxtDirDelRight(pCmdUI); } +// Enable/disable Delete Both menu choice on context menu +void CDirView::OnUpdateCtxtDirDelBoth(CCmdUI* pCmdUI) +{ + DoUpdateCtxtDirDelBoth(pCmdUI); +} // Should Delete left be enabled or disabled ? -void CDirView::DoUpdateCtxtDirDelLeft(CCmdUI* pCmdUI) +void CDirView::DoUpdateCtxtDirDelLeft(CCmdUI* pCmdUI) { - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) + int sel=-1; + int count=0, total=0; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) { - // no item there - pCmdUI->Enable(FALSE); - return; - } - // found item (normal case) - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - // what we do depends on comparison result for this item - switch(di.code) - { - case FILE_LUNIQUE: - pCmdUI->Enable(TRUE); - break; - case FILE_RUNIQUE: - pCmdUI->Enable(FALSE); // no left item, so can't delete left - break; - case FILE_DIFF: - case FILE_BINDIFF: - pCmdUI->Enable(TRUE); - break; - case FILE_SAME: - pCmdUI->Enable(FALSE); - break; - case FILE_LDIRUNIQUE: - pCmdUI->Enable(TRUE); - break; - case FILE_RDIRUNIQUE: - pCmdUI->Enable(FALSE); // no left item, so can't delete left - break; - default: - pCmdUI->Enable(FALSE); - break; + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemDeletableOnLeft(di.code)) + ++count; + ++total; } + pCmdUI->Enable(count>0); + CString s; + AfxFormatString2(s, IDS_DEL_LEFT_FMT, NumToStr(count), NumToStr(total)); + pCmdUI->SetText(s); } // Should Delete right be enabled or disabled ? void CDirView::DoUpdateCtxtDirDelRight(CCmdUI* pCmdUI) { - int sel = m_pList->GetNextItem(-1, LVNI_SELECTED); - if (sel == -1) + int sel=-1; + int count=0, total=0; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) { - // no item there - pCmdUI->Enable(FALSE); - return; + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemDeletableOnRight(di.code)) + ++count; + ++total; } - // found item (normal case) - // find item from document - POSITION pos; - const DIFFITEM& di = GetDiffItem(sel, pos); - // what we do depends on comparison result for this item - switch(di.code) + pCmdUI->Enable(count>0); + CString s; + AfxFormatString2(s, IDS_DEL_RIGHT_FMT, NumToStr(count), NumToStr(total)); + pCmdUI->SetText(s); +} +// Should Delete both be enabled or disabled ? +void CDirView::DoUpdateCtxtDirDelBoth(CCmdUI* pCmdUI) +{ + int sel=-1; + int count=0, total=0; + while ((sel = m_pList->GetNextItem(sel, LVNI_SELECTED)) != -1) { - case FILE_LUNIQUE: - pCmdUI->Enable(FALSE); // no right item, so can't delete right - break; - case FILE_RUNIQUE: - pCmdUI->Enable(TRUE); - break; - case FILE_DIFF: - case FILE_BINDIFF: - pCmdUI->Enable(TRUE); - break; - case FILE_SAME: - pCmdUI->Enable(FALSE); - break; - case FILE_LDIRUNIQUE: - pCmdUI->Enable(FALSE); // no right item, so can't delete right - break; - case FILE_RDIRUNIQUE: - pCmdUI->Enable(TRUE); - break; - default: - pCmdUI->Enable(FALSE); - break; + const DIFFITEM& di = GetDiffItem(sel); + if (IsItemDeletableOnBoth(di.code)) + ++count; + ++total; } + pCmdUI->Enable(count>0); + CString s; + AfxFormatString2(s, IDS_DEL_BOTH_FMT, NumToStr(count), NumToStr(total)); + pCmdUI->SetText(s); } -// Prompt & delete file, & remove its data entries -void CDirView::ConfirmAndDeleteFileAndUpdate(LPCTSTR szFile, POSITION pos, int sel) + +POSITION CDirView::GetItemKey(int idx) { - // May want an option to suppress these message boxes + return GetItemKeyFromData(m_pList->GetItemData(idx)); +} - if (!mf->ConfirmAndDeleteFile(szFile)) - return; - // remove item data from document & screen - GetDocument()->m_pCtxt->m_dirlist.RemoveAt(pos); - GetListCtrl().DeleteItem(sel); +// SetItemKey & GetItemKey encapsulate how the display list items +// are mapped to DiffItems, which in turn are DiffContext keys to the actual DIFFITEM data +POSITION CDirView::GetItemKeyFromData(DWORD dw) +{ + return (POSITION)dw; +} +void CDirView::SetItemKey(int idx, POSITION diffpos) +{ + m_pList->SetItemData(idx, (DWORD)diffpos); } -// Prompt & delete directory, & remove its data entries -void CDirView::ConfirmAndDeleteDirAndUpdate(LPCTSTR szDir, POSITION pos, int sel) +// given index in list control, get its associated DIFFITEM data +DIFFITEM CDirView::GetDiffItem(int sel) { - // May want an option to suppress these message boxes + POSITION diffpos = GetItemKey(sel); + return GetDiffContext()->GetDiffAt(diffpos); +} - if (!mf->ConfirmAndDeleteDir(szDir)) - return; - // remove item data from document & screen - // so, this assumes it is a unique entry (has no children listed) - GetDocument()->m_pCtxt->m_dirlist.RemoveAt(pos); - GetListCtrl().DeleteItem(sel); +void CDirView::DeleteAllDisplayItems() +{ + // item data are just positions (diffposes) + // that is, they contain no memory needing to be freed + m_pList->DeleteAllItems(); +} + +// User chose (context menu) open left +void CDirView::OnCtxtDirOpenLeft() +{ + DoOpen(SIDE_LEFT); +} +// User chose (context menu) open right +void CDirView::OnCtxtDirOpenRight() +{ + DoOpen(SIDE_RIGHT); +} + +// User chose (context menu) open left with +void CDirView::OnCtxtDirOpenLeftWith() +{ + DoOpenWith(SIDE_LEFT); +} +// User chose (context menu) open right with +void CDirView::OnCtxtDirOpenRightWith() +{ + DoOpenWith(SIDE_RIGHT); +} +// return selected item index, or -1 if none or multiple +int CDirView::GetSingleSelectedItem() const +{ + int sel=-1, sel2=-1; + sel = m_pList->GetNextItem(sel, LVNI_SELECTED); + if (sel == -1) return -1; + sel2 = m_pList->GetNextItem(sel, LVNI_SELECTED); + if (sel2 != -1) return -1; + return sel; +} +// Enable/disable Open Left menu choice on context menu +void CDirView::OnUpdateCtxtDirOpenLeft(CCmdUI* pCmdUI) +{ + DoUpdateOpenLeft(pCmdUI); +} +// Enable/disable Open Right menu choice on context menu +void CDirView::OnUpdateCtxtDirOpenRight(CCmdUI* pCmdUI) +{ + DoUpdateOpenRight(pCmdUI); } + +// Enable/disable Open Left With menu choice on context menu +void CDirView::OnUpdateCtxtDirOpenLeftWith(CCmdUI* pCmdUI) +{ + DoUpdateOpenLeft(pCmdUI); +} +// Enable/disable Open Right With menu choice on context menu +void CDirView::OnUpdateCtxtDirOpenRightWith(CCmdUI* pCmdUI) +{ + DoUpdateOpenRight(pCmdUI); +} + +// used for both OpenLeft and OpenLeftWith +void CDirView::DoUpdateOpenLeft(CCmdUI* pCmdUI) +{ + int sel = GetSingleSelectedItem(); + if (sel != -1) + { + const DIFFITEM& di = GetDiffItem(sel); + if (!IsItemOpenableOnLeft(di.code)) + sel = -1; + } + + pCmdUI->Enable(sel>=0); +} +// used for both OpenRight and OpenRightWith +void CDirView::DoUpdateOpenRight(CCmdUI* pCmdUI) +{ + int sel = GetSingleSelectedItem(); + if (sel != -1) + { + const DIFFITEM& di = GetDiffItem(sel); + if (!IsItemOpenableOnRight(di.code)) + sel = -1; + } + + pCmdUI->Enable(sel>=0); +} + + diff --git a/Src/DirView.h b/Src/DirView.h index d0bccdef1..d8e048b1f 100644 --- a/Src/DirView.h +++ b/Src/DirView.h @@ -43,6 +43,9 @@ struct tagDIFFITEM; typedef struct tagDIFFITEM DIFFITEM; +class CDiffContext; + +typedef enum { eMain, eContext } eMenuType; class CDirDoc; @@ -53,18 +56,66 @@ protected: DECLARE_DYNCREATE(CDirView) // Attributes - CImageList m_imageList; public: CDirDoc* GetDocument(); // non-debug version is inline - CSortHeaderCtrl m_ctlSortHeader; + const CDirDoc * GetDocument() const { return const_cast(this)->GetDocument(); } +private: + CDiffContext * GetDiffContext(); // Operations public: static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); - BOOL GetSelectedDirNames(CString& strLeft, CString& strRight); void UpdateResources(); - BOOL GetSelectedFileNames(CString& strLeft, CString& strRight); - CListCtrl * m_pList; + POSITION GetItemKey(int idx); + void SetItemKey(int idx, POSITION diffpos); + void DeleteAllDisplayItems(); + + +// Implementation types +private: + typedef enum { ACT_COPY=1, ACT_DEL_LEFT, ACT_DEL_RIGHT, ACT_DEL_BOTH } ACT_TYPE; + struct action { CString src; CString dest; BOOL dirflag; int idx; }; + typedef CList DeletedItemList; // indices into display list control + struct ActionList + { + int selcount; // #items in full selection (not all may be affected) + ACT_TYPE atype; + CList actions; + CStringList errors; + DeletedItemList deletedItems; + ActionList(ACT_TYPE at) : selcount(0), atype(at) { } + int GetCount() const { return actions.GetCount(); } + }; + typedef enum { SIDE_LEFT=1, SIDE_RIGHT } SIDE_TYPE; + +// Implementation in DirActions.cpp +private: + BOOL GetSelectedDirNames(CString& strLeft, CString& strRight) const; + BOOL GetSelectedFileNames(CString& strLeft, CString& strRight) const; + CString GetSelectedFileName(SIDE_TYPE stype) const; + void GetItemFileNames(int sel, CString& strLeft, CString& strRight) const; + BOOL IsItemLeftOnly(int code); + BOOL IsItemRightOnly(int code); + BOOL IsItemCopyableToLeft(int code); + BOOL IsItemCopyableToRight(int code); + BOOL IsItemDeletableOnLeft(int code); + BOOL IsItemDeletableOnRight(int code); + BOOL IsItemDeletableOnBoth(int code); + BOOL IsItemOpenableOnLeft(int code); + BOOL IsItemOpenableOnRight(int code); + void DoCopyFileToRight(); + void DoCopyFileToLeft(); + void DoDelLeft(); + void DoDelRight(); + void DoDelBoth(); + void DoOpen(SIDE_TYPE stype); + void DoOpenWith(SIDE_TYPE stype); + void ConfirmAndPerformActions(ActionList & actions); + BOOL ConfirmActionList(const ActionList & actions); + void PerformActionList(ActionList & actions); + void PerformAndRemoveTopAction(ActionList & actions); +// End DirActions.cpp + // Overrides // ClassWizard generated virtual function overrides @@ -83,10 +134,16 @@ protected: virtual void Dump(CDumpContext& dc) const; #endif - // Generated message map functions +// Implementation data protected: + CSortHeaderCtrl m_ctlSortHeader; + CImageList m_imageList; bool m_bSortAscending; // is currently sorted ascending. int m_sortColumn; // index to column which is sorted + CListCtrl * m_pList; + + + // Generated message map functions afx_msg void OnColumnClick(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnContextMenu(CWnd*, CPoint point); //{{AFX_MSG(CDirView) @@ -103,32 +160,45 @@ protected: afx_msg void OnUpdateCtxtDirDelLeft(CCmdUI* pCmdUI); afx_msg void OnCtxtDirDelRight(); afx_msg void OnUpdateCtxtDirDelRight(CCmdUI* pCmdUI); + afx_msg void OnCtxtDirDelBoth(); + afx_msg void OnUpdateCtxtDirDelBoth(CCmdUI* pCmdUI); + afx_msg void OnCtxtDirOpenLeft(); + afx_msg void OnUpdateCtxtDirOpenLeft(CCmdUI* pCmdUI); + afx_msg void OnCtxtDirOpenLeftWith(); + afx_msg void OnUpdateCtxtDirOpenLeftWith(CCmdUI* pCmdUI); + afx_msg void OnCtxtDirOpenRight(); + afx_msg void OnUpdateCtxtDirOpenRight(CCmdUI* pCmdUI); + afx_msg void OnCtxtDirOpenRightWith(); + afx_msg void OnUpdateCtxtDirOpenRightWith(CCmdUI* pCmdUI); afx_msg void OnDestroy(); afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: void OpenSelection(); - const DIFFITEM& CDirView::GetDiffItem(int sel, POSITION & pos); - void DoCopyFileToRight(); - void DoCopyFileToLeft(); - void DoUpdateDirCopyFileToLeft(CCmdUI* pCmdUI); - void DoUpdateDirCopyFileToRight(CCmdUI* pCmdUI); + void DoUpdateDirCopyFileToLeft(CCmdUI* pCmdUI, eMenuType menuType); + void DoUpdateDirCopyFileToRight(CCmdUI* pCmdUI, eMenuType menuType); void ModifyPopup(CMenu * pPopup, int nStringResource, int nMenuId, LPCTSTR szPath); - void DoDelLeft(); - void DoDelRight(); void DoUpdateCtxtDirDelLeft(CCmdUI* pCmdUI); void DoUpdateCtxtDirDelRight(CCmdUI* pCmdUI); - void ConfirmAndDeleteFileAndUpdate(LPCTSTR szFile, POSITION pos, int sel); - void ConfirmAndDeleteDirAndUpdate(LPCTSTR szDir, POSITION pos, int sel); - + void DoUpdateCtxtDirDelBoth(CCmdUI* pCmdUI); + void DoUpdateOpenLeft(CCmdUI* pCmdUI); + void DoUpdateOpenRight(CCmdUI* pCmdUI); + POSITION GetItemKeyFromData(DWORD dw); + DIFFITEM GetDiffItem(int sel); + int GetSingleSelectedItem() const; }; + #ifndef _DEBUG // debug version in DirView.cpp inline CDirDoc* CDirView::GetDocument() { return (CDirDoc*)m_pDocument; } #endif + +CString NumToStr(int n); + + ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} diff --git a/Src/MainFrm.cpp b/Src/MainFrm.cpp index 4ab692279..2c1f92e47 100644 --- a/Src/MainFrm.cpp +++ b/Src/MainFrm.cpp @@ -742,10 +742,11 @@ void CMainFrame::rptStatus(BYTE code) break; } CString s; - // AfxFormatString4 doesn't exist - s.Format(_T("s:%d d:%d lf:%d ld:%d rf:%d rd:%d bd:%d e:%d") - , m_nStatusFileSame, m_nStatusFileDiff, m_nStatusFileBinDiff, m_nStatusFileError - , m_nStatusLeftFileOnly, m_nStatusLeftDirOnly, m_nStatusRightFileOnly, m_nStatusRightDirOnly); + // TODO: Load the format string from resource + s.Format(_T("s:%d d:%d bd:%d lf:%d ld:%d rf:%d rd:%d e:%d") + , m_nStatusFileSame, m_nStatusFileDiff, m_nStatusFileBinDiff + , m_nStatusLeftFileOnly, m_nStatusLeftDirOnly, m_nStatusRightFileOnly, m_nStatusRightDirOnly + , m_nStatusFileError); m_wndStatusBar.SetPaneText(2, s); } @@ -849,6 +850,7 @@ BOOL CMainFrame::DoFileOpen(LPCTSTR pszLeft /*=NULL*/, LPCTSTR pszRight /*=NULL* pCtxt->SetRegExp(strExt); m_pDirDoc->Rescan(); } + pCtxt->ClearStatus(); } } else @@ -896,7 +898,7 @@ BOOL CMainFrame::CreateBackup(LPCTSTR pszPath) } // Get user language description of error, if available -CString CMainFrame::GetSystemErrorDesc(int nerr) +CString GetSystemErrorDesc(int nerr) { LPVOID lpMsgBuf; CString str = _T("?"); @@ -920,12 +922,13 @@ CString CMainFrame::GetSystemErrorDesc(int nerr) } // trim trailing line returns -void CMainFrame::RemoveLineReturns(CString & str) +static void RemoveLineReturns(CString & str) { str.Replace(_T("\n"), _T("")); str.Replace(_T("\r"), _T("")); } +// TODO: Can we move this into DirActions.cpp ? // Delete file (return TRUE if deleted, else put up error & return FALSE) BOOL CMainFrame::DeleteFileOrError(LPCTSTR szFile) { @@ -942,29 +945,23 @@ BOOL CMainFrame::DeleteFileOrError(LPCTSTR szFile) return TRUE; } -// Prompt & delete file (return TRUE if deleted) -BOOL CMainFrame::ConfirmAndDeleteFile(LPCTSTR szFile) +// Delete file (return TRUE if successful, else sets error string & returns FALSE) +BOOL DeleteFileSilently(LPCTSTR szFile, CString * psError) { - CString s; - AfxFormatString1(s, IDS_CONFIRM_DELETE_FILE, szFile); - if (AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION)!=IDYES) + if (!DeleteFile(szFile)) + { + CString sError = GetSystemErrorDesc(GetLastError()); + RemoveLineReturns(sError); + AfxFormatString2(*psError, IDS_DELETE_FILE_FAILED, szFile, sError); return FALSE; - return DeleteFileOrError(szFile); + } + return TRUE; } -// Prompt & delete directory (return TRUE if deleted) -BOOL CMainFrame::ConfirmAndDeleteDir(LPCTSTR szDir) -{ - CString s; - AfxFormatString1(s, IDS_CONFIRM_DELETE_DIR, szDir); - if (AfxMessageBox(s, MB_YESNO|MB_ICONQUESTION)!=IDYES) - return FALSE; - return DeleteRecurseDir(szDir); -} // delete directory by recursively deleting all contents // gives up on first error -BOOL CMainFrame::DeleteRecurseDir(LPCTSTR szDir) +BOOL DeleteDirSilently(LPCTSTR szDir, CString * psError) { CFileFind finder; CString sSpec = szDir; @@ -978,12 +975,12 @@ BOOL CMainFrame::DeleteRecurseDir(LPCTSTR szDir) if (finder.IsDots()) continue; if (finder.IsDirectory()) { - if (!DeleteRecurseDir(finder.GetFilePath())) + if (!DeleteDirSilently(finder.GetFilePath(), psError)) return FALSE; } else { - if (!DeleteFileOrError(finder.GetFilePath())) + if (!DeleteFileSilently(finder.GetFilePath(), psError)) return FALSE; } } @@ -993,30 +990,46 @@ BOOL CMainFrame::DeleteRecurseDir(LPCTSTR szDir) { CString sError = GetSystemErrorDesc(GetLastError()); RemoveLineReturns(sError); - sError += (CString)_T(" [") + szDir + _T("]"); - CString s; - AfxFormatString1(s, IDS_DELETE_FILE_FAILED, sError); - AfxMessageBox(s, MB_OK|MB_ICONSTOP); + AfxFormatString2(*psError, IDS_DELETE_FILE_FAILED, szDir, sError); + return FALSE; + } + return TRUE; +} + +// wrapper for DoSyncFiles which adds filename to error string reported +BOOL CMainFrame::SyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest, CString * psError) +{ + if (!DoSyncFiles(pszSrc, pszDest, psError)) + { + AfxFormatString2(*psError, IDS_COPY_FILE_FAILED, *psError, pszSrc); return FALSE; } return TRUE; } -BOOL CMainFrame::SyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest) +// (error string reported does not include filename +BOOL CMainFrame::DoSyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest, CString * psError) { + CString sActionError; CString strSavePath(pszDest); if (!CheckSavePath(strSavePath)) + { + psError->LoadString(IDS_ERROR_FILE_WRITEABLE); return FALSE; + } if (!CreateBackup(strSavePath)) + { + psError->LoadString(IDS_ERROR_BACKUP); return FALSE; + } // Now it's just a matter of copying the right file to the left DeleteFile(strSavePath); // (errors are handled from CopyFile below) if (!CopyFile(pszSrc, strSavePath, FALSE)) { - + *psError = GetSystemErrorDesc(GetLastError()); return FALSE; } @@ -1024,27 +1037,22 @@ BOOL CMainFrame::SyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest) return TRUE; } -void CMainFrame::UpdateCurrentFileStatus(UINT nStatus) +void CMainFrame::UpdateCurrentFileStatus(UINT nStatus, int idx) { - if (NULL != m_pDirDoc) - { - CDirView *pv = m_pDirDoc->GetMainView(); - if (NULL != pv) - { - CListCtrl& lc = pv->GetListCtrl(); - int sel = lc.GetNextItem(-1, LVNI_SELECTED); - if (sel != -1) - { - // first change it in the dirlist - POSITION pos = reinterpret_cast(lc.GetItemData(sel)); - DIFFITEM di = m_pDirDoc->m_pCtxt->m_dirlist.GetAt(pos); - di.code = (BYTE)nStatus; - m_pDirDoc->m_pCtxt->m_dirlist.SetAt(pos, di); - m_pDirDoc->UpdateItemStatus(sel); - //m_pDirDoc->Redisplay(); - } - } - } + ASSERT(m_pDirDoc); + CDirView *pv = m_pDirDoc->GetMainView(); + ASSERT(pv); + // first change it in the dirlist + POSITION diffpos = pv->GetItemKey(idx); + + // TODO: Why is the update broken into these pieces ? + // Someone could figure out these pieces and probably simplify this. + + // update DIFFITEM code + m_pDirDoc->m_pCtxt->UpdateStatusCode(diffpos, (BYTE)nStatus); + // update DIFFITEM time, and also tell views + m_pDirDoc->UpdateItemStatus(idx); + //m_pDirDoc->Redisplay(); } void CMainFrame::OnViewSelectfont() diff --git a/Src/MainFrm.h b/Src/MainFrm.h index dd084de52..ed8023117 100644 --- a/Src/MainFrm.h +++ b/Src/MainFrm.h @@ -57,16 +57,12 @@ public: // Operations public: - CString GetSystemErrorDesc(int nerr); - void RemoveLineReturns(CString & str); BOOL DeleteFileOrError(LPCTSTR szFile); - BOOL DeleteRecurseDir(LPCTSTR szDir); - BOOL ConfirmAndDeleteFile(LPCTSTR szFile); - BOOL ConfirmAndDeleteDir(LPCTSTR szDir); void rptStatus(BYTE code); void clearStatus(); - BOOL SyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest); - void UpdateCurrentFileStatus(UINT nStatus); + BOOL SyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest, CString * psError); + BOOL DoSyncFiles(LPCTSTR pszSrc, LPCTSTR pszDest, CString * psError); + void UpdateCurrentFileStatus(UINT nStatus, int idx); BOOL DoFileOpen(LPCTSTR pszLeft = NULL, LPCTSTR pszRight = NULL, BOOL bRecurse = FALSE); void ShowMergeDoc(LPCTSTR szLeft, LPCTSTR szRight); void UpdateResources(); @@ -167,6 +163,10 @@ private: extern CMainFrame *mf; +BOOL DeleteFileSilently(LPCTSTR szFile, CString * psError); +BOOL DeleteDirSilently(LPCTSTR szDir, CString * psError); +CString GetSystemErrorDesc(int nerr); + ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} diff --git a/Src/Merge.dsp b/Src/Merge.dsp index 143cb94f9..4522d15b2 100644 --- a/Src/Merge.dsp +++ b/Src/Merge.dsp @@ -196,6 +196,10 @@ SOURCE=.\Dir.cpp # End Source File # Begin Source File +SOURCE=.\DirActions.cpp +# End Source File +# Begin Source File + SOURCE=.\DirDoc.cpp !IF "$(CFG)" == "Merge - Win32 Release" @@ -408,6 +412,10 @@ SOURCE=.\OpenDlg.cpp # End Source File # Begin Source File +SOURCE=.\OutputDlg.cpp +# End Source File +# Begin Source File + SOURCE=.\PropColors.cpp # End Source File # Begin Source File @@ -680,6 +688,10 @@ SOURCE=.\OpenDlg.h # End Source File # Begin Source File +SOURCE=.\OutputDlg.h +# End Source File +# Begin Source File + SOURCE=.\PropColors.h # End Source File # Begin Source File diff --git a/Src/Merge.rc b/Src/Merge.rc index 6fca1985c..24b5298ff 100644 --- a/Src/Merge.rc +++ b/Src/Merge.rc @@ -163,6 +163,12 @@ BEGIN MENUITEM "Copy file to right", ID_DIR_COPY_FILE_TO_RIGHT MENUITEM "Delete left", ID_DIR_DEL_LEFT MENUITEM "Delete right", ID_DIR_DEL_RIGHT + MENUITEM "Delete both", ID_DIR_DEL_BOTH + MENUITEM SEPARATOR + MENUITEM "Open left", ID_DIR_OPEN_LEFT + MENUITEM "Open left with...", ID_DIR_OPEN_LEFT_WITH + MENUITEM "Open right", ID_DIR_OPEN_RIGHT + MENUITEM "Open right with...", ID_DIR_OPEN_RIGHT_WITH END END @@ -372,6 +378,15 @@ BEGIN WS_EX_CLIENTEDGE END +IDD_OUTPUT DIALOG DISCARDABLE 0, 0, 278, 157 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Dialog" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,106,136,50,14 + EDITTEXT IDC_EDIT1,7,7,264,123,ES_MULTILINE | ES_READONLY | + WS_VSCROLL +END #ifndef _MAC ///////////////////////////////////////////////////////////////////////////// @@ -595,8 +610,8 @@ BEGIN IDS_ALLFILES "All Files (*.*)|*.*||" IDS_CONFIRM_ALL_LEFT "Are you sure you want to copy all differences to the left file?" IDS_CONFIRM_ALL_RIGHT "Are you sure you want to copy all differences to the right file?" - IDS_COPY2DIR_LEFT_FMT "Copy file to left: %1" - IDS_CONFIRM_COPY2DIR "Are you sure you want to copy the selected file to %1?" + IDS_ERRORS_TITLE "%1 Error(s)" + IDS_CONFIRM_COPY2DIR "Are you sure you want to copy %1 of %2 selected item(s)" IDS_FONT_CHANGE "The selected font change will not be applied to any currently visible difference windows." IDS_DIRECTORY_WINDOW_TITLE "Directory Comparison Results" IDS_DIRECTORY_WINDOW_STATUS_FMT "Results for %1 and %2" @@ -759,12 +774,16 @@ BEGIN ID_CURDIFF "Scroll to the current difference\nCurrent Diff (Alt+Enter)" ID_LASTDIFF "Scroll to the last difference\nLast Diff (Alt+End)" ID_DIFFNUM "placeholder" - ID_DIR_DEL_LEFT "Delete selected file on left" - ID_DIR_DEL_RIGHT "Delete selected file on right" + ID_DIR_DEL_LEFT "Delete selected item on left" + ID_DIR_DEL_RIGHT "Delete selected item on right" ID_DIFFSTATUS "placeholder for status count" ID_OPTIONS_SHOWUNIQUELEFT "Displays files that exist in only on left side" ID_OPTIONS_SHOWUNIQUERIGHT "Displays files that exist in only on right side" + ID_DIR_DEL_BOTH "Delete selected item(s) on both sides" + ID_DIR_OPEN_LEFT "Open left file" + ID_DIR_OPEN_LEFT_WITH "Open left file with ..." + ID_DIR_OPEN_RIGHT "Open right file" END STRINGTABLE DISCARDABLE @@ -938,16 +957,32 @@ BEGIN IDS_LTIME_HEADER "Left Date" IDS_RTIME_HEADER "Right Date" IDS_EXTENSION_HEADER "Extension" - IDS_COPY2DIR_RIGHT_FMT "Copy file to right: %1" - IDS_DEL_LEFT_FMT "Delete left: %1" + IDS_CONFIRM_DELETE_ITEMS + "Are you sure you want to delete %1 of %2 selected item(s) ?" + IDS_DEL_LEFT_FMT "Delete left side %1 of %2 selected item(s)" END STRINGTABLE DISCARDABLE BEGIN - IDS_DEL_RIGHT_FMT "Delete right: %1" - IDS_DELETE_FILE_FAILED "Delete file failed: %1" + IDS_DEL_RIGHT_FMT "Delete right side %1 of %2 selected item(s)" + IDS_DELETE_FILE_FAILED "Delete file %1 failed: %2" IDS_CONFIRM_DELETE_DIR "Delete directory %1?" IDS_REMOVE_DIR_FAILED "Remove directory failed: %1" + IDS_COPY_TO_LEFT "Copy to left side %1 of %2 selected item(s)" + IDS_DEL_BOTH_FMT "Delete both sides %1 of %2 selected item(s)" + IDS_COPY_TO_RIGHT "Copy to right side %1 of %2 selected item(s)" + IDS_DELETE_DIR_FAILED "Delete directory %1 failed: %s" + IDS_COPY_FILE_FAILED "Copying file %1 failed: %2" + IDS_COPY_DIR_FAILED "Copying directory %1 failed: %2" + IDS_ERROR_FILE_WRITEABLE "Error checking destination writeability" + IDS_ERROR_BACKUP "Error backing up file" + IDS_CONFIRM_COPY_SINGLE "Are you sure you want to copy %1 to %2?" + IDS_CONFIRM_DELETE_SINGLE "Are you sure you want to delete %1?" +END + +STRINGTABLE DISCARDABLE +BEGIN + ID_DIR_OPEN_RIGHT_WITH "Open right file with..." END #endif // English (U.S.) resources diff --git a/Src/OutputDlg.cpp b/Src/OutputDlg.cpp new file mode 100644 index 000000000..4af743a34 --- /dev/null +++ b/Src/OutputDlg.cpp @@ -0,0 +1,61 @@ +// OutputDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "merge.h" +#include "OutputDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// COutputDlg dialog + + +COutputDlg::COutputDlg(const CString & sTitle, const CString & sText) +: CDialog(COutputDlg::IDD) +, m_sTitle(sTitle) +, m_sText(sText) +{ + //{{AFX_DATA_INIT(COutputDlg) + // NOTE: the ClassWizard will add member initialization here + //}}AFX_DATA_INIT +} + + +void COutputDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(COutputDlg) + // NOTE: the ClassWizard will add DDX and DDV calls here + //}}AFX_DATA_MAP +} + + +BEGIN_MESSAGE_MAP(COutputDlg, CDialog) + //{{AFX_MSG_MAP(COutputDlg) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// COutputDlg message handlers + +BOOL COutputDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + SetWindowText(m_sTitle); + SetDlgItemText(IDC_EDIT1, m_sText); + + return TRUE; // return TRUE unless you set the focus to a control + // EXCEPTION: OCX Property Pages should return FALSE +} + +void OutputBox(LPCTSTR szTitle, LPCTSTR szText) +{ + COutputDlg dlg(szTitle, szText); + dlg.DoModal(); +} diff --git a/Src/OutputDlg.h b/Src/OutputDlg.h new file mode 100644 index 000000000..694ccc70c --- /dev/null +++ b/Src/OutputDlg.h @@ -0,0 +1,52 @@ +#if !defined(AFX_OUTPUTDLG_H__7F99AF10_24E5_49AF_8D97_9A86A80BAD48__INCLUDED_) +#define AFX_OUTPUTDLG_H__7F99AF10_24E5_49AF_8D97_9A86A80BAD48__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// OutputDlg.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// COutputDlg dialog + +void OutputBox(LPCTSTR szTitle, LPCTSTR szText); + +class COutputDlg : public CDialog +{ +// Construction +public: + COutputDlg(const CString & sTitle, const CString & sText); + +// Dialog Data + //{{AFX_DATA(COutputDlg) + enum { IDD = IDD_OUTPUT }; + // NOTE: the ClassWizard will add data members here + //}}AFX_DATA + + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(COutputDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + + // Generated message map functions + //{{AFX_MSG(COutputDlg) + virtual BOOL OnInitDialog(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() + +private: + CString m_sTitle; + CString m_sText; +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_OUTPUTDLG_H__7F99AF10_24E5_49AF_8D97_9A86A80BAD48__INCLUDED_) diff --git a/Src/resource.h b/Src/resource.h index 2f06215cf..c588fbef2 100644 --- a/Src/resource.h +++ b/Src/resource.h @@ -3,36 +3,35 @@ // Used by Merge.rc // #define IDC_CUT 3 -#define IDSAVEAS 3 #define IDC_COPY 4 #define IDC_PASTE 5 #define IDC_UNDO 6 +#define IDSAVEAS 7 #define IDD_ABOUTBOX 100 -#define IDR_POPUP_DIFFVIEW 102 -#define IDB_SPLASH 103 -#define IDR_POPUP_DIRVIEW 104 -#define IDD_PROP_VSS 107 -#define IDR_MAINFRAME 128 -#define IDR_MERGETYPE 129 -#define IDD_OPEN 130 -#define IDB_WINMERGE 130 +#define IDR_POPUP_DIFFVIEW 101 +#define IDR_POPUP_DIRVIEW 102 + +#define IDD_PROP_VSS 103 +#define IDR_MAINFRAME 104 +#define IDR_MERGETYPE 105 +#define IDD_OPEN 106 +#define IDD_EDITFILE 107 +#define IDD_VSS 108 +#define IDD_PROPPAGE_LARGE 109 +#define IDD_CLEARCASE 110 +#define IDD_PROPPAGE_FILTER 111 +#define IDD_OUTPUT 112 #define IDS_VERSION_FMT 130 -#define IDD_EDITFILE 131 -#define IDB_OLDSPLASH 131 + #define IDS_ALLFILES 131 -#define IDD_VSS 132 #define IDS_CONFIRM_ALL_LEFT 132 -#define IDD_PROPPAGE_LARGE 133 #define IDS_CONFIRM_ALL_RIGHT 133 -#define IDS_COPY2DIR_LEFT_FMT 134 -#define IDD_CLEARCASE 134 +#define IDS_ERRORS_TITLE 134 #define IDS_CONFIRM_COPY2DIR 135 -#define IDD_PROPPAGE_FILTER 135 #define IDS_FONT_CHANGE 136 #define IDS_DIRECTORY_WINDOW_TITLE 137 #define IDS_DIRECTORY_WINDOW_STATUS_FMT 138 #define IDS_FILES_ARE_DIFFERENT 139 -#define IDB_LFOLDER 140 #define IDS_BIN_FILES_DIFF 140 #define IDS_ONLY_IN_FMT 141 #define IDS_IDENTICAL 142 @@ -69,13 +68,25 @@ #define IDS_LTIME_HEADER 171 #define IDS_RTIME_HEADER 172 #define IDS_EXTENSION_HEADER 173 -#define IDS_COPY2DIR_RIGHT_FMT 174 +#define IDS_CONFIRM_DELETE_ITEMS 174 #define IDS_DEL_LEFT_FMT 175 #define IDS_DEL_RIGHT_FMT 176 #define IDS_DELETE_FAILED 177 #define IDS_DELETE_FILE_FAILED 177 #define IDS_CONFIRM_DELETE_DIR 178 #define IDS_REMOVE_DIR_FAILED 179 +#define IDS_COPY_TO_LEFT 180 +#define IDS_COPY_TO_LEFT_MAIN 181 +#define IDS_DEL_BOTH_FMT 181 +#define IDS_COPY_TO_RIGHT 182 +#define IDS_COPY_TO_RIGHT_MAIN 183 +#define IDS_DELETE_DIR_FAILED 184 +#define IDS_COPY_FILE_FAILED 185 +#define IDS_COPY_DIR_FAILED 186 +#define IDS_ERROR_FILE_WRITEABLE 187 +#define IDS_ERROR_BACKUP 188 +#define IDS_CONFIRM_COPY_SINGLE 189 +#define IDS_CONFIRM_DELETE_SINGLE 190 #define IDB_EQUAL 213 #define IDB_NOTEQUAL 214 #define IDB_RFOLDER 215 @@ -83,6 +94,11 @@ #define IDB_BINARY 217 #define IDB_LFILE 218 #define IDB_RFILE 219 +#define IDB_SPLASH 220 +#define IDB_WINMERGE 221 +#define IDB_OLDSPLASH 222 +#define IDB_LFOLDER 223 + #define IDC_LEFT_EDIT 1000 #define IDC_FILE_EDIT 1000 #define IDC_LEFT_BUTTON 1001 @@ -122,6 +138,7 @@ #define IDC_PASSWORD 1028 #define IDC_DIFFERENCE_COLOR 1031 #define IDC_SEL_DIFFERENCE_COLOR 1032 +#define IDC_EDIT1 1033 #define IDR_MARGIN_CURSOR 22900 #define IDD_LANGUAGE_SELECT 30000 #define IDD_PROPSYNTAX 30001 @@ -268,15 +285,20 @@ #define ID_DIFFSTATUS 32809 #define ID_OPTIONS_SHOWUNIQUELEFT 32810 #define ID_OPTIONS_SHOWUNIQUERIGHT 32811 +#define ID_DIR_DEL_BOTH 32812 +#define ID_DIR_OPEN_LEFT 32813 +#define ID_DIR_OPEN_LEFT_WITH 32814 +#define ID_DIR_OPEN_RIGHT 32815 +#define ID_DIR_OPEN_RIGHT_WITH 32816 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_3D_CONTROLS 1 -#define _APS_NEXT_RESOURCE_VALUE 135 -#define _APS_NEXT_COMMAND_VALUE 32812 -#define _APS_NEXT_CONTROL_VALUE 1032 -#define _APS_NEXT_SYMED_VALUE 106 +#define _APS_NEXT_RESOURCE_VALUE 113 +#define _APS_NEXT_COMMAND_VALUE 32817 +#define _APS_NEXT_CONTROL_VALUE 1034 +#define _APS_NEXT_SYMED_VALUE 108 #endif #endif -- 2.11.0