OSDN Git Service

Merge with stable
authorsdottaka <sdottaka@users.sourceforge.net>
Mon, 29 Dec 2014 15:13:24 +0000 (00:13 +0900)
committersdottaka <sdottaka@users.sourceforge.net>
Mon, 29 Dec 2014 15:13:24 +0000 (00:13 +0900)
19 files changed:
1  2 
Src/AboutDlg.cpp
Src/DiffContext.cpp
Src/DirActions.cpp
Src/DirActions.h
Src/DirDoc.cpp
Src/DirItemIterator.h
Src/ImgMergeFrm.cpp
Src/MainFrm.cpp
Src/Merge.cpp
Src/Merge.h
Src/Merge.vcxproj
Src/Merge.vcxproj.filters
Src/MergeApp.cpp
Src/MergeApp.h
Src/OptionsDef.h
Src/OptionsInit.cpp
Src/PropGeneral.cpp
Src/PropGeneral.h
Translations/WinMerge/English.pot

  
  #include "stdafx.h"
  #include "AboutDlg.h"
 -#include "Constants.h"
 +#include "statlink.h"
  #include "Merge.h"
 -#include "version.h"
 -#include "paths.h"
 -#include "Environment.h"
 +#include "DDXHelper.h"
++#include "Picture.h"
 +#include "resource.h" // IDD_ABOUTBOX
  
 -
 -BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
 -      //{{AFX_MSG_MAP(CAboutDlg)
 +/** 
 + * @brief About-dialog class.
 + * 
 + * Shows About-dialog bitmap and draws version number and other
 + * texts into it.
 + */
 +class CAboutDlg::Impl : public CDialog
 +{
 +public:
 +      CAboutDlg::Impl(CAboutDlg *p, CWnd* pParent = NULL);
 +
 +// Dialog Data
 +      //{{AFX_DATA(CAboutDlg::Impl)
 +      enum { IDD = IDD_ABOUTBOX };
 +      CStaticLink     m_ctlWWW;
 +      //}}AFX_DATA
 +
 +      // ClassWizard generated virtual function overrides
 +      //{{AFX_VIRTUAL(CAboutDlg::Impl)
 +      protected:
 +      virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
 +      //}}AFX_VIRTUAL
 +
 +// Implementation
 +protected:
 +      //{{AFX_MSG(CAboutDlg::Impl)
 +      virtual BOOL OnInitDialog();
 +      //}}AFX_MSG
 +      DECLARE_MESSAGE_MAP()
 +public:
 +      afx_msg void OnBnClickedOpenContributors();
++      afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);\r
++      afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);\r
 +
 +private:
 +      CAboutDlg *m_p;
++      CPicture m_image;
++      CFont m_font;
 +};
 +
 +BEGIN_MESSAGE_MAP(CAboutDlg::Impl, CDialog)
 +      //{{AFX_MSG_MAP(CAboutDlg::Impl)
        //}}AFX_MSG_MAP
        ON_BN_CLICKED(IDC_OPEN_CONTRIBUTORS, OnBnClickedOpenContributors)
+       ON_WM_DRAWITEM()
+       ON_WM_CTLCOLOR()
  END_MESSAGE_MAP()
  
 -CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
 +CAboutDlg::Impl::Impl(CAboutDlg *p, CWnd* pParent /*=NULL*/)
 +      : CDialog(CAboutDlg::Impl::IDD)
 +      , m_p(p)
  {
  }
  
@@@ -97,14 -63,61 +104,20 @@@ BOOL CAboutDlg::Impl::OnInitDialog(
        theApp.TranslateDialog(m_hWnd);
        CDialog::OnInitDialog();
  
-       // Load application icon
-       HICON icon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
-       if (icon != NULL) {
-               CStatic * pIcon = (CStatic *) GetDlgItem(IDC_ABOUTBOX_ICON);
-               pIcon->SetIcon(icon);
-       }
+       m_image.Load(IDR_SPLASH);
+       CDC *pDC = GetDC();
+       int fontHeight = -MulDiv(10, pDC->GetDeviceCaps(LOGPIXELSY), 72);
+       m_font.CreateFont(fontHeight, 0, 0, 0, FW_MEDIUM, FALSE, FALSE,
+               0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
+               DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, _T("Tahoma"));
+       ReleaseDC(pDC);
 -      String text = LoadResString(IDS_SPLASH_DEVELOPERS);
 -      string_replace(text, _T(", "), _T("\n"));
 -      CWnd *sta = GetDlgItem(IDC_STATIC);
 -      sta->SetWindowTextW(text.c_str());
 -      sta->SetFont(&m_font);
 -
 -      CVersionInfo version(AfxGetResourceHandle());
 -      String sVersion = version.GetProductVersion();
 -      m_strVersion = LangFormatString1(IDS_VERSION_FMT, sVersion.c_str()).c_str();
 -      if (m_strVersion.Find(_T(" - ")) != -1)
 -      {
 -              m_strVersion.Replace(_T(" - "), _T("\n"));
 -              m_strVersion += _T(" ");
 -      }
 -      else
 -      {
 -              m_strVersion += _T("\n");
 -      }
 -
 -#ifdef _UNICODE
 -      m_strVersion += theApp.LoadString(IDS_UNICODE).c_str();
 -#endif
 -
 -#if defined _M_IX86
 -      m_strVersion += _T(" x86");
 -#elif defined _M_IA64
 -      m_strVersion += _T(" IA64");
 -#elif defined _M_X64
 -      m_strVersion += _T(" ");
 -      m_strVersion += theApp.LoadString(IDS_WINX64).c_str();
 -#endif
++      GetDlgItem(IDC_STATIC)->SetWindowText(m_p->m_info.developers.c_str());
++      GetDlgItem(IDC_STATIC)->SetFont(&m_font);
+       GetDlgItem(IDC_VERSION)->SetFont(&m_font);
  
 -      String sPrivateBuild = version.GetPrivateBuild();
 -      if (!sPrivateBuild.empty())
 -      {
 -              m_strPrivateBuild = LangFormatString1(IDS_PRIVATEBUILD_FMT,
 -                      sPrivateBuild.c_str()).c_str();
 -      }
 -
 -      String copyright = LoadResString(IDS_SPLASH_GPLTEXT);
 -      copyright += _T("\n");
 -      copyright += version.GetLegalCopyright();
 -      copyright += _T(" All rights reserved.");
 -      m_ctlCompany.SetWindowText(copyright.c_str());
 -      m_ctlWWW.m_link = WinMergeURL;
 +      m_ctlWWW.m_link = m_p->m_info.website.c_str();
  
        UpdateData(FALSE);
        
                      // EXCEPTION: OCX Property Pages should return FALSE
  }
  
 -HBRUSH CAboutDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
++HBRUSH CAboutDlg::Impl::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+ {
+       if (nCtlColor == CTLCOLOR_STATIC && pWnd != GetDlgItem(IDC_WWW))
+       {
+               pDC->SetBkMode(TRANSPARENT);
+               return (HBRUSH)GetStockObject(NULL_BRUSH);
+       }
+       return CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
+ }
 -void CAboutDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
++void CAboutDlg::Impl::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
+ {
+       CRect rc;
+       GetDlgItem(nIDCtl)->GetClientRect(&rc);
+       m_image.Render(CDC::FromHandle(lpDrawItemStruct->hDC), rc);
+ }
  /**
   * @brief Show contributors list.
   * Opens Contributors.txt into notepad.
@@@ -91,12 -91,21 +91,12 @@@ CDiffContext::~CDiffContext(
   * @param [in] bLeft Update left-side info.
   * @param [in] bRight Update right-side info.
   */
--void CDiffContext::UpdateStatusFromDisk(uintptr_t diffpos, bool bLeft, bool bRight)
++void CDiffContext::UpdateStatusFromDisk(uintptr_t diffpos, int index)
  {
        DIFFITEM &di = GetDiffRefAt(diffpos);
 -      if (bLeft)
 -      {
 -              di.diffFileInfo[0].ClearPartial();
 -              if (!di.diffcode.isSideSecondOnly())
 -                      UpdateInfoFromDiskHalf(di, 0);
 -      }
 -      if (bRight)
 -      {
 -              di.diffFileInfo[1].ClearPartial();
 -              if (!di.diffcode.isSideFirstOnly())
 -                      UpdateInfoFromDiskHalf(di, 1);
 -      }
 +      di.diffFileInfo[index].ClearPartial();
 +      if (di.diffcode.isExists(index))
 +              UpdateInfoFromDiskHalf(di, index);
  }
  
  /**
  // One idea would be to provide an iterator over them.
  //
  
 -#include "stdafx.h"
 -#include "Merge.h"
 +#include "DirActions.h"
 +#include "MergeApp.h"
  #include "UnicodeString.h"
 -#include "DirView.h"
 -#include "DirDoc.h"
 -#include "coretools.h"
 -#include "paths.h"
  #include "7zCommon.h"
  #include "ShellFileOperations.h"
 -#include "OptionsDef.h"
 -#include "WaitStatusCursor.h"
  #include "DiffItem.h"
  #include "FileActionScript.h"
 -#include "LoadSaveCodepageDlg.h"
 -#include "IntToIntMap.h"
 -#include "FileOrFolderSelect.h"
 -#include "ConfirmFolderCopyDlg.h"
 +#include "locality.h"
 +#include "FileFilterHelper.h"
 +#include "coretools.h"
  #include "OptionsMgr.h"
  
- using Poco::UIntPtr;
 -#ifdef _DEBUG
 -#define new DEBUG_NEW
 -#undef THIS_FILE
 -static char THIS_FILE[] = __FILE__;
 -#endif
 -
 -// Flags for checking compare items:
 -// Don't check for existence
 -#define ALLOW_DONT_CARE 0
 -// Allow it being folder
 -#define ALLOW_FOLDER 1
 -// Allow it being file
 -#define ALLOW_FILE 2
 -// Allow all types (currently file and folder)
 -#define ALLOW_ALL (ALLOW_FOLDER | ALLOW_FILE)
--
 -static bool ConfirmCopy(int origin, int destination, int count,
 +static void ThrowConfirmCopy(const CDiffContext& ctxt, int origin, int destination, int count,
                const String& src, const String& dest, bool destIsSide);
 -static bool ConfirmMove(int origin, int destination, int count,
 +static void ThrowConfirmMove(const CDiffContext& ctxt, int origin, int destination, int count,
                const String& src, const String& dest, bool destIsSide);
 -static bool ConfirmDialog(const String &caption, const String &question,
 -              int origin, int destination, int count,
 +static void ThrowConfirmationNeededException(const CDiffContext& ctxt, const String &caption, const String &question,
 +              int origin, int destination, size_t count,
                const String& src, const String& dest, bool destIsSide);
  
 -static bool CheckPathsExist(const String &orig, const String& dest, int allowOrig,
 -              int allowDest, String & failedPath);
 -
 +ContentsChangedException::ContentsChangedException(const String& failpath)
 +{
 +      m_msg = string_format_string1(
 +      _("Operation aborted!\n\nFolder contents at disks has changed, path\n%1\nwas not found.\n\nPlease refresh the compare."),
 +      failpath);
 +}
  
  /**
   * @brief Ask user a confirmation for copying item(s).
@@@ -236,113 -977,148 +234,113 @@@ void ConfirmActionList(const CDiffConte
  }
  
  /**
 - * @brief Perform an array of actions
 - * @note There can be only COPY or DELETE actions, not both!
 - * @sa MergeApp::SaveToVersionControl()
 - * @sa MergeApp::SyncFilesToVCS()
 + * @brief Update results for FileActionItem.
 + * This functions is called to update DIFFITEM after FileActionItem.
 + * @param [in] act Action that was done.
 + * @param [in] pos List position for DIFFITEM affected.
   */
 -void CDirView::PerformActionList(FileActionScript & actionScript)
 +UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContext& ctxt, DIFFITEM &di)
  {
 -      // Reset suppressing VSS dialog for multiple files.
 -      // Set in CMainFrame::SaveToVersionControl().
 -      theApp.m_CheckOutMulti = false;
 -      theApp.m_bVssSuppressPathCheck = false;
 -
 -      // Check option and enable putting deleted items to Recycle Bin
 -      if (GetOptionsMgr()->GetBool(OPT_USE_RECYCLE_BIN))
 -              actionScript.UseRecycleBin(true);
 -      else
 -              actionScript.UseRecycleBin(false);
 +      bool bUpdateSrc  = false;
 +      bool bUpdateDest = false;
 +      bool bRemoveItem = false;
 +
 +      // Use FileActionItem types for simplicity for now.
 +      // Better would be to use FileAction contained, since it is not
 +      // UI dependent.
 +      switch (act.UIResult)
 +      {
 +      case FileActionItem::UI_SYNC:
 +              bUpdateSrc = true;
 +              bUpdateDest = true;
 +              di.diffcode.setSideFlag(act.UIDestination);
 +              if (act.dirflag)
 +                      SetDiffCompare(di, DIFFCODE::NOCMP);
 +              else
 +                      SetDiffCompare(di, DIFFCODE::SAME);
 +              SetDiffCounts(di, 0, 0);
 +              break;
 +
 +      case FileActionItem::UI_DEL:
 +              if (di.diffcode.isSideOnly(act.UIOrigin))
 +              {
-                       ctxt.RemoveDiff(reinterpret_cast<UIntPtr>(&di));
++                      ctxt.RemoveDiff(reinterpret_cast<uintptr_t>(&di));
 +                      bRemoveItem = true;
 +              }
 +              else
 +              {
 +                      di.diffcode.unsetSideFlag(act.UIOrigin);
 +                      SetDiffCompare(di, DIFFCODE::NOCMP);
 +                      bUpdateSrc = true;
 +              }
 +              break;
 +      }
  
 -      actionScript.SetParentWindow(this->GetSafeHwnd());
 +      if (bUpdateSrc)
-               ctxt.UpdateStatusFromDisk(reinterpret_cast<UIntPtr>(&di), act.UIOrigin);
++              ctxt.UpdateStatusFromDisk(reinterpret_cast<uintptr_t>(&di), act.UIOrigin);
 +      if (bUpdateDest)
-               ctxt.UpdateStatusFromDisk(reinterpret_cast<UIntPtr>(&di), act.UIDestination);
++              ctxt.UpdateStatusFromDisk(reinterpret_cast<uintptr_t>(&di), act.UIDestination);
  
 -      theApp.AddOperation();
 -      if (actionScript.Run())
 -              UpdateAfterFileScript(actionScript);
 -      theApp.RemoveOperation();
 +      if (bRemoveItem)
 +              return UPDATEITEM_REMOVE;
 +      if (bUpdateSrc | bUpdateDest)
 +              return UPDATEITEM_UPDATE;
 +      return UPDATEITEM_NONE;
  }
  
  /**
 - * @brief Update results after running FileActionScript.
 - * This functions is called after script is finished to update
 - * results (including UI).
 - * @param [in] actionlist Script that was run.
 + * @brief Find the CDiffContext diffpos of an item from its left & right paths
 + * @return POSITION to item, NULL if not found.
 + * @note Filenames must be same, if they differ NULL is returned.
   */
- UIntPtr FindItemFromPaths(const CDiffContext& ctxt, const String& pathLeft, const String& pathRight)
 -void CDirView::UpdateAfterFileScript(FileActionScript & actionList)
++uintptr_t FindItemFromPaths(const CDiffContext& ctxt, const String& pathLeft, const String& pathRight)
  {
 -      bool bItemsRemoved = false;
 -      int curSel = GetFirstSelectedInd();
 -      CDirDoc *pDoc = GetDocument();
 -      while (actionList.GetActionItemCount()>0)
 +      String file1 = paths_FindFileName(pathLeft);
 +      String file2 = paths_FindFileName(pathRight);
 +
 +      // Filenames must be identical
 +      if (string_compare_nocase(file1, file2) != 0)
 +              return NULL;
 +
 +      String path1(pathLeft, 0, pathLeft.length() - file1.length()); // include trailing backslash
 +      String path2(pathRight, 0, pathRight.length() - file2.length()); // include trailing backslash
 +
 +      // Path can contain (because of difftools?) '/' and '\'
 +      // so for comparing purposes, convert whole path to use '\\'
 +      replace_char(&*path1.begin(), '/', '\\');
 +      replace_char(&*path2.begin(), '/', '\\');
 +
 +      String base1 = ctxt.GetLeftPath(); // include trailing backslash
 +      if (path1.compare(0, base1.length(), base1.c_str()) != 0)
 +              return NULL;
 +      path1.erase(0, base1.length()); // turn into relative path
 +      if (String::size_type length = path1.length())
 +              path1.resize(length - 1); // remove trailing backslash
 +
 +      String base2 = ctxt.GetRightPath(); // include trailing backslash
 +      if (path2.compare(0, base2.length(), base2.c_str()) != 0)
 +              return NULL;
 +      path2.erase(0, base2.length()); // turn into relative path
 +      if (String::size_type length = path2.length())
 +              path2.resize(length - 1); // remove trailing backslash
 +
-       UIntPtr pos = ctxt.GetFirstDiffPosition();
-       while (UIntPtr currentPos = pos) // Save our current pos before getting next
++      uintptr_t pos = ctxt.GetFirstDiffPosition();
++      while (uintptr_t currentPos = pos) // Save our current pos before getting next
        {
 -              // Start handling from tail of list, so removing items
 -              // doesn't invalidate our item indexes.
 -              FileActionItem act = actionList.RemoveTailActionItem();
 -              UINT_PTR diffpos = GetItemKey(act.context);
 -              DIFFCODE diffcode = pDoc->GetDiffByKey(diffpos).diffcode;
 -              bool bUpdateLeft = false;
 -              bool bUpdateRight = false;
 -
 -              // Synchronized items may need VCS operations
 -              if (act.UIResult == FileActionItem::UI_SYNC)
 +              const DIFFITEM &di = ctxt.GetNextDiffPosition(pos);
 +              if (di.diffFileInfo[0].path == path1 &&
 +                      di.diffFileInfo[1].path == path2 &&
 +                      di.diffFileInfo[0].filename == file1 &&
 +                      di.diffFileInfo[1].filename == file2)
                {
 -                      if (theApp.m_bCheckinVCS)
 -                              theApp.CheckinToClearCase(act.dest);
 +                      return currentPos;
                }
 -
 -              // Update UI
 -              switch (act.UIResult)
 -              {
 -              case FileActionItem::UI_SYNC:
 -                      bUpdateLeft = true;
 -                      bUpdateRight = true;
 -                      break;
 -              
 -              case FileActionItem::UI_DESYNC:
 -                      // Cannot happen yet since we have only "simple" operations
 -                      break;
 -
 -              case FileActionItem::UI_DEL_LEFT:
 -                      if (diffcode.isSideFirstOnly())
 -                      {
 -                              if (m_bTreeMode)
 -                                      CollapseSubdir(act.context);
 -                              m_pList->DeleteItem(act.context);
 -                              bItemsRemoved = true;
 -                      }
 -                      else
 -                      {
 -                              bUpdateLeft = true;
 -                      }
 -                      break;
 -
 -              case FileActionItem::UI_DEL_RIGHT:
 -                      if (diffcode.isSideSecondOnly())
 -                      {
 -                              if (m_bTreeMode)
 -                                      CollapseSubdir(act.context);
 -                              m_pList->DeleteItem(act.context);
 -                              bItemsRemoved = true;
 -                      }
 -                      else
 -                      {
 -                              bUpdateRight = true;
 -                      }
 -                      break;
 -
 -              case FileActionItem::UI_DEL_BOTH:
 -                      if (m_bTreeMode)
 -                              CollapseSubdir(act.context);
 -                      m_pList->DeleteItem(act.context);
 -                      bItemsRemoved = true;
 -                      break;
 -              }
 -
 -              // Update doc (difflist)
 -              pDoc->UpdateDiffAfterOperation(act, diffpos);
 -
 -              if (bUpdateLeft || bUpdateRight)
 -              {
 -                      pDoc->UpdateStatusFromDisk(diffpos, bUpdateLeft, bUpdateRight);
 -                      UpdateDiffItemStatus(act.context);
 -              }
 -      }
 -      
 -      // Make sure selection is at sensible place if all selected items
 -      // were removed.
 -      if (bItemsRemoved == true)
 -      {
 -              UINT selected = GetSelectedCount();
 -              if (selected == 0)
 -              {
 -                      if (curSel < 1)
 -                              ++curSel;
 -                      MoveFocus(0, curSel - 1, selected);
 -              }
 -      }
 -}
 -
 -/// Get directories of first selected item
 -bool CDirView::GetSelectedDirNames(String& strLeft, String& strRight) const
 -{
 -      bool bResult = GetSelectedFileNames(strLeft, strRight);
 -
 -      if (bResult)
 -      {
 -              strLeft = paths_GetPathOnly(strLeft);
 -              strRight = paths_GetPathOnly(strRight);
 -      }
 -      return bResult;
 -}
 +      }
 +      return 0;
 +}
  
  /// is it possible to copy item to left ?
 -bool CDirView::IsItemCopyableToLeft(const DIFFITEM & di) const
 +bool IsItemCopyable(const DIFFITEM & di, int index)
  {
        // don't let them mess with error items
        if (di.diffcode.isResultError()) return false;
@@@ -545,303 -1356,58 +543,303 @@@ bool IsItemNavigableDiff(const CDiffCon
        return true;
  }
  
 -/// get the file names on both sides for first selected item
 -bool CDirView::GetSelectedFileNames(String& strLeft, String& strRight) const
 +bool IsItemExistAll(const CDiffContext& ctxt, const DIFFITEM & di)
  {
 -      int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
 -      if (sel == -1)
 +      // Not a valid diffitem, one of special items (e.g "..")
 +      if (di.diffcode.diffcode == 0)
                return false;
 -      GetItemFileNames(sel, strLeft, strRight);
 +      if (ctxt.GetCompareDirs() == 2)
 +              return di.diffcode.isSideBoth();
 +      else
 +              return di.diffcode.isSideAll();
 +}
 +
 +
 +/**
 + * @brief Determines if the user wants to see given item.
 + * This function determines what items to show and what items to hide. There
 + * are lots of combinations, but basically we check if menuitem is enabled or
 + * disabled and show/hide matching items. For non-recursive compare we never
 + * hide folders as that would disable user browsing into them. And we even
 + * don't really know if folders are identical or different as we haven't
 + * compared them.
 + * @param [in] di Item to check.
 + * @return true if item should be shown, false if not.
 + * @sa CDirDoc::Redisplay()
 + */
 +bool IsShowable(const CDiffContext& ctxt, const DIFFITEM & di, const DirViewFilterSettings& filter)
 +{
 +      if (di.customFlags1 & ViewCustomFlags::HIDDEN)
 +              return false;
 +
 +      if (di.diffcode.isResultFiltered())
 +      {
 +              // Treat SKIPPED as a 'super'-flag. If item is skipped and user
 +              // wants to see skipped items show item regardless of other flags
 +              return filter.show_skipped;
 +      }
 +
 +      if (di.diffcode.isDirectory())
 +      {
 +              // Subfolders in non-recursive compare can only be skipped or unique
 +              if (!ctxt.m_bRecursive)
 +              {
 +                      // left/right filters
 +                      if (di.diffcode.isSideFirstOnly() && !filter.show_unique_left)
 +                              return false;
 +                      if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right)
 +                              return false;
 +
 +                      // result filters
 +                      if (di.diffcode.isResultError() /*&& !GetMainFrame()->m_bShowErrors FIXME:*/)
 +                              return false;
 +              }
 +              else // recursive mode (including tree-mode)
 +              {
 +                      // left/right filters
 +                      if (di.diffcode.isSideFirstOnly() && !filter.show_unique_left)
 +                              return false;
 +                      if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right)
 +                              return false;
 +
 +                      // ONLY filter folders by result (identical/different) for tree-view.
 +                      // In the tree-view we show subfolders with identical/different
 +                      // status. The flat view only shows files inside folders. So if we
 +                      // filter by status the files inside folder are filtered too and
 +                      // users see files appearing/disappearing without clear logic.          
 +                      if (filter.tree_mode)
 +                      {
 +                              // result filters
 +                              if (di.diffcode.isResultError()/* && !GetMainFrame()->m_bShowErrors FIXME:*/)
 +                                      return false;
 +
 +                              // result filters
 +                              if (di.diffcode.isResultSame() && !filter.show_identical)
 +                                      return false;
 +                              if (di.diffcode.isResultDiff() && !filter.show_different)
 +                                      return false;
 +                      }
 +              }
 +      }
 +      else
 +      {
 +              // left/right filters
 +              if (di.diffcode.isSideFirstOnly() && !filter.show_unique_left)
 +                      return false;
 +              if (di.diffcode.isSideSecondOnly() && !filter.show_unique_right)
 +                      return false;
 +
 +              // file type filters
 +              if (di.diffcode.isBin() && !filter.show_binaries)
 +                      return false;
 +
 +              // result filters
 +              if (di.diffcode.isResultSame() && !filter.show_identical)
 +                      return false;
 +              if (di.diffcode.isResultError() /* && !GetMainFrame()->m_bShowErrors FIXME:*/)
 +                      return false;
 +              if (di.diffcode.isResultDiff() && !filter.show_different)
 +                      return false;
 +      }
        return true;
  }
 -/// get file name on specified side for first selected item
 -String CDirView::GetSelectedFileName(SIDE_TYPE stype) const
 +
 +/**
 + * @brief Open one selected item.
 + * @param [in] pos1 Item position.
 + * @param [in,out] di1 Pointer to first diffitem.
 + * @param [in,out] di2 Pointer to second diffitem.
 + * @param [in,out] di3 Pointer to third diffitem.
 + * @param [out] paths First/Second/Third paths.
 + * @param [out] sel1 Item's selection index in listview.
 + * @param [in,out] isDir Is item folder?
 + * return false if there was error or item was completely processed.
 + */
- bool GetOpenOneItem(const CDiffContext& ctxt, UIntPtr pos1, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
++bool GetOpenOneItem(const CDiffContext& ctxt, uintptr_t pos1, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
 +              PathContext & paths, int & sel1, bool & isdir, String& errmsg)
  {
 -      String left, right;
 -      if (!GetSelectedFileNames(left, right)) return _T("");
 -      return stype==SIDE_LEFT ? left : right;
 +      *di1 = &ctxt.GetDiffAt(pos1);
 +      *di2 = *di1;
 +      *di3 = *di1;
 +
 +      paths = GetItemFileNames(ctxt, **di1);
 +
 +      if ((*di1)->diffcode.isDirectory())
 +              isdir = true;
 +
 +      if (isdir && ((*di1)->diffcode.isExistsFirst() && (*di1)->diffcode.isExistsSecond() && (*di1)->diffcode.isExistsThird()))
 +      {
 +              // Check both folders exist. If either folder is missing that means
 +              // folder has been changed behind our back, so we just tell user to
 +              // refresh the compare.
 +              PATH_EXISTENCE path1Exists = paths_DoesPathExist(paths[0]);
 +              PATH_EXISTENCE path2Exists = paths_DoesPathExist(paths[1]);
 +              if (path1Exists != IS_EXISTING_DIR || path2Exists != IS_EXISTING_DIR)
 +              {
 +                      String invalid = path1Exists == IS_EXISTING_DIR ? paths[0] : paths[1];
 +                      errmsg = string_format_string1(
 +                              _("Operation aborted!\n\nFolder contents at disks has changed, path\n%1\nwas not found.\n\nPlease refresh the compare."),
 +                              invalid);
 +                      return false;
 +              }
 +      }
 +
 +      return true;
  }
 +
  /**
 - * @brief Get the file names on both sides for specified item.
 - * @note Return empty strings if item is special item.
 + * @brief Open two selected items.
 + * @param [in] pos1 First item position.
 + * @param [in] pos2 Second item position.
 + * @param [in,out] di1 Pointer to first diffitem.
 + * @param [in,out] di2 Pointer to second diffitem.
 + * @param [out] paths First/Second/Third paths.
 + * @param [out] sel1 First item's selection index in listview.
 + * @param [out] sel2 Second item's selection index in listview.
 + * @param [in,out] isDir Is item folder?
 + * return false if there was error or item was completely processed.
   */
- bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, UIntPtr pos1, UIntPtr pos2, const DIFFITEM **di1, const DIFFITEM **di2,
 -void CDirView::GetItemFileNames(int sel, String& strLeft, String& strRight) const
++bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, uintptr_t pos1, uintptr_t pos2, const DIFFITEM **di1, const DIFFITEM **di2,
 +              PathContext & paths, int & sel1, int & sel2, bool & isDir, String& errmsg)
  {
 -      UINT_PTR diffpos = GetItemKey(sel);
 -      if (diffpos == (UINT_PTR)SPECIAL_ITEM_POS)
 +      String pathLeft, pathRight;
 +
 +      // Two items selected, get their info
 +      *di1 = &ctxt.GetDiffAt(pos1);
 +      *di2 = &ctxt.GetDiffAt(pos2);
 +
 +      // Check for binary & side compatibility & file/dir compatibility
 +      if (!AreItemsOpenable(ctxt, selectionType, **di1, **di2))
        {
 -              strLeft.erase();
 -              strRight.erase();
 +              return false;
        }
 -      else
 +
 +      String temp;
 +      switch (selectionType)
        {
 -              const DIFFITEM & di = GetDocument()->GetDiffByKey(diffpos);
 -              const String leftrelpath = di.diffFileInfo[0].GetFile();
 -              const String rightrelpath = di.diffFileInfo[1].GetFile();
 -              const String & leftpath = GetDocument()->GetBasePath(0);
 -              const String & rightpath = GetDocument()->GetBasePath(1);
 -              strLeft = paths_ConcatPath(leftpath, leftrelpath);
 -              strRight = paths_ConcatPath(rightpath, rightrelpath);
 +      case SELECTIONTYPE_NORMAL:
 +              // Ensure that di1 is on left (swap if needed)
 +              if ((*di1)->diffcode.isSideSecondOnly() || ((*di1)->diffcode.isSideBoth() &&
 +                              (*di2)->diffcode.isSideFirstOnly()))
 +              {
 +                      const DIFFITEM * temp = *di1;
 +                      *di1 = *di2;
 +                      *di2 = temp;
 +                      int num = sel1;
 +                      sel1 = sel2;
 +                      sel2 = num;
 +              }
 +              // Fill in pathLeft & pathRight
 +              GetItemFileNames(ctxt, **di1, pathLeft, temp);
 +              GetItemFileNames(ctxt, **di2, temp, pathRight);
 +              break;
 +      case SELECTIONTYPE_LEFT1LEFT2:
 +              GetItemFileNames(ctxt, **di1, pathLeft, temp);
 +              GetItemFileNames(ctxt, **di2, pathRight, temp);
 +              break;
 +      case SELECTIONTYPE_RIGHT1RIGHT2:
 +              GetItemFileNames(ctxt, **di1, temp, pathLeft);
 +              GetItemFileNames(ctxt, **di2, temp, pathRight);
 +              break;
 +      case SELECTIONTYPE_LEFT1RIGHT2:
 +              GetItemFileNames(ctxt, **di1, pathLeft, temp);
 +              GetItemFileNames(ctxt, **di2, temp, pathRight);
 +              break;
 +      case SELECTIONTYPE_LEFT2RIGHT1:
 +              GetItemFileNames(ctxt, **di1, temp, pathRight);
 +              GetItemFileNames(ctxt, **di2, pathLeft, temp);
 +              break;
 +      }
 +
 +      if ((*di1)->diffcode.isDirectory())
 +      {
 +              isDir = true;
 +              if (GetPairComparability(PathContext(pathLeft, pathRight)) != IS_EXISTING_DIR)
 +              {
 +                      errmsg = _("The selected folder is invalid.");
 +                      return false;
 +              }
        }
 +
 +      paths.SetLeft(pathLeft);
 +      paths.SetRight(pathRight);
 +
 +      return true;
  }
  
  /**
 - * @brief Get the file names on both sides for specified item.
 - * @note Return empty strings if item is special item.
 + * @brief Open three selected items.
 + * @param [in] pos1 First item position.
 + * @param [in] pos2 Second item position.
 + * @param [in] pos3 Third item position.
 + * @param [in,out] di1 Pointer to first diffitem.
 + * @param [in,out] di2 Pointer to second diffitem.
 + * @param [in,out] di3 Pointer to third diffitem.
 + * @param [out] paths First/Second/Third paths.
 + * @param [out] sel1 First item's selection index in listview.
 + * @param [out] sel2 Second item's selection index in listview.
 + * @param [out] sel3 Third item's selection index in listview.
 + * @param [in,out] isDir Is item folder?
 + * return false if there was error or item was completely processed.
   */
- bool GetOpenThreeItems(const CDiffContext& ctxt, UIntPtr pos1, UIntPtr pos2, UIntPtr pos3, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
 -void CDirView::GetItemFileNames(int sel, PathContext * paths) const
++bool GetOpenThreeItems(const CDiffContext& ctxt, uintptr_t pos1, uintptr_t pos2, uintptr_t pos3, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
 +              PathContext & paths, int & sel1, int & sel2, int & sel3, bool & isDir, String& errmsg)
  {
 -      String strPath[3];
 -      UINT_PTR diffpos = GetItemKey(sel);
 -      if (diffpos == SPECIAL_ITEM_POS)
 +      String pathLeft, pathMiddle, pathRight;
 +
 +      if (!pos3)
        {
 -              for (int nIndex = 0; nIndex < GetDocument()->m_nDirs; nIndex++)
 -                      paths->SetPath(nIndex, _T(""));
 +              // Two items selected, get their info
 +              *di1 = &ctxt.GetDiffAt(pos1);
 +              *di2 = &ctxt.GetDiffAt(pos2);
 +
 +              // Check for binary & side compatibility & file/dir compatibility
 +              if (!::AreItemsOpenable(ctxt, **di1, **di2, **di2) && 
 +                      !::AreItemsOpenable(ctxt, **di1, **di1, **di2))
 +              {
 +                      return false;
 +              }
 +              // Ensure that di1 is on left (swap if needed)
 +              if ((*di1)->diffcode.isExists(0) && (*di1)->diffcode.isExists(1) && (*di2)->diffcode.isExists(2))
 +              {
 +                      *di3 = *di2;
 +                      *di2 = *di1;
 +                      sel3 = sel2;
 +                      sel2 = sel1;
 +              }
 +              else if ((*di1)->diffcode.isExists(0) && (*di1)->diffcode.isExists(2) && (*di2)->diffcode.isExists(1))
 +              {
 +                      *di3 = *di1;
 +                      sel3 = sel1;
 +              }
 +              else if ((*di1)->diffcode.isExists(1) && (*di1)->diffcode.isExists(2) && (*di2)->diffcode.isExists(0))
 +              {
 +                      std::swap(*di1, *di2);
 +                      std::swap(sel1, sel2);
 +                      *di3 = *di2;
 +                      sel3 = sel2;
 +              }
 +              else if ((*di2)->diffcode.isExists(0) && (*di2)->diffcode.isExists(1) && (*di1)->diffcode.isExists(2))
 +              {
 +                      std::swap(*di1, *di2);
 +                      std::swap(sel1, sel2);
 +                      *di3 = *di2;
 +                      *di2 = *di1;
 +                      sel3 = sel2;
 +                      sel2 = sel1;
 +              }
 +              else if ((*di2)->diffcode.isExists(0) && (*di2)->diffcode.isExists(2) && (*di1)->diffcode.isExists(1))
 +              {
 +                      std::swap(*di1, *di2);
 +                      std::swap(sel1, sel2);
 +                      *di3 = *di1;
 +                      sel3 = sel1;
 +              }
 +              else if ((*di2)->diffcode.isExists(1) && (*di2)->diffcode.isExists(2) && (*di1)->diffcode.isExists(0))
 +              {
 +                      *di3 = *di2;
 +                      sel3 = sel2;
 +              }
        }
        else
        {
@@@ -1052,148 -1587,58 +1050,148 @@@ void SetDiffCounts(DIFFITEM& di, unsign
  }
  
  /**
 - * @brief Display file encoding dialog to user & handle user's choices
 - *
 - * This handles DirView invocation, so multiple files may be affected
 + * @brief Set item's view-flag.
 + * @param [in] key Item fow which flag is set.
 + * @param [in] flag Flag value to set.
 + * @param [in] mask Mask for possible flag values.
   */
 -void CDirView::DoFileEncodingDialog()
 +void SetItemViewFlag(DIFFITEM& di, unsigned flag, unsigned mask)
  {
 -      CLoadSaveCodepageDlg dlg(GetDocument()->m_nDirs);
 -      // set up labels about what will be affected
 -      FormatEncodingDialogDisplays(&dlg);
 -      dlg.EnableSaveCodepage(false); // disallow setting a separate codepage for saving
 +      unsigned curFlags = di.customFlags1;
 +      curFlags &= ~mask; // Zero bits masked
 +      curFlags |= flag;
 +      di.customFlags1 = curFlags;
 +}
  
 -      // Invoke dialog
 -      if (dlg.DoModal() != IDOK)
 -              return;
 +/**
 + * @brief Set all item's view-flag.
 + * @param [in] flag Flag value to set.
 + * @param [in] mask Mask for possible flag values.
 + */
 +void SetItemViewFlag(CDiffContext& ctxt, unsigned flag, unsigned mask)
 +{
-       UIntPtr pos = ctxt.GetFirstDiffPosition();
++      uintptr_t pos = ctxt.GetFirstDiffPosition();
  
 -      int nCodepage = dlg.GetLoadCodepage();
 +      while (pos != NULL)
 +      {
 +              UINT curFlags = ctxt.GetCustomFlags1(pos);
 +              curFlags &= ~mask; // Zero bits masked
 +              curFlags |= flag;
 +              ctxt.SetCustomFlags1(pos, curFlags);
 +              ctxt.GetNextDiffPosition(pos);
 +      }
 +}
  
 -      bool doLeft = dlg.DoesAffectLeft();
 -      bool doRight = dlg.DoesAffectRight();
 +/**
 + * @brief Mark selected items as needing for rescan.
 + * @return Count of items to rescan.
 + */
 +void MarkForRescan(DIFFITEM &di)
 +{
 +      SetDiffStatus(di, 0, DIFFCODE::TEXTFLAGS | DIFFCODE::SIDEFLAGS | DIFFCODE::COMPAREFLAGS);
 +      SetDiffStatus(di, DIFFCODE::NEEDSCAN, DIFFCODE::SCANFLAGS);
 +}
  
 -      int i=-1;
 -      while ((i = m_pList->GetNextItem(i, LVNI_SELECTED)) != -1)
 +/**
 + * @brief Return string such as "15 of 30 Files Affected" or "30 Files Affected"
 + */
 +String FormatFilesAffectedString(int nFilesAffected, int nFilesTotal)
 +{
 +      if (nFilesAffected == nFilesTotal)
 +              return string_format_string1(_("(%1 Files Affected)"), NumToStr(nFilesTotal));
 +      else
 +              return string_format_string2(_("(%1 of %2 Files Affected)"), NumToStr(nFilesAffected), NumToStr(nFilesTotal));
 +}
 +
 +String FormatMenuItemString(const String& fmt1, const String& fmt2, int count, int total)
 +{
 +      if (count == total)
 +              return string_format_string1(fmt1, NumToStr(total));
 +      else
 +              return string_format_string2(fmt2, NumToStr(count), NumToStr(total));
 +}
 +
 +String FormatMenuItemString(SIDE_TYPE src, SIDE_TYPE dst, int count, int total)
 +{
 +      String fmt1, fmt2;
 +      if (src == SIDE_LEFT && dst == SIDE_RIGHT)
        {
 -              DIFFITEM & di = GetDiffItemRef(i);
 -              if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
 -                      continue;
 -              if (di.diffcode.isDirectory())
 -                      continue;
 +              fmt1 = _("Left to Right (%1)");
 +              fmt2 = _("Left to Right (%1 of %2)");
 +      }
 +      else if (src == SIDE_LEFT && dst == SIDE_MIDDLE)
 +      {
 +              fmt1 = _("Left to Middle (%1)");
 +              fmt2 = _("Left to middle (%1 of %2)");
 +      }
 +      else if (src == SIDE_MIDDLE && dst == SIDE_LEFT)
 +      {
 +              fmt1 = _("Middle to Left (%1)");
 +              fmt2 = _("Middle to Left (%1 of %2)");
 +      }
 +      else if (src == SIDE_MIDDLE && dst == SIDE_RIGHT)
 +      {
 +              fmt1 = _("Middle to Right (%1)");
 +              fmt2 = _("Middle to Right (%1 of %2)");
 +      }
 +      else if (src == SIDE_RIGHT && dst == SIDE_LEFT)
 +      {
 +              fmt1 = _("Right to Left (%1)");
 +              fmt2 = _("Right to Left (%1 of %2)");
 +      }
 +      else if (src == SIDE_RIGHT && dst == SIDE_MIDDLE)
 +      {
 +              fmt1 = _("Right to Middle (%1)");
 +              fmt2 = _("Right to Middle (%1 of %2)");
 +      }
 +      return FormatMenuItemString(fmt1, fmt2, count, total);
 +}
  
 -              // Does it exist on left? (ie, right or both)
 -              if (doLeft && di.diffcode.isExistsFirst() && di.diffFileInfo[0].IsEditableEncoding())
 -              {
 -                      di.diffFileInfo[0].encoding.SetCodepage(nCodepage);
 -              }
 -              // Does it exist on right (ie, left or both)
 -              if (doRight && di.diffcode.isExistsSecond() && di.diffFileInfo[1].IsEditableEncoding())
 -              {
 -                      di.diffFileInfo[1].encoding.SetCodepage(nCodepage);
 -              }
 +String FormatMenuItemString(SIDE_TYPE src, int count, int total)
 +{
 +      String fmt1, fmt2;
 +      if (src == SIDE_LEFT)
 +      {
 +              fmt1 = _("Left (%1)");
 +              fmt2 = _("Left (%1 of %2)");
 +      }
 +      else if (src == SIDE_MIDDLE)
 +      {
 +              fmt1 = _("Middle (%1)");
 +              fmt2 = _("Middle (%1 of %2)");
 +      }
 +      else if (src == SIDE_RIGHT)
 +      {
 +              fmt1 = _("Right (%1)");
 +              fmt2 = _("Right (%1 of %2)");
        }
 -      m_pList->InvalidateRect(NULL);
 -      m_pList->UpdateWindow();
 +      return FormatMenuItemString(fmt1, fmt2, count, total);
 +}
  
 -      // TODO: We could loop through any active merge windows belonging to us
 -      // and see if any of their files are affected
 -      // but, if they've been edited, we cannot throw away the user's work?
 +String FormatMenuItemStringBoth(int count, int total)
 +{
 +      return FormatMenuItemString(_("Both (%1)"), _("Both (%1 of %2)"), count, total);
  }
  
 -void CDirView::DoUpdateFileEncodingDialog(CCmdUI* pCmdUI)
 +String FormatMenuItemStringTo(SIDE_TYPE src, int count, int total)
  {
 -      bool haveSelectedItems = (m_pList->GetNextItem(-1, LVNI_SELECTED) != -1);
 -      pCmdUI->Enable(haveSelectedItems);
 +      String fmt1, fmt2;
 +      if (src == SIDE_LEFT)
 +      {
 +              fmt1 = _("Left to... (%1)");
 +              fmt2 = _("Left to... (%1 of %2)");
 +      }
 +      else if (src == SIDE_MIDDLE)
 +      {
 +              fmt1 = _("Middle to... (%1)");
 +              fmt2 = _("Middle to... (%1 of %2)");
 +      }
 +      else if (src == SIDE_RIGHT)
 +      {
 +              fmt1 = _("Right to... (%1)");
 +              fmt2 = _("Right to... (%1 of %2)");
 +      }
 +      return FormatMenuItemString(fmt1, fmt2, count, total);
  }
  
  /**
@@@ -1231,82 -1679,62 +1229,82 @@@ bool RenameOnSameDir(const String& szOl
  }
  
  /**
 - * @brief Rename selected item on both left and right sides.
 - *
 - * @param szNewItemName [in] New item name.
 - *
 - * @return true if at least one file was renamed successfully.
 - */
 -bool CDirView::DoItemRename(const String& szNewItemName)
 + * @brief Convert number to string.
 + * Converts number to string, with commas between digits in
 + * locale-appropriate manner.
 +*/
 +String NumToStr(int n)
  {
 -      PathContext paths;
 -      int nDirs = GetDocument()->m_nDirs;
 -
 -      int nSelItem = m_pList->GetNextItem(-1, LVNI_SELECTED);
 -      ASSERT(-1 != nSelItem);
 -      GetItemFileNames(nSelItem, &paths);
 -
 -      // We must check that paths still exists
 -      String failpath;
 -      DIFFITEM &di = GetDiffItemRef(nSelItem);
 -      bool succeed = CheckPathsExist(paths.GetLeft(), paths.GetRight(), 
 -              di.diffcode.isExistsFirst() ? ALLOW_FILE | ALLOW_FOLDER : ALLOW_DONT_CARE,
 -              di.diffcode.isExistsSecond() ? ALLOW_FILE | ALLOW_FOLDER : ALLOW_DONT_CARE,
 -              failpath);
 -      if (succeed == false)
 +      return locality::NumToLocaleStr(n);
 +}
 +
 +void ExpandSubdirs(CDiffContext& ctxt, DIFFITEM& dip)
 +{
 +      dip.customFlags1 |= ViewCustomFlags::EXPANDED;
-       UIntPtr diffpos = ctxt.GetFirstChildDiffPosition(reinterpret_cast<UIntPtr>(&dip));
++      uintptr_t diffpos = ctxt.GetFirstChildDiffPosition(reinterpret_cast<uintptr_t>(&dip));
 +      while (diffpos)
        {
 -              WarnContentsChanged(failpath);
 -              return false;
 +              DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
 +              if (!di.IsAncestor(&dip))
 +                      break;
 +              if (di.HasChildren())
 +                      di.customFlags1 |= ViewCustomFlags::EXPANDED;
        }
 +}
  
 -      UINT_PTR key = GetItemKey(nSelItem);
 -      ASSERT(key != SPECIAL_ITEM_POS);
 -      ASSERT(&di == &GetDocument()->GetDiffRefByKey(key));
 -
 -      bool bRename[3] = {false};
 -      int index;
 -      for (index = 0; index < nDirs; index++)
 +void ExpandAllSubdirs(CDiffContext& ctxt)
 +{
-       UIntPtr diffpos = ctxt.GetFirstDiffPosition();
++      uintptr_t diffpos = ctxt.GetFirstDiffPosition();
 +      while (diffpos)
        {
 -              if (di.diffcode.isExists(index))
 -                      bRename[index] = RenameOnSameDir(paths[index], szNewItemName);
 +              DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
 +              di.customFlags1 |= ViewCustomFlags::EXPANDED;
        }
 +}
  
 -      int nSuccessCount = 0;
 -      for (index = 0; index < nDirs; index++)
 -              nSuccessCount += bRename[index] ? 1 : 0;
 +void CollapseAllSubdirs(CDiffContext& ctxt)
 +{
-       UIntPtr diffpos = ctxt.GetFirstDiffPosition();
++      uintptr_t diffpos = ctxt.GetFirstDiffPosition();
 +      while (diffpos)
 +      {
 +              DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
 +              di.customFlags1 &= ~ViewCustomFlags::EXPANDED;
 +      }
 +}
  
 -      if (nSuccessCount > 0)
 +DirViewTreeState *SaveTreeState(const CDiffContext& ctxt)
 +{
 +      DirViewTreeState *pTreeState = new DirViewTreeState();
-       UIntPtr diffpos = ctxt.GetFirstDiffPosition();
++      uintptr_t diffpos = ctxt.GetFirstDiffPosition();
 +      while (diffpos)
        {
 -              for (index = 0; index < nDirs; index++)
 +              const DIFFITEM &di = ctxt.GetNextDiffPosition(diffpos);
 +              if (di.HasChildren())
                {
 -                      if (bRename[index])
 -                              di.diffFileInfo[index].filename = szNewItemName;
 -                      else
 -                              di.diffFileInfo[index].filename = _T("");
 +                      String relpath = paths_ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename);
 +                      pTreeState->insert(std::pair<String, bool>(relpath, !!(di.customFlags1 & ViewCustomFlags::EXPANDED)));
                }
        }
 +      return pTreeState;
 +}
  
 -      return (bRename[0] || bRename[1] || (nDirs > 2 && bRename[2]));
 +void RestoreTreeState(CDiffContext& ctxt, DirViewTreeState *pTreeState)
 +{
-       UIntPtr diffpos = ctxt.GetFirstDiffPosition();
++      uintptr_t diffpos = ctxt.GetFirstDiffPosition();
 +      while (diffpos)
 +      {
 +              DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
 +              if (di.HasChildren())
 +              {
 +                      String relpath = paths_ConcatPath(di.diffFileInfo[0].path, di.diffFileInfo[0].filename);
 +                      std::map<String, bool>::iterator p = pTreeState->find(relpath);
 +                      if (p != pTreeState->end())
 +                      {
 +                              di.customFlags1 &= ~ViewCustomFlags::EXPANDED;
 +                              di.customFlags1 |= (p->second ? ViewCustomFlags::EXPANDED : 0);
 +                      }
 +              }
 +      }
  }
  
  /**
index 9fc5be5,0000000..4c52a2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,792 -1,0 +1,792 @@@
- Poco::UIntPtr FindItemFromPaths(const CDiffContext& ctxt, const String& pathLeft, const String& pathRight);
 +#pragma once
 +
 +#include "UnicodeString.h"
 +#include "DiffContext.h"
 +#include "FileActionScript.h"
 +#include "paths.h"
 +#include "IntToIntMap.h"
 +#include <algorithm>
 +
 +struct DIFFITEM;
 +class CDiffContext;
 +class PathContext;
 +class PluginManager;
 +class FileActionScript;
 +class CTempPathContext;
 +
 +/**
 + * @brief Folder compare icon indexes.
 + * This enum defines indexes for imagelist used for folder compare icons.
 + * Note that this enum must be in synch with code in OnInitialUpdate() and
 + * GetColImage(). Also remember that icons are in resource file...
 + */
 +enum
 +{
 +      DIFFIMG_LUNIQUE,
 +      DIFFIMG_MUNIQUE,
 +      DIFFIMG_RUNIQUE,
 +      DIFFIMG_LMISSING,
 +      DIFFIMG_MMISSING,
 +      DIFFIMG_RMISSING,
 +      DIFFIMG_DIFF,
 +      DIFFIMG_SAME,
 +      DIFFIMG_BINSAME,
 +      DIFFIMG_BINDIFF,
 +      DIFFIMG_LDIRUNIQUE,
 +      DIFFIMG_MDIRUNIQUE,
 +      DIFFIMG_RDIRUNIQUE,
 +      DIFFIMG_LDIRMISSING,
 +      DIFFIMG_MDIRMISSING,
 +      DIFFIMG_RDIRMISSING,
 +      DIFFIMG_SKIP,
 +      DIFFIMG_DIRSKIP,
 +      DIFFIMG_DIRDIFF,
 +      DIFFIMG_DIRSAME,
 +      DIFFIMG_DIR,
 +      DIFFIMG_ERROR,
 +      DIFFIMG_DIRUP,
 +      DIFFIMG_DIRUP_DISABLE,
 +      DIFFIMG_ABORT,
 +      DIFFIMG_TEXTDIFF,
 +      DIFFIMG_TEXTSAME,
 +};
 +
 +typedef enum {
 +      SIDE_LEFT = 1,
 +      SIDE_MIDDLE,
 +      SIDE_RIGHT
 +} SIDE_TYPE;
 +
 +typedef enum {
 +      SELECTIONTYPE_NORMAL,
 +      SELECTIONTYPE_LEFT1LEFT2,
 +      SELECTIONTYPE_RIGHT1RIGHT2,
 +      SELECTIONTYPE_LEFT1RIGHT2,
 +      SELECTIONTYPE_LEFT2RIGHT1
 +} SELECTIONTYPE;
 +
 +typedef enum {
 +      UPDATEITEM_NONE,
 +      UPDATEITEM_UPDATE,
 +      UPDATEITEM_REMOVE
 +} UPDATEITEM_TYPE;
 +
 +struct ViewCustomFlags
 +{
 +      enum
 +      {
 +              // We use extra bits so that no valid values are 0
 +              // and each set of flags is in a different hex digit
 +              // to make debugging easier
 +              // These can always be packed down in the future
 +              INVALID_CODE = 0,
 +              VISIBILITY = 0x3, VISIBLE = 0x1, HIDDEN = 0x2, EXPANDED = 0x4
 +      };
 +};
 +
 +struct AllowUpwardDirectory
 +{
 +      enum ReturnCode
 +      {
 +              Never,
 +              No,
 +              ParentIsRegularPath,
 +              ParentIsTempPath
 +      };
 +};
 +
 +
 +struct DirViewFilterSettings
 +{
 +      template<class GetOptionBool>
 +      DirViewFilterSettings(GetOptionBool getoptbool)
 +      {
 +              show_skipped = getoptbool(OPT_SHOW_SKIPPED);
 +              show_unique_left = getoptbool(OPT_SHOW_UNIQUE_LEFT);
 +              show_unique_right = getoptbool(OPT_SHOW_UNIQUE_RIGHT);
 +              show_binaries = getoptbool(OPT_SHOW_BINARIES);
 +              show_identical = getoptbool(OPT_SHOW_IDENTICAL);
 +              show_different = getoptbool(OPT_SHOW_DIFFERENT);
 +              tree_mode = getoptbool(OPT_TREE_MODE);
 +      };
 +      bool show_skipped;
 +      bool show_unique_left;
 +      bool show_unique_right;
 +      bool show_binaries;
 +      bool show_identical;
 +      bool show_different;
 +      bool tree_mode;
 +};
 +
 +typedef std::map<String, bool> DirViewTreeState;
 +
 +String NumToStr(int n);
 +String FormatFilesAffectedString(int nFilesAffected, int nFilesTotal);
 +String FormatMenuItemString(SIDE_TYPE src, int count, int total);
 +String FormatMenuItemString(SIDE_TYPE src, SIDE_TYPE dst, int count, int total);
 +String FormatMenuItemStringBoth(int count, int total);
 +String FormatMenuItemString(const String& fmt1, const String& fmt2, int count, int total);
 +String FormatMenuItemStringTo(SIDE_TYPE src, int count, int total);
 +
 +void ConfirmActionList(const CDiffContext& ctxt, const FileActionScript & actionList);
 +UPDATEITEM_TYPE UpdateDiffAfterOperation(const FileActionItem & act, CDiffContext& ctxt, DIFFITEM &di);
 +
- bool GetOpenOneItem(const CDiffContext& ctxt, Poco::UIntPtr pos1, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
++uintptr_t FindItemFromPaths(const CDiffContext& ctxt, const String& pathLeft, const String& pathRight);
 +
 +bool IsItemCopyable(const DIFFITEM & di, int index);
 +bool IsItemDeletable(const DIFFITEM & di, int index);
 +bool IsItemDeletableOnBoth(const CDiffContext& ctxt, const DIFFITEM & di);
 +bool IsItemOpenable(const CDiffContext& ctxt, const DIFFITEM & di, bool treemode);
 +bool AreItemsOpenable(const CDiffContext& ctxt, SELECTIONTYPE selectionType, const DIFFITEM & di1, const DIFFITEM & di2);
 +bool AreItemsOpenable(const CDiffContext& ctxt, const DIFFITEM & di1, const DIFFITEM & di2, const DIFFITEM & di3);
 +bool IsItemOpenableOn(const DIFFITEM & di, int index);
 +bool IsItemOpenableOnWith(const DIFFITEM & di, int index);
 +bool IsItemCopyableToOn(const DIFFITEM & di, int index);
 +bool IsItemNavigableDiff(const CDiffContext& ctxt, const DIFFITEM & di);
 +bool IsItemExistAll(const CDiffContext& ctxt, const DIFFITEM & di);
 +bool IsShowable(const CDiffContext& ctxt, const DIFFITEM & di, const DirViewFilterSettings& filter);
 +
- bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, Poco::UIntPtr pos1, Poco::UIntPtr pos2, const DIFFITEM **di1, const DIFFITEM **di2,
++bool GetOpenOneItem(const CDiffContext& ctxt, uintptr_t pos1, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
 +              PathContext &paths, int & sel1, bool & isDir, String& errmsg);
- bool GetOpenThreeItems(const CDiffContext& ctxt, Poco::UIntPtr pos1, Poco::UIntPtr pos2, Poco::UIntPtr pos3, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
++bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, uintptr_t pos1, uintptr_t pos2, const DIFFITEM **di1, const DIFFITEM **di2,
 +              PathContext &paths, int & sel1, int & sel2, bool & isDir, String& errmsg);
++bool GetOpenThreeItems(const CDiffContext& ctxt, uintptr_t pos1, uintptr_t pos2, uintptr_t pos3, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
 +              PathContext &paths, int & sel1, int & sel2, int & sel3, bool & isDir, String& errmsg);
 +
 +void GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM& di, String& strLeft, String& strRight);
 +PathContext GetItemFileNames(const CDiffContext& ctxt, const DIFFITEM& di);
 +String GetItemFileName(const CDiffContext& ctx, const DIFFITEM & di, int index);
 +int GetColImage(const CDiffContext&ctxt, const DIFFITEM & di);
 +
 +void SetDiffStatus(DIFFITEM& di, unsigned  diffcode, unsigned mask);
 +void SetDiffCompare(DIFFITEM& di, unsigned diffcode);
 +void SetDiffSide(DIFFITEM& di, unsigned diffcode);
 +void SetDiffCounts(DIFFITEM& di, unsigned diffs, unsigned ignored);
 +void SetItemViewFlag(DIFFITEM& di, unsigned flag, unsigned mask);
 +void SetItemViewFlag(CDiffContext& ctxt, unsigned flag, unsigned mask);
 +void MarkForRescan(DIFFITEM& di);
 +
 +bool RenameOnSameDir(const String& szOldFileName, const String& szNewFileName);
 +
 +void ExpandSubdirs(CDiffContext& ctxt, DIFFITEM& dip);
 +void ExpandAllSubdirs(CDiffContext &ctxt);
 +void CollapseAllSubdirs(CDiffContext &ctxt);
 +DirViewTreeState *SaveTreeState(const CDiffContext& ctxt);
 +void RestoreTreeState(CDiffContext &ctxt, DirViewTreeState *pTreeState);
 +
 +AllowUpwardDirectory::ReturnCode
 +CheckAllowUpwardDirectory(const CDiffContext& ctxt, const CTempPathContext *pTempPathContext, PathContext &pathsParent);
 +
 +inline int SideToIndex(const CDiffContext& ctxt, SIDE_TYPE stype)
 +{
 +      switch (stype)
 +      {
 +      case SIDE_MIDDLE: return ctxt.GetCompareDirs() == 3 ? 1 : -1;
 +      case SIDE_RIGHT: return ctxt.GetCompareDirs() - 1;
 +      default: return 0;
 +      }
 +}
 +
 +struct ConfirmationNeededException
 +{
 +      String m_caption;
 +      String m_question;
 +      String m_fromText;
 +      String m_toText;
 +      String m_fromPath;
 +      String m_toPath;
 +};
 +
 +struct ContentsChangedException
 +{
 +      ContentsChangedException(const String& failpath);
 +      String m_msg;
 +};
 +
 +struct DirActions
 +{
 +      typedef bool (DirActions::*method_type2)(const DIFFITEM& di) const;
 +      typedef FileActionScript *(DirActions::*method_type)(FileActionScript *, const std::pair<int, const DIFFITEM *> it) const;
 +
 +      DirActions(const CDiffContext& ctxt, const bool RO[], method_type func = NULL, method_type2 func2 = NULL) : 
 +              m_ctxt(ctxt), m_RO(RO), m_cur_method(func), m_cur_method2(func2) {}
 +
 +      template <SIDE_TYPE src, SIDE_TYPE dst>
 +      bool IsItemCopyableOnTo(const DIFFITEM& di) const
 +      {
 +              return (di.diffcode.diffcode != 0 && !m_RO[SideToIndex(m_ctxt, dst)] && ::IsItemCopyable(di, SideToIndex(m_ctxt, src)));
 +      }
 +
 +      template <SIDE_TYPE src>
 +      bool IsItemCopyableToOn(const DIFFITEM& di) const
 +      {
 +              return (di.diffcode.diffcode != 0 && ::IsItemCopyableToOn(di, SideToIndex(m_ctxt, src)));
 +      }
 +
 +      template <SIDE_TYPE src>
 +      bool IsItemMovableToOn(const DIFFITEM& di) const
 +      {
 +              const int idx = SideToIndex(m_ctxt, src);
 +              return (di.diffcode.diffcode != 0 && !m_RO[idx] && IsItemDeletable(di, idx) && ::IsItemCopyableToOn(di, idx));
 +      }
 +
 +      template <SIDE_TYPE src>
 +      bool IsItemDeletableOn(const DIFFITEM& di) const
 +      { 
 +              const int idx = SideToIndex(m_ctxt, src);
 +              return (di.diffcode.diffcode != 0 && !m_RO[idx] && IsItemDeletable(di, idx));
 +      }
 +      bool IsItemDeletableOnBoth(const DIFFITEM& di) const
 +      {
 +              if (di.diffcode.diffcode != 0)
 +              {
 +                      int i;
 +                      for (i = 0; i < m_ctxt.GetCompareDirs(); ++i)
 +                      {
 +                              if (m_RO[i] || !IsItemDeletable(di, i))
 +                                      break;
 +                      }
 +                      return (i == m_ctxt.GetCompareDirs());
 +              }
 +              return false;
 +      }
 +      bool IsItemDeletableOnEitherOrBoth(const DIFFITEM& di) const
 +      {
 +              if (di.diffcode.diffcode != 0)
 +              {
 +                      int i;
 +                      for (i = 0; i < m_ctxt.GetCompareDirs(); ++i)
 +                      {
 +                              if (!m_RO[i] && IsItemDeletable(di, i))
 +                                      break;
 +                      }
 +                      return (i < m_ctxt.GetCompareDirs());
 +              }
 +              return false;
 +      }
 +
 +      template <SIDE_TYPE src>
 +      bool IsItemOpenanbleOn(const DIFFITEM& di) const
 +      {
 +              return (di.diffcode.diffcode != 0 && IsItemOpenableOn(di, SideToIndex(m_ctxt, src)));
 +      }
 +
 +      template <SIDE_TYPE src>
 +      bool IsItemOpenanbleOnWith(const DIFFITEM& di) const
 +      {
 +              return (di.diffcode.diffcode != 0 && IsItemOpenableOnWith(di, SideToIndex(m_ctxt, src)));
 +      }
 +
 +      bool IsItemFile(const DIFFITEM& di) const
 +      {
 +              return (di.diffcode.diffcode != 0 && di.diffcode.isDirectory());
 +      }
 +
 +      template <SIDE_TYPE src>
 +      bool IsItemExist(const DIFFITEM& di) const
 +      {
 +              return (di.diffcode.diffcode != 0 && di.diffcode.isExists(SideToIndex(m_ctxt, src)));
 +      }
 +
 +      template <SIDE_TYPE src>
 +      bool IsItemEditableEncoding(const DIFFITEM& di) const
 +      {
 +              const int index = SideToIndex(m_ctxt, src);
 +              return (di.diffcode.diffcode != 0 && di.diffcode.isExists(index) && di.diffFileInfo[index].IsEditableEncoding());
 +      }
 +
 +      bool IsItemNavigableDiff(const DIFFITEM& di) const
 +      {
 +              return ::IsItemNavigableDiff(m_ctxt, di);
 +      }
 +
 +      FileActionScript *CopyItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, SIDE_TYPE src, SIDE_TYPE dst) const
 +      {
 +              const DIFFITEM& di = *it.second;
 +              const int srcidx = SideToIndex(m_ctxt, src);
 +              const int dstidx = SideToIndex(m_ctxt, dst);
 +              if (di.diffcode.diffcode != 0 && !m_RO[dstidx] && IsItemCopyable(di, srcidx))
 +              {
 +                      FileActionItem act;
 +                      act.src  = GetItemFileName(m_ctxt, di, srcidx);
 +                      act.dest = GetItemFileName(m_ctxt, di, dstidx);
 +                      
 +                      // We must check that paths still exists
 +                      if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST)
 +                              throw ContentsChangedException(act.src);
 +
 +                      act.context = it.first;
 +                      act.dirflag = di.diffcode.isDirectory();
 +                      act.atype = FileAction::ACT_COPY;
 +                      act.UIResult = FileActionItem::UI_SYNC;
 +                      act.UIOrigin = srcidx;
 +                      act.UIDestination = dstidx;
 +                      pscript->AddActionItem(act);
 +              }
 +              return pscript;
 +      }
 +
 +      template<SIDE_TYPE src, SIDE_TYPE to>
 +      FileActionScript *Copy(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
 +      {
 +              return CopyItem(pscript, it, src, to);
 +      }
 +
 +      FileActionScript *DeleteItem(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, SIDE_TYPE src) const
 +      {
 +              const DIFFITEM& di = *it.second;
 +              const int index = SideToIndex(m_ctxt, src);
 +              if (di.diffcode.diffcode != 0 && !m_RO[index] && IsItemDeletable(di, index))
 +              {
 +                      FileActionItem act;
 +                      act.src = GetItemFileName(m_ctxt, di, index);
 +
 +                      // We must check that path still exists
 +                      if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST)
 +                              throw ContentsChangedException(act.src);
 +
 +                      act.context = it.first;
 +                      act.dirflag = di.diffcode.isDirectory();
 +                      act.atype = FileAction::ACT_DEL;
 +                      act.UIOrigin = index;
 +                      act.UIResult = FileActionItem::UI_DEL;
 +                      pscript->AddActionItem(act);
 +              }
 +              return pscript;
 +      }
 +
 +      template<SIDE_TYPE src>
 +      FileActionScript *DeleteOn(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
 +      {
 +              return DeleteItem(pscript, it, src);
 +      }
 +
 +      FileActionScript *DeleteOnBoth(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
 +      {
 +              const DIFFITEM& di = *it.second;
 +
 +              if (di.diffcode.diffcode != 0 && IsItemDeletableOnBoth(di) && 
 +                      (std::count(m_RO, m_RO + m_ctxt.GetCompareDirs(), true) == 0))
 +              {
 +                      for (int i = 0; i < m_ctxt.GetCompareDirs(); ++i)
 +                      {
 +                              FileActionItem act;
 +                              act.src = GetItemFileName(m_ctxt, di, i);
 +                              // We must first check that paths still exists
 +                              if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST)
 +                                      throw ContentsChangedException(act.src);
 +                              act.context = it.first;
 +                              act.dirflag = di.diffcode.isDirectory();
 +                              act.atype = FileAction::ACT_DEL;
 +                              act.UIOrigin = i;
 +                              act.UIResult = FileActionItem::UI_DEL;
 +                              pscript->AddActionItem(act);
 +                      }
 +              }
 +              return pscript;
 +      }
 +
 +      FileActionScript *DeleteOnEitherOrBoth(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
 +      {
 +              const DIFFITEM& di = *it.second;
 +              if (di.diffcode.diffcode != 0)
 +              {
 +                      for (int i = 0; i < m_ctxt.GetCompareDirs(); ++i)
 +                      {
 +                              if (IsItemDeletable(di, i) && !m_RO[i])
 +                              {
 +                                      FileActionItem act;
 +                                      act.src = GetItemFileName(m_ctxt, di, i);
 +                                      act.UIResult = FileActionItem::UI_DEL;
 +                                      act.dirflag = di.diffcode.isDirectory();
 +                                      act.context = it.first;
 +                                      act.UIOrigin = i;
 +                                      act.atype = FileAction::ACT_DEL;
 +                                      pscript->AddActionItem(act);
 +                              }
 +                      }
 +              }
 +              return pscript;
 +      }
 +
 +      FileActionScript *CopyOrMoveItemTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *>& it, FileAction::ACT_TYPE atype, SIDE_TYPE src) const
 +      {
 +              const int index = SideToIndex(m_ctxt, src);
 +              const DIFFITEM& di = *it.second;
 +
 +              if (di.diffcode.diffcode != 0 && IsItemCopyable(di, index) && 
 +                      (atype == FileAction::ACT_MOVE ? (!m_RO[index] && IsItemDeletable(di, index)) : true))
 +              {
 +                      FileActionItem act;
 +                      act.src = GetItemFileName(m_ctxt, di, index);
 +                       
 +                      // We must check that path still exists
 +                      if (paths_DoesPathExist(act.src) == DOES_NOT_EXIST)
 +                              throw ContentsChangedException(act.src);
 +
 +                      act.dest = paths_ConcatPath(pscript->m_destBase, di.diffFileInfo[index].filename);
 +                      act.dirflag = di.diffcode.isDirectory();
 +                      act.context = it.first;
 +                      act.atype = atype;
 +                      act.UIResult = (atype == FileAction::ACT_COPY) ? FileActionItem::UI_DONT_CARE : FileActionItem::UI_DEL;
 +                      act.UIOrigin = index;
 +                      pscript->AddActionItem(act);
 +              }
 +              return pscript;
 +      }
 +
 +      template<SIDE_TYPE src>
 +      FileActionScript *CopyTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
 +      {
 +              return CopyOrMoveItemTo(pscript, it, FileAction::ACT_COPY, src);
 +      }
 +
 +      template<SIDE_TYPE src>
 +      FileActionScript *MoveTo(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
 +      {
 +              return CopyOrMoveItemTo(pscript, it, FileAction::ACT_MOVE, src);
 +      }
 +
 +      FileActionScript *operator()(FileActionScript *pscript, const std::pair<int, const DIFFITEM *> it) const
 +      {
 +              return ((*this).*m_cur_method)(pscript, it);
 +      }
 +
 +      bool operator()(const DIFFITEM & di) const
 +      {
 +              return ((*this).*m_cur_method2)(di);
 +      }
 +
 +      method_type m_cur_method;
 +      method_type2 m_cur_method2;
 +      const CDiffContext& m_ctxt;
 +      const bool *m_RO;
 +};
 +
 +struct Counts {
 +      Counts() : count(0), total(0) {}
 +      Counts(int c, int t): count(c), total(t) {}
 +      int count;
 +      int total;
 +};
 +
 +template<class InputIterator, class Predicate>
 +Counts Count(const InputIterator& begin, const InputIterator& end, const Predicate& pred) 
 +{
 +      int count = 0, total = 0;
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              if (pred(*it))
 +                      ++count;
 +              ++total;
 +      }
 +      return Counts(count, total);
 +}
 +
 +struct ContextMenuCounts {
 +      int nTotal; // total #items (includes files & directories, either side)
 +      int nCopyable[3];
 +      int nDeletable[3];
 +      int nDeletableOnBoth;
 +      int nOpenable[3];
 +      int nOpenableOnBoth;
 +      int nOpenableOnWith[3];
 +      int nDiffItems;
 +};
 +
 +template<class InputIterator>
 +ContextMenuCounts CountForContextMenu(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt)
 +{
 +      ContextMenuCounts counts = {0};
 +      int nDirs = ctxt.GetCompareDirs();
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
 +                      continue;
 +              int nOpenablePerItem = 0;
 +              for (int j = 0; j < nDirs; ++j)
 +              {
 +                      if (IsItemCopyable(di, j))
 +                              ++counts.nCopyable[j];
 +                      if (IsItemDeletable(di, j))
 +                              ++counts.nDeletable[j];         
 +                      if (IsItemOpenableOn(di, j))
 +                      {
 +                              ++nOpenablePerItem;
 +                              ++counts.nOpenable[j];
 +                      }
 +                      if (IsItemOpenableOnWith(di, j))
 +                              ++counts.nOpenableOnWith[j];
 +              }
 +              if (IsItemDeletableOnBoth(ctxt, di))
 +                      ++counts.nDeletableOnBoth;
 +
 +              if (IsItemNavigableDiff(ctxt, di))
 +                      ++counts.nDiffItems;
 +
 +              if (nOpenablePerItem == nDirs)
 +                      ++counts.nOpenableOnBoth;
 +
 +              ++counts.nTotal;
 +      }
 +      return counts;
 +}
 +
 +
 +/**
 + * @brief Rename selected item on both left and right sides.
 + *
 + * @param szNewItemName [in] New item name.
 + *
 + * @return true if at least one file was renamed successfully.
 + */
 +template<class InputIterator>
 +bool DoItemRename(InputIterator& it, const CDiffContext& ctxt, const String& szNewItemName)
 +{
 +      PathContext paths;
 +      int nDirs = ctxt.GetCompareDirs();
 +
 +      assert(it != InputIterator());
 +
 +      // We must check that paths still exists
 +      String failpath;
 +      DIFFITEM &di = *it;
 +      paths = ::GetItemFileNames(ctxt, di);
 +      for (int i = 0; i < paths.GetSize(); ++i)
 +      {
 +              if (paths_DoesPathExist(paths[i]) == DOES_NOT_EXIST)
 +                      throw ContentsChangedException(failpath);
 +      }
 +
 +      bool bRename[3] = {false};
 +      int index;
 +      for (index = 0; index < nDirs; index++)
 +      {
 +              if (di.diffcode.isExists(index))
 +                      bRename[index] = RenameOnSameDir(paths[index], szNewItemName);
 +      }
 +
 +      int nSuccessCount = 0;
 +      for (index = 0; index < nDirs; index++)
 +              nSuccessCount += bRename[index] ? 1 : 0;
 +
 +      if (nSuccessCount > 0)
 +      {
 +              for (index = 0; index < nDirs; index++)
 +              {
 +                      if (bRename[index])
 +                              di.diffFileInfo[index].filename = szNewItemName;
 +                      else
 +                              di.diffFileInfo[index].filename = _T("");
 +              }
 +      }
 +
 +      return (bRename[0] || bRename[1] || (nDirs > 2 && bRename[2]));
 +}
 +
 +template<class InputIterator, class OutputIterator>
 +OutputIterator CopyPathnames(const InputIterator& begin, const InputIterator& end, OutputIterator result, SIDE_TYPE stype, const CDiffContext& ctxt)
 +{
 +      const int index = SideToIndex(ctxt, stype);
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              if (di.diffcode.isExists(index))
 +              {
 +                      *result = GetItemFileName(ctxt, di, index);
 +                      ++result;
 +              }
 +      }
 +      return result;
 +}
 +
 +template<class InputIterator, class OutputIterator>
 +OutputIterator CopyBothPathnames(const InputIterator& begin, const InputIterator& end, OutputIterator result, const CDiffContext& ctxt)
 +{
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
 +              {
 +                      if (di.diffcode.isExists(i))
 +                      {
 +                              *result = GetItemFileName(ctxt, di, i);
 +                              ++result;
 +                      }
 +              }
 +      }
 +      return result;
 +}
 +
 +template<class InputIterator, class OutputIterator>
 +OutputIterator CopyFilenames(const InputIterator& begin, const InputIterator& end, OutputIterator result)
 +{
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              if (!di.diffcode.isDirectory())
 +              {
 +                      *result = di.diffFileInfo[0].filename;
 +                      ++result;
 +              }
 +      }
 +      return result;
 +}
 +
 +template<class InputIterator, class OutputIterator>
 +OutputIterator CopyPathnamesForDragAndDrop(const InputIterator& begin, const InputIterator& end, OutputIterator result, const CDiffContext& ctxt)
 +{
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +
 +              // check for special items (e.g not "..")
 +              if (di.diffcode.diffcode == 0)
 +                      continue;
 +
 +              if (!IsItemExistAll(ctxt, di) || di.diffcode.isResultDiff())
 +              {
 +                      for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
 +                      {
 +                              if (di.diffcode.isExists(i))
 +                              {
 +                                      *result = GetItemFileName(ctxt, di, i);
 +                                      ++result;
 +                              }
 +                      }
 +              }
 +              else
 +              {
 +                      *result = GetItemFileName(ctxt, di, 0);
 +                      ++result;
 +              }
 +      }
 +      return result;
 +}
 +
 +template<class InputIterator, class BinaryFunction>
 +void ApplyFolderNameAndFileName(const InputIterator& begin, const InputIterator& end, SIDE_TYPE stype,
 +      const CDiffContext& ctxt, BinaryFunction func)
 +{
 +      int index = SideToIndex(ctxt, stype);
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
 +                      continue;
 +              String filename = di.diffFileInfo[index].filename;
 +              String currentDir = di.getFilepath(index, ctxt.GetNormalizedPath(index));
 +              func(currentDir, filename);
 +      }
 +}
 +
 +/**
 + * @brief Apply specified setting for prediffing to all selected items
 + */
 +template<class InputIterator>
 +void ApplyPluginPrediffSetting(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt, int newsetting)
 +{
 +      // Unlike other group actions, here we don't build an action list
 +      // to execute; we just apply this change directly
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              if (!di.diffcode.isDirectory())
 +              {
 +                      String filteredFilenames;
 +                      for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
 +                      {
 +                              if (di.diffcode.isExists(i))
 +                              {
 +                                      if (!filteredFilenames.empty()) filteredFilenames += _T("|");
 +                                      filteredFilenames += ::GetItemFileName(ctxt, di, i);
 +                              }
 +                      }
 +                      PackingInfo * infoUnpacker = 0;
 +                      PrediffingInfo * infoPrediffer = 0;
 +                      const_cast<CDiffContext&>(ctxt).FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
 +                      infoPrediffer->Initialize(newsetting);
 +              }
 +      }
 +}
 +
 +/**
 + * @brief Updates just before displaying plugin context view in list
 + */
 +template<class InputIterator>
 +std::pair<int, int> CountPredifferYesNo(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt)
 +{
 +      int nPredifferYes = 0;
 +      int nPredifferNo = 0;
 +
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
 +                      continue;
 +
 +              // note the prediffer flag for 'files present on both sides and not skipped'
 +              if (!di.diffcode.isDirectory() && !di.diffcode.isBin() && IsItemExistAll(ctxt, di)
 +                      && !di.diffcode.isResultFiltered())
 +              {
 +                      PathContext files = GetItemFileNames(ctxt, di);
 +                      String filteredFilenames = string_join(files.begin(), files.end(), _T("|"));
 +                      PackingInfo * unpacker;
 +                      PrediffingInfo * prediffer;
 +                      const_cast<CDiffContext&>(ctxt).FetchPluginInfos(filteredFilenames, &unpacker, &prediffer);
 +                      if (prediffer->bToBeScanned == 1 || prediffer->pluginName.empty() == false)
 +                              nPredifferYes ++;
 +                      else
 +                              nPredifferNo ++;
 +              }
 +      }
 +      return std::make_pair(nPredifferYes, nPredifferNo);
 +}
 +
 +template<class InputIterator>
 +IntToIntMap CountCodepages(const InputIterator& begin, const InputIterator& end, const CDiffContext& ctxt)
 +{
 +      IntToIntMap map;
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              const DIFFITEM& di = *it;
 +              for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
 +              {
 +                      if (di.diffcode.diffcode != 0 && di.diffcode.isExists(i))
 +                              map.Increment(di.diffFileInfo[i].encoding.m_codepage);
 +              }
 +      }
 +      return map;
 +}
 +
 +template<class InputIterator>
 +void ApplyCodepage(const InputIterator& begin, const InputIterator& end, CDiffContext& ctxt, const bool affect[3], int nCodepage)
 +{
 +      for (InputIterator it = begin; it != end; ++it)
 +      {
 +              DIFFITEM& di = *it;
 +              if (di.diffcode.diffcode == 0) // Invalid value, this must be special item
 +                      continue;
 +              if (di.diffcode.isDirectory())
 +                      continue;
 +
 +              for (int i = 0; i < ctxt.GetCompareDirs(); ++i)
 +              {
 +                      // Does it exist on left? (ie, right or both)
 +                      if (affect[i] && di.diffcode.isExists(i) && di.diffFileInfo[i].IsEditableEncoding())
 +                      {
 +                              di.diffFileInfo[i].encoding.SetCodepage(nCodepage);
 +                      }
 +              }
 +      }
 +}
 +
 +/// get file name on specified side for first selected item
 +template<class InputIterator>
 +String GetSelectedFileName(InputIterator& it, SIDE_TYPE stype, const CDiffContext& ctxt)
 +{
 +      if (it == InputIterator())
 +              return _T("");
 +      return GetItemFileName(ctxt, *it, SideToIndex(ctxt, stype));
 +}
diff --cc Src/DirDoc.cpp
@@@ -336,10 -514,10 +336,10 @@@ CDirView * CDirDoc::GetMainView() cons
   * calls slow DirView functions to get item position and to update GUI.
   * Use UpdateStatusFromDisk() function instead.
   */
- void CDirDoc::ReloadItemStatus(uintptr_t_t_t_t diffPos, int index)
 -void CDirDoc::ReloadItemStatus(uintptr_t diffPos, bool bLeft, bool bRight)
++void CDirDoc::ReloadItemStatus(uintptr_t diffPos, int index)
  {
        // in case just copied (into existence) or modified
 -      UpdateStatusFromDisk(diffPos, bLeft, bRight);
 +      m_pCtxt->UpdateStatusFromDisk(diffPos, index);
  
        int nIdx = m_pDirView->GetItemIndex(diffPos);
        if (nIdx != -1)
@@@ -473,10 -702,10 +473,10 @@@ BOOL CDirDoc::ReusingDirDoc(
  void CDirDoc::UpdateChangedItem(PathContext &paths,
        UINT nDiffs, UINT nTrivialDiffs, BOOL bIdentical)
  {
-       uintptr_t_t_t pos = FindItemFromPaths(*m_pCtxt, paths.GetLeft(), paths.GetRight());
 -      uintptr_t pos = FindItemFromPaths(paths.GetLeft(), paths.GetRight());
++      uintptr_t pos = FindItemFromPaths(*m_pCtxt, paths.GetLeft(), paths.GetRight());
        // If we failed files could have been swapped so lets try again
        if (!pos)
 -              pos = FindItemFromPaths(paths.GetRight(), paths.GetLeft());
 +              pos = FindItemFromPaths(*m_pCtxt, paths.GetRight(), paths.GetLeft());
        
        // Update status if paths were found for items.
        // Fail means we had unique items compared as 'renamed' items
index 8074f06,0000000..9467ee0
mode 100644,000000..100644
--- /dev/null
@@@ -1,164 -1,0 +1,164 @@@
-                       if (m_pList->GetItemData(m_sel) == reinterpret_cast<void *>((Poco::UIntPtr)-1L))
 +/**
 + *  @file DirItemIterator.h
 + *
 + *  @brief Declaration DirItemIterator classes.
 + */ 
 +// ID line follows -- this is updated by SVN
 +// $Id$
 +
 +#pragma once
 +
 +#include <iterator>
 +#include <utility>
 +#include "IListCtrl.h"
 +
 +struct DIFFITEM;
 +
 +class DirItemWithIndexIterator : public std::iterator<std::forward_iterator_tag, std::pair<int, DIFFITEM *> >
 +{
 +public:
 +      DirItemWithIndexIterator(IListCtrl *pList, int sel = -1, bool selected = false, bool reverse = false) :
 +        m_pList(pList), m_sel(sel), m_selected(selected), m_reverse(reverse)
 +      {
 +              if (m_sel == -1)
 +              {
 +                      if (m_reverse)
 +                      {
 +                              int last = m_pList->GetRowCount() - 1;
 +                              if (!m_selected || m_pList->IsSelectedItem(last))
 +                                      m_sel = last;
 +                              else
 +                                      m_sel = m_pList->GetNextItem(last, m_selected, m_reverse);
 +                      }
 +                      else
 +                              m_sel = m_pList->GetNextItem(-1, m_selected, m_reverse);
 +              }
 +              if (m_sel != -1)
 +              {
++                      if (m_pList->GetItemData(m_sel) == reinterpret_cast<void *>((uintptr_t)-1L))
 +                              m_sel = m_pList->GetNextItem(m_sel, m_selected, m_reverse);
 +              }
 +      }
 +
 +      DirItemWithIndexIterator() : m_pList(NULL), m_sel(-1)
 +      {
 +      }
 +
 +      ~DirItemWithIndexIterator() {}
 +
 +      DirItemWithIndexIterator& operator=(const DirItemWithIndexIterator& it)
 +      {
 +              m_sel = it.m_sel;
 +              m_pList = it.m_pList;
 +              return *this;
 +      }
 +
 +      DirItemWithIndexIterator& operator++()
 +      {
 +              m_sel = m_pList->GetNextItem(m_sel, m_selected, m_reverse);
 +              return *this;
 +      }
 +
 +      std::pair<int, DIFFITEM *> operator*()
 +      {
 +              return std::make_pair(m_sel, reinterpret_cast<DIFFITEM *>(m_pList->GetItemData(m_sel)));
 +      }
 +
 +      bool operator==(const DirItemWithIndexIterator& it) const
 +      {
 +              return m_sel == it.m_sel;
 +      }
 +
 +      bool operator!=(const DirItemWithIndexIterator& it) const
 +      {
 +              return m_sel != it.m_sel;
 +      }
 +
 +      bool m_selected;
 +      bool m_reverse;
 +      int m_sel;
 +
 +private:
 +      IListCtrl *m_pList;
 +};
 +
 +class DirItemIterator : public std::iterator<std::forward_iterator_tag, DIFFITEM*>
 +{
 +public:
 +      DirItemIterator(IListCtrl *pList, int sel = -1, bool selected = false, bool reverse = false) : 
 +        m_pList(pList), m_sel(sel), m_selected(selected), m_reverse(reverse), m_pdi(NULL)
 +      {
 +              if (m_sel == -1)
 +              {
 +                      if (m_reverse)
 +                      {
 +                              int last = m_pList->GetRowCount() - 1;
 +                              if (!m_selected || m_pList->IsSelectedItem(last))
 +                                      m_sel = last;
 +                              else
 +                                      m_sel = m_pList->GetNextItem(last, m_selected, m_reverse);
 +                      }
 +                      else
 +                              m_sel = m_pList->GetNextItem(-1, m_selected, m_reverse);
 +              }
 +              if (m_sel != -1)
 +              {
 +                      m_pdi = reinterpret_cast<const DIFFITEM *>(m_pList->GetItemData(m_sel));
 +                      if (m_pdi == reinterpret_cast<const DIFFITEM *>(-1L))
 +                      {
 +                              m_sel = m_pList->GetNextItem(m_sel, m_selected, m_reverse);
 +                              m_pdi = reinterpret_cast<const DIFFITEM *>(m_pList->GetItemData(m_sel));
 +                      }
 +              }
 +      }
 +
 +      DirItemIterator() : m_pList(NULL), m_sel(-1), m_reverse(false)
 +      {
 +      }
 +
 +      ~DirItemIterator() {}
 +
 +      DirItemIterator& operator=(const DirItemIterator& it)
 +      {
 +              m_sel = it.m_sel;
 +              m_pList = it.m_pList;
 +              return *this;
 +      }
 +
 +      DirItemIterator& operator++()
 +      {
 +              m_sel = m_pList->GetNextItem(m_sel, m_selected, m_reverse);
 +              m_pdi = reinterpret_cast<const DIFFITEM *>(m_pList->GetItemData(m_sel));
 +              if (m_pdi == reinterpret_cast<const DIFFITEM *>(-1L))
 +                      m_sel = -1;
 +              return *this;
 +      }
 +
 +      DIFFITEM& operator*()
 +      {
 +              return *const_cast<DIFFITEM *>(m_pdi);
 +      }
 +
 +      const DIFFITEM& operator*() const
 +      {
 +              return *m_pdi;
 +      }
 +
 +      bool operator==(const DirItemIterator& it) const
 +      {
 +              return m_sel == it.m_sel;
 +      }
 +
 +      bool operator!=(const DirItemIterator& it) const
 +      {
 +              return m_sel != it.m_sel;
 +      }
 +
 +      bool m_selected;
 +      bool m_reverse;
 +      int m_sel;
 +      const DIFFITEM *m_pdi;
 +
 +private:
 +      IListCtrl *m_pList;
 +};
Simple merge
diff --cc Src/MainFrm.cpp
Simple merge
diff --cc Src/Merge.cpp
Simple merge
diff --cc Src/Merge.h
Simple merge
        <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>\r
        <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UnicodeDebug|x64'">NotUsing</PrecompiledHeader>\r
      </ClCompile>\r
-     <ClCompile Include="Splash.cpp">\r
-     </ClCompile>\r
 +    <ClCompile Include="Common\SplitterWndEx.cpp">\r
 +    </ClCompile>\r
      <ClCompile Include="Common\StatLink.cpp">\r
      </ClCompile>\r
      <ClCompile Include="StdAfx.cpp">\r
      <ClInclude Include="Common\coretools.h" />\r
      <ClInclude Include="Common\coretypes.h" />\r
      <ClInclude Include="Common\CSubclass.h" />\r
++    <ClInclude Include="DirActions.h" />\r
      <ClInclude Include="IMergeDoc.h" />\r
      <ClInclude Include="ImgMergeFrm.h" />\r
      <ClInclude Include="Merge7zFormatMergePluginImpl.h" />\r
      <ClInclude Include="Merge7zFormatRegister.h">\r
        <Filter>Header Files</Filter>\r
      </ClInclude>\r
 +    <ClInclude Include="DirViewColItems.h">\r
 +      <Filter>Header Files</Filter>\r
 +    </ClInclude>\r
++    <ClInclude Include="DirActions.h">\r
++      <Filter>Header Files</Filter>\r
++    </ClInclude>\r
    </ItemGroup>\r
    <ItemGroup>\r
      <None Include="res\binarydiff.ico">\r
@@@ -70,101 -66,35 +70,115 @@@ String LoadResString(unsigned id
        return theApp.LoadString(id);
  }
  
 -/**
 - * @brief Lang aware version of AfxFormatStrings()
 - */
 -String LangFormatStrings(unsigned id, const TCHAR * const *rglpsz, int nString)
 +String tr(const std::string &str)
  {
 -      String fmt = theApp.LoadString(id);
 -      CString str;
 -      AfxFormatStrings(str, fmt.c_str(), rglpsz, nString);
 -      return (LPCTSTR)str;
 +      String translated_str;
 +      theApp.TranslateString(str, translated_str);
 +      return translated_str;
  }
  
 -/**
 - * @brief Lang aware version of AfxFormatString1()
 - */
 -String LangFormatString1(unsigned id, const TCHAR *lpsz1)
 +void AppErrorMessageBox(const String& msg)
  {
 -      return LangFormatStrings(id, &lpsz1, 1);
 +      AppMsgBox::error(msg);
  }
  
 -/**
 - * @brief Lang aware version of AfxFormatString2()
 - */
 -String LangFormatString2(unsigned id, const TCHAR *lpsz1, const TCHAR *lpsz2)
 +namespace AppMsgBox
 +{
 +
 +namespace detail
 +{
 +      int convert_to_winflags(int flags)
 +      {
 +              int newflags = 0;
 +
 +              if ((flags & (YES | NO | CANCEL)) == (YES | NO | CANCEL)) newflags |= MB_YESNOCANCEL;
 +              else if ((flags & (YES | NO)) == (YES | NO)) newflags |= MB_YESNO;
 +              else if ((flags & (OK | CANCEL)) == (OK | CANCEL)) newflags |= MB_OKCANCEL;
 +              else if ((flags & OK) == OK) newflags |= MB_OK;
 +      
 +              if (flags & YES_TO_ALL) newflags |= MB_YES_TO_ALL;
 +              if (flags & DONT_DISPLAY_AGAIN) newflags |= MB_DONT_DISPLAY_AGAIN;
 +
 +              return newflags;
 +      }
 +
 +      int convert_resp(int resp)
 +      {
 +              switch (resp)
 +              {
 +              case IDOK:
 +                      return OK;
 +              case IDCANCEL:
 +                      return CANCEL;
 +              case IDNO:
 +                      return NO;
 +              case IDYES:
 +                      return YES;
 +              case IDYESTOALL:
 +                      return YES_TO_ALL;
 +              default:
 +                      return OK;
 +              }
 +      }
 +}
 +
 +int error(const String& msg, int type)
  {
 -      const TCHAR *rglpsz[2] = { lpsz1, lpsz2 };
 -      return LangFormatStrings(id, rglpsz, 2);
 +      return detail::convert_resp(AfxMessageBox(msg.c_str(), detail::convert_to_winflags(type) | MB_ICONSTOP));
  }
  
 -void AppErrorMessageBox(const String& msg)
 +int warning(const String& msg, int type)
  {
 -      AfxMessageBox(msg.c_str(), MB_ICONSTOP);
 +      return detail::convert_resp(AfxMessageBox(msg.c_str(), detail::convert_to_winflags(type) | MB_ICONWARNING));
 +}
 +
 +int information(const String& msg, int type)
 +{
 +      return detail::convert_resp(AfxMessageBox(msg.c_str(), detail::convert_to_winflags(type) | MB_ICONINFORMATION));
 +}
 +
 +}
 +
 +AboutInfo::AboutInfo()
 +{
 +      CVersionInfo verinfo;
 +      version = string_format_string1(_("Version %1"), verinfo.GetProductVersion());
++      if (version.find(_T(" - ")) != String::npos)
++      {
++              string_replace(version, _T(" - "), _T("\n"));
++              version += _T(" ");
++      }
++      else
++      {
++              version += _T("\n");
++      }
 +#ifdef _UNICODE
 +      version += _T(" ");
 +      version += _("Unicode");
 +#endif
 +
 +#if defined _M_IX86
 +      version += _T(" x86");
 +#elif defined _M_IA64
 +      version += _T(" IA64");
 +#elif defined _M_X64
 +      version += _T(" ");
 +      version += _("X64");
 +#endif
 +
-       copyright = verinfo.GetLegalCopyright();
++      copyright = _("WinMerge comes with ABSOLUTELY NO WARRANTY. This is free software and you are welcome to redistribute it under certain circumstances; see the GNU General Public License in the Help menu for details.");\r
++      copyright += _T("\n");\r
++      copyright += verinfo.GetLegalCopyright();
++      copyright += _T(" All rights reserved.");
 +
 +      private_build = verinfo.GetPrivateBuild();
 +      if (!private_build.empty())
 +      {
 +              private_build = string_format_string1(_("Private Build: %1"), private_build);
 +      }
 +
 +      website = WinMergeURL;
++
++      developers = _("Developers:\nDean Grimm, Christian List, Kimmo Varis, Jochen Tucht, Tim Gerundt, Takashi Sawanaki, Gal Hammer, Alexander Skinner");
++      string_replace(developers, _T(", "), _T("\n"));
  }
diff --cc Src/MergeApp.h
@@@ -9,15 -6,6 +9,16 @@@
  class COptionsMgr;
  class FileFilterHelper;
  
 +struct AboutInfo
 +{
 +      AboutInfo();
 +      String copyright;
 +      String version;
++      String developers;
 +      String private_build;
 +      String website;
 +};
 +
  /** @brief Retrieve error description from Windows; uses FormatMessage */
  String GetSysError(int nerr = -1);
  
Simple merge
Simple merge
@@@ -46,18 -45,17 +46,17 @@@ static char THIS_FILE[] = __FILE__
   */
  PropGeneral::PropGeneral(COptionsMgr *optionsMgr) 
  : OptionsPanel(optionsMgr, PropGeneral::IDD)
 -, m_bScroll(FALSE)
 -, m_bSingleInstance(FALSE)
 -, m_bVerifyPaths(FALSE)
 +, m_bScroll(false)
- , m_bDisableSplash(false)
 +, m_bSingleInstance(false)
 +, m_bVerifyPaths(false)
  , m_bCloseWindowWithEsc(TRUE)
 -, m_bAskMultiWindowClose(FALSE)
 -, m_bMultipleFileCmp(FALSE)
 -, m_bMultipleDirCmp(FALSE)
 +, m_bAskMultiWindowClose(false)
 +, m_bMultipleFileCmp(false)
 +, m_bMultipleDirCmp(false)
  , m_nAutoCompleteSource(0)
 -, m_bPreserveFiletime(FALSE)
 -, m_bShowSelectFolderOnStartup(FALSE)
 -, m_bCloseWithOK(TRUE)
 +, m_bPreserveFiletime(false)
 +, m_bShowSelectFolderOnStartup(false)
 +, m_bCloseWithOK(true)
  {
  }
  
@@@ -132,14 -128,13 +129,13 @@@ void PropGeneral::ReadOptions(
   */
  void PropGeneral::WriteOptions()
  {
 -      GetOptionsMgr()->SaveOption(OPT_SCROLL_TO_FIRST, m_bScroll == TRUE);
 -      GetOptionsMgr()->SaveOption(OPT_SINGLE_INSTANCE, m_bSingleInstance == TRUE);
 -      GetOptionsMgr()->SaveOption(OPT_VERIFY_OPEN_PATHS, m_bVerifyPaths == TRUE);
 -      GetOptionsMgr()->SaveOption(OPT_CLOSE_WITH_ESC, m_bCloseWindowWithEsc == TRUE);
 -      GetOptionsMgr()->SaveOption(OPT_ASK_MULTIWINDOW_CLOSE, m_bAskMultiWindowClose == TRUE);
 -      GetOptionsMgr()->SaveOption(OPT_MULTIDOC_MERGEDOCS, m_bMultipleFileCmp == TRUE);
 -      GetOptionsMgr()->SaveOption(OPT_MULTIDOC_DIRDOCS, m_bMultipleDirCmp == TRUE);
 +      GetOptionsMgr()->SaveOption(OPT_SCROLL_TO_FIRST, m_bScroll);
-       GetOptionsMgr()->SaveOption(OPT_DISABLE_SPLASH, m_bDisableSplash);
 +      GetOptionsMgr()->SaveOption(OPT_SINGLE_INSTANCE, m_bSingleInstance);
 +      GetOptionsMgr()->SaveOption(OPT_VERIFY_OPEN_PATHS, m_bVerifyPaths);
 +      GetOptionsMgr()->SaveOption(OPT_CLOSE_WITH_ESC, m_bCloseWindowWithEsc);
 +      GetOptionsMgr()->SaveOption(OPT_ASK_MULTIWINDOW_CLOSE, m_bAskMultiWindowClose);
 +      GetOptionsMgr()->SaveOption(OPT_MULTIDOC_MERGEDOCS, m_bMultipleFileCmp);
 +      GetOptionsMgr()->SaveOption(OPT_MULTIDOC_DIRDOCS, m_bMultipleDirCmp);
        GetOptionsMgr()->SaveOption(OPT_AUTO_COMPLETE_SOURCE, m_nAutoCompleteSource);
        GetOptionsMgr()->SaveOption(OPT_PRESERVE_FILETIMES, m_bPreserveFiletime);
        GetOptionsMgr()->SaveOption(OPT_SHOW_SELECT_FILES_AT_STARTUP, m_bShowSelectFolderOnStartup);
@@@ -30,18 -30,17 +30,17 @@@ public
  // Dialog Data
        //{{AFX_DATA(PropGeneral)
        enum { IDD = IDD_PROPPAGE_GENERAL };
 -      BOOL  m_bScroll;
 -      BOOL  m_bSingleInstance;
 -      BOOL  m_bVerifyPaths;
 -      BOOL  m_bCloseWindowWithEsc;
 -      BOOL  m_bAskMultiWindowClose;
 -      BOOL    m_bMultipleFileCmp;
 -      BOOL    m_bMultipleDirCmp;
 -      int             m_nAutoCompleteSource;
 -      BOOL    m_bPreserveFiletime;
 -      BOOL    m_bShowSelectFolderOnStartup;
 -      BOOL    m_bCloseWithOK;
 +      bool  m_bScroll;
-       bool  m_bDisableSplash;
 +      bool  m_bSingleInstance;
 +      bool  m_bVerifyPaths;
 +      bool  m_bCloseWindowWithEsc;
 +      bool  m_bAskMultiWindowClose;
 +      bool  m_bMultipleFileCmp;
 +      bool  m_bMultipleDirCmp;
 +      int   m_nAutoCompleteSource;
 +      bool  m_bPreserveFiletime;
 +      bool  m_bShowSelectFolderOnStartup;
 +      bool  m_bCloseWithOK;
        //}}AFX_DATA
  
  
@@@ -8,7 -8,7 +8,7 @@@ msgid "
  msgstr ""
  "Project-Id-Version: WinMerge\n"
  "Report-Msgid-Bugs-To: http://bugs.winmerge.org/\n"
- "POT-Creation-Date: 2014-12-18 22:44+0000\n"
 -"POT-Creation-Date: 2014-12-29 17:09+0000\n"
++"POT-Creation-Date: 2014-12-29 23:28+0000\n"
  "PO-Revision-Date: \n"
  "Last-Translator: \n"
  "Language-Team: English <winmerge-translate@lists.sourceforge.net>\n"