OSDN Git Service

Patch [ 651689 ] Add OpenLeft, OpenLeftWith, ... context
authorChristian List <list1974@hotmail.com>
Wed, 15 Jan 2003 15:09:00 +0000 (15:09 +0000)
committerChristian List <list1974@hotmail.com>
Wed, 15 Jan 2003 15:09:00 +0000 (15:09 +0000)
14 files changed:
Src/Diff.cpp
Src/DiffContext.cpp
Src/DiffContext.h
Src/DirActions.cpp [new file with mode: 0644]
Src/DirDoc.cpp
Src/DirView.cpp
Src/DirView.h
Src/MainFrm.cpp
Src/MainFrm.h
Src/Merge.dsp
Src/Merge.rc
Src/OutputDlg.cpp [new file with mode: 0644]
Src/OutputDlg.h [new file with mode: 0644]
Src/resource.h

index f891964..6509034 100644 (file)
@@ -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.  */
 
index 61ef60d..0c67787 100644 (file)
@@ -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;
+}
+
index f111ba0..f1e4a7f 100644 (file)
@@ -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<DIFFITEM,DIFFITEM> 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<DIFFITEM,DIFFITEM> 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 (file)
index 0000000..37e9109
--- /dev/null
@@ -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);
+}
index 97c6472..db77954 100644 (file)
@@ -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:
index 584a6f4..a84040f 100644 (file)
@@ -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<POSITION>(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<CDirView*>(lParamSort);
-       DIFFITEM lDi = pView->GetDocument()->m_pCtxt->m_dirlist.GetAt( reinterpret_cast<POSITION>(lParam1) );
-       DIFFITEM rDi = pView->GetDocument()->m_pCtxt->m_dirlist.GetAt( reinterpret_cast<POSITION>(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<POSITION>(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);
+}
+
+
index d0bccde..d8e048b 100644 (file)
@@ -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<CDirView *>(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<int, int> DeletedItemList; // indices into display list control
+       struct ActionList
+       {
+               int selcount; // #items in full selection (not all may be affected)
+               ACT_TYPE atype;
+               CList<action, action&> 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}}
index 4ab6922..2c1f92e 100644 (file)
@@ -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<POSITION>(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() 
index dd084de..ed80231 100644 (file)
@@ -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}}
index 143cb94..4522d15 100644 (file)
@@ -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
index 6fca198..24b5298 100644 (file)
@@ -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 (file)
index 0000000..4af743a
--- /dev/null
@@ -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 (file)
index 0000000..694ccc7
--- /dev/null
@@ -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_)
index 2f06215..c588fbe 100644 (file)
@@ -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
 #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
 #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
 #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
 #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