OSDN Git Service

Merge with stable
authorsdottaka <sdottaka@users.sourceforge.net>
Thu, 22 Jan 2015 06:50:31 +0000 (15:50 +0900)
committersdottaka <sdottaka@users.sourceforge.net>
Thu, 22 Jan 2015 06:50:31 +0000 (15:50 +0900)
1  2 
Src/DirActions.cpp
Src/DirActions.h
Src/DirView.cpp

@@@ -543,373 -1356,69 +543,373 @@@ 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() && FALSE/* !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() && FALSE/* !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_t pos1, const DIFFITEM **di1, const DIFFITEM **di2, const DIFFITEM **di3,
-               PathContext & paths, int & sel1, bool & isdir, String& errmsg)
++bool GetOpenOneItem(const CDiffContext& ctxt, uintptr_t pos1, const DIFFITEM *pdi[3],
++              PathContext & paths, int & sel1, bool & isdir, int nPane[3], String& errmsg)
  {
-       *di1 = &ctxt.GetDiffAt(pos1);
-       *di2 = *di1;
-       *di3 = *di1;
 -      String left, right;
 -      if (!GetSelectedFileNames(left, right)) return _T("");
 -      return stype==SIDE_LEFT ? left : right;
++      pdi[0] = &ctxt.GetDiffAt(pos1);
++      pdi[1] = pdi[0];
++      pdi[2] = pdi[0];
 +
-       paths = GetItemFileNames(ctxt, **di1);
++      paths = GetItemFileNames(ctxt, *pdi[0]);
 +
-       if ((*di1)->diffcode.isDirectory())
++      for (int nIndex = 0; nIndex < paths.GetSize(); ++nIndex)
++              nPane[nIndex] = nIndex;
++
++      if (pdi[0]->diffcode.isDirectory())
 +              isdir = true;
 +
-       if (isdir && ((*di1)->diffcode.isExistsFirst() && (*di1)->diffcode.isExistsSecond() && (*di1)->diffcode.isExistsThird()))
++      if (isdir && (pdi[0]->diffcode.isExistsFirst() && pdi[1]->diffcode.isExistsSecond() && pdi[2]->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_t pos1, uintptr_t pos2, const DIFFITEM **di1, const DIFFITEM **di2,
-               PathContext & paths, int & sel1, int & sel2, bool & isDir, String& errmsg)
 -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 *pdi[3],
++              PathContext & paths, int & sel1, int & sel2, bool & isDir, int nPane[3], 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);
++      pdi[0] = &ctxt.GetDiffAt(pos1);
++      pdi[1] = &ctxt.GetDiffAt(pos2);
++      nPane[0] = 0;
++      nPane[1] = 1;
 +
 +      // Check for binary & side compatibility & file/dir compatibility
-       if (!AreItemsOpenable(ctxt, selectionType, **di1, **di2))
++      if (!AreItemsOpenable(ctxt, selectionType, *pdi[0], *pdi[1]))
        {
 -              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()))
++              if (pdi[0]->diffcode.isSideSecondOnly() || (pdi[0]->diffcode.isSideBoth() &&
++                              pdi[1]->diffcode.isSideFirstOnly()))
 +              {
-                       const DIFFITEM * temp = *di1;
-                       *di1 = *di2;
-                       *di2 = temp;
-                       int num = sel1;
-                       sel1 = sel2;
-                       sel2 = num;
++                      std::swap(pdi[0], pdi[1]);
++                      std::swap(sel1, sel2);
 +              }
-               // 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);
++              nPane[0] = nPane[1] = 0;
 +              break;
 +      case SELECTIONTYPE_RIGHT1RIGHT2:
-               GetItemFileNames(ctxt, **di1, temp, pathLeft);
-               GetItemFileNames(ctxt, **di2, temp, pathRight);
++              nPane[0] = nPane[1] = 1;
 +              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);
++              std::swap(pdi[0], pdi[1]);
++              std::swap(sel1, sel2);
 +              break;
 +      }
 +
-       if ((*di1)->diffcode.isDirectory())
++      if (pdi[0]->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);
 +
++      PathContext files1, files2;
++      files1 = GetItemFileNames(ctxt, *pdi[0]);
++      files2 = GetItemFileNames(ctxt, *pdi[1]);
++      paths.SetLeft(files1[nPane[0]]);
++      paths.SetRight(files2[nPane[1]]);
 +
 +      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_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 CDirView::GetItemFileNames(int sel, PathContext * paths) const
++bool GetOpenThreeItems(const CDiffContext& ctxt, uintptr_t pos1, uintptr_t pos2, uintptr_t pos3, const DIFFITEM *pdi[3],
++      PathContext & paths, int & sel1, int & sel2, int & sel3, bool & isDir, int nPane[3], String& errmsg)
  {
 -      String strPath[3];
 -      UINT_PTR diffpos = GetItemKey(sel);
 -      if (diffpos == SPECIAL_ITEM_POS)
 +      String pathLeft, pathMiddle, pathRight;
 +
++      // FIXME:
++      for (int nIndex = 0; nIndex < 3; ++nIndex)
++              nPane[nIndex] = nIndex;
 +      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);
++              pdi[0] = &ctxt.GetDiffAt(pos1);
++              pdi[1] = &ctxt.GetDiffAt(pos2);
 +
 +              // Check for binary & side compatibility & file/dir compatibility
-               if (!::AreItemsOpenable(ctxt, **di1, **di2, **di2) && 
-                       !::AreItemsOpenable(ctxt, **di1, **di1, **di2))
++              if (!::AreItemsOpenable(ctxt, *pdi[0], *pdi[1], *pdi[1]) && 
++                      !::AreItemsOpenable(ctxt, *pdi[0], *pdi[0], *pdi[1]))
 +              {
 +                      return false;
 +              }
-               // Ensure that di1 is on left (swap if needed)
-               if ((*di1)->diffcode.isExists(0) && (*di1)->diffcode.isExists(1) && (*di2)->diffcode.isExists(2))
++              // Ensure that pdi[0] is on left (swap if needed)
++              if (pdi[0]->diffcode.isExists(0) && pdi[0]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(2))
 +              {
-                       *di3 = *di2;
-                       *di2 = *di1;
++                      pdi[2] = pdi[1];
++                      pdi[1] = pdi[0];
 +                      sel3 = sel2;
 +                      sel2 = sel1;
 +              }
-               else if ((*di1)->diffcode.isExists(0) && (*di1)->diffcode.isExists(2) && (*di2)->diffcode.isExists(1))
++              else if (pdi[0]->diffcode.isExists(0) && pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(1))
 +              {
-                       *di3 = *di1;
++                      pdi[2] = pdi[0];
 +                      sel3 = sel1;
 +              }
-               else if ((*di1)->diffcode.isExists(1) && (*di1)->diffcode.isExists(2) && (*di2)->diffcode.isExists(0))
++              else if (pdi[0]->diffcode.isExists(1) && pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(0))
 +              {
-                       std::swap(*di1, *di2);
++                      std::swap(pdi[0], pdi[1]);
 +                      std::swap(sel1, sel2);
-                       *di3 = *di2;
++                      pdi[2] = pdi[1];
 +                      sel3 = sel2;
 +              }
-               else if ((*di2)->diffcode.isExists(0) && (*di2)->diffcode.isExists(1) && (*di1)->diffcode.isExists(2))
++              else if (pdi[1]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(1) && pdi[0]->diffcode.isExists(2))
 +              {
-                       std::swap(*di1, *di2);
++                      std::swap(pdi[0], pdi[1]);
 +                      std::swap(sel1, sel2);
-                       *di3 = *di2;
-                       *di2 = *di1;
++                      pdi[2] = pdi[1];
++                      pdi[1] = pdi[0];
 +                      sel3 = sel2;
 +                      sel2 = sel1;
 +              }
-               else if ((*di2)->diffcode.isExists(0) && (*di2)->diffcode.isExists(2) && (*di1)->diffcode.isExists(1))
++              else if (pdi[1]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(2) && pdi[0]->diffcode.isExists(1))
 +              {
-                       std::swap(*di1, *di2);
++                      std::swap(pdi[0], pdi[1]);
 +                      std::swap(sel1, sel2);
-                       *di3 = *di1;
++                      pdi[2] = pdi[0];
 +                      sel3 = sel1;
 +              }
-               else if ((*di2)->diffcode.isExists(1) && (*di2)->diffcode.isExists(2) && (*di1)->diffcode.isExists(0))
++              else if (pdi[1]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(2) && pdi[0]->diffcode.isExists(0))
 +              {
-                       *di3 = *di2;
++                      pdi[2] = pdi[1];
 +                      sel3 = sel2;
 +              }
        }
        else
        {
 -              const DIFFITEM & di = GetDocument()->GetDiffByKey(diffpos);
 -              for (int nIndex = 0; nIndex < GetDocument()->m_nDirs; nIndex++)
 +              // Three items selected, get their info
-               *di1 = &ctxt.GetDiffAt(pos1);
-               *di2 = &ctxt.GetDiffAt(pos2);
-               *di3 = &ctxt.GetDiffAt(pos3);
++              pdi[0] = &ctxt.GetDiffAt(pos1);
++              pdi[1] = &ctxt.GetDiffAt(pos2);
++              pdi[2] = &ctxt.GetDiffAt(pos3);
 +
 +              // Check for binary & side compatibility & file/dir compatibility
-               if (!::AreItemsOpenable(ctxt, **di1, **di2, **di3))
++              if (!::AreItemsOpenable(ctxt, *pdi[0], *pdi[1], *pdi[2]))
 +              {
 +                      return false;
 +              }
-               // Ensure that di1 is on left (swap if needed)
-               if ((*di1)->diffcode.isExists(0) && (*di2)->diffcode.isExists(1) && (*di3)->diffcode.isExists(2))
++              // Ensure that pdi[0] is on left (swap if needed)
++              if (pdi[0]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(1) && pdi[2]->diffcode.isExists(2))
 +              {
 +              }
-               else if ((*di1)->diffcode.isExists(0) && (*di2)->diffcode.isExists(2) && (*di3)->diffcode.isExists(1))
++              else if (pdi[0]->diffcode.isExists(0) && pdi[1]->diffcode.isExists(2) && pdi[2]->diffcode.isExists(1))
                {
-                       std::swap(*di2, *di3);
 -                      const String relpath = di.diffFileInfo[nIndex].GetFile();
 -                      const String & path = GetDocument()->GetBasePath(nIndex);
 -                      paths->SetPath(nIndex, paths_ConcatPath(path, relpath));
++                      std::swap(pdi[1], pdi[2]);
 +                      std::swap(sel2, sel3);
                }
-               else if ((*di1)->diffcode.isExists(1) && (*di2)->diffcode.isExists(0) && (*di3)->diffcode.isExists(2))
++              else if (pdi[0]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(0) && pdi[2]->diffcode.isExists(2))
 +              {
-                       std::swap(*di1, *di2);
++                      std::swap(pdi[0], pdi[1]);
 +                      std::swap(sel1, sel2);
 +              }
-               else if ((*di1)->diffcode.isExists(1) && (*di2)->diffcode.isExists(2) && (*di3)->diffcode.isExists(0))
++              else if (pdi[0]->diffcode.isExists(1) && pdi[1]->diffcode.isExists(2) && pdi[2]->diffcode.isExists(0))
 +              {
-                       std::swap(*di1, *di3);
++                      std::swap(pdi[0], pdi[2]);
 +                      std::swap(sel1, sel3);
-                       std::swap(*di2, *di3);
++                      std::swap(pdi[1], pdi[2]);
 +                      std::swap(sel2, sel3);
 +              }
-               else if ((*di1)->diffcode.isExists(2) && (*di2)->diffcode.isExists(0) && (*di3)->diffcode.isExists(1))
++              else if (pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(0) && pdi[2]->diffcode.isExists(1))
 +              {
-                       std::swap(*di1, *di2);
++                      std::swap(pdi[0], pdi[1]);
 +                      std::swap(sel1, sel2);
-                       std::swap(*di2, *di3);
++                      std::swap(pdi[1], pdi[2]);
 +                      std::swap(sel2, sel3);
 +              }
-               else if ((*di1)->diffcode.isExists(2) && (*di2)->diffcode.isExists(1) && (*di3)->diffcode.isExists(0))
++              else if (pdi[0]->diffcode.isExists(2) && pdi[1]->diffcode.isExists(1) && pdi[2]->diffcode.isExists(0))
 +              {
-                       std::swap(*di1, *di3);
++                      std::swap(pdi[0], pdi[2]);
 +                      std::swap(sel1, sel3);
 +              }
 +      }
 +
 +      // Fill in pathLeft & & pathMiddle & pathRight
-       PathContext pathsTemp = GetItemFileNames(ctxt, **di1);
++      PathContext pathsTemp = GetItemFileNames(ctxt, *pdi[0]);
 +      pathLeft = pathsTemp[0];
-       pathsTemp = GetItemFileNames(ctxt, **di2);
++      pathsTemp = GetItemFileNames(ctxt, *pdi[1]);
 +      pathMiddle = pathsTemp[1];
-       pathsTemp = GetItemFileNames(ctxt, **di3);
++      pathsTemp = GetItemFileNames(ctxt, *pdi[2]);
 +      pathRight = pathsTemp[2];
 +
-       if ((*di1)->diffcode.isDirectory())
++      if (pdi[0]->diffcode.isDirectory())
 +      {
 +              isDir = true;
 +              if (GetPairComparability(PathContext(pathLeft, pathMiddle, pathRight)) != IS_EXISTING_DIR)
 +              {
 +                      errmsg = _("The selected folder is invalid.");
 +                      return false;
 +              } 
        }
 +
 +      paths.SetLeft(pathLeft.c_str());
 +      paths.SetRight(pathRight.c_str());
 +
 +      return true;
  }
  
  /**
index 4c52a2a,0000000..1f6e732
mode 100644,000000..100644
--- /dev/null
@@@ -1,792 -1,0 +1,792 @@@
- 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 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);
 +#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);
 +
 +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 GetOpenOneItem(const CDiffContext& ctxt, uintptr_t pos1, const DIFFITEM *pdi[3],
++              PathContext &paths, int & sel1, bool & isDir, int nPane[3], String& errmsg);
++bool GetOpenTwoItems(const CDiffContext& ctxt, SELECTIONTYPE selectionType, uintptr_t pos1, uintptr_t pos2, const DIFFITEM *pdi[3],
++              PathContext &paths, int & sel1, int & sel2, bool & isDir, int nPane[3], String& errmsg);
++bool GetOpenThreeItems(const CDiffContext& ctxt, uintptr_t pos1, uintptr_t pos2, uintptr_t pos3, const DIFFITEM *pdi[3],
++              PathContext &paths, int & sel1, int & sel2, int & sel3, bool & isDir, int nPane[3], 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/DirView.cpp
@@@ -1167,18 -1279,20 +1167,20 @@@ void CDirView::OpenParentDirectory(
  {
        CDirDoc *pDoc = GetDocument();
        PathContext pathsParent;
 -      switch (pDoc->AllowUpwardDirectory(pathsParent))
 +      switch (CheckAllowUpwardDirectory(GetDiffContext(), pDoc->m_pTempPathContext, pathsParent))
        {
 -      case CDirDoc::AllowUpwardDirectory::ParentIsTempPath:
 +      case AllowUpwardDirectory::ParentIsTempPath:
                pDoc->m_pTempPathContext = pDoc->m_pTempPathContext->DeleteHead();
                // fall through (no break!)
 -      case CDirDoc::AllowUpwardDirectory::ParentIsRegularPath: 
 +      case AllowUpwardDirectory::ParentIsRegularPath: 
        {
-               DWORD dwFlags[3] = {FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU};
+               DWORD dwFlags[3];
+               for (int nIndex = 0; nIndex < pathsParent.GetSize(); ++nIndex)
+                       dwFlags[nIndex] = FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nIndex) ? FFILEOPEN_READONLY : 0);
 -              GetMainFrame()->DoFileOpen(&pathsParent, dwFlags, pDoc->GetRecursive(), (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? NULL : pDoc);
 +              GetMainFrame()->DoFileOpen(&pathsParent, dwFlags, GetDiffContext().m_bRecursive, (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? NULL : pDoc);
        }
                // fall through (no break!)
 -      case CDirDoc::AllowUpwardDirectory::No:
 +      case AllowUpwardDirectory::No:
                break;
        default:
                LangMessageBox(IDS_INVALID_DIRECTORY, MB_ICONSTOP);
@@@ -1276,26 -1735,31 +1278,27 @@@ void CDirView::OpenSelection(SELECTIONT
  
        // Common variables which both code paths below are responsible for setting
        PathContext paths;
-       const DIFFITEM *di1 = NULL, *di2 = NULL, *di3 = NULL; // left & right items (di1==di2 if single selection)
 -      DIFFITEM *pdi[3] = {0}; // left & right items (di1==di2 if single selection)
++      const DIFFITEM *pdi[3] = {0}; // left & right items (di1==di2 if single selection)
        bool isdir = false; // set if we're comparing directories
 -      bool bRO[3] = { false, false, false };
+       int nPane[3];
 -
 +      String errmsg;
 +      bool success;
        if (pDoc->m_nDirs < 3 && pos2)
-               success = GetOpenTwoItems(ctxt, selectionType, pos1, pos2, &di1, &di2,
-                               paths, sel1, sel2, isdir, errmsg);
 -      {
 -              bool success = OpenTwoItems(selectionType, pos1, pos2, pdi,
 -                              paths, sel1, sel2, isdir, nPane);
 -              if (!success)
 -                      return;
 -      }
++              success = GetOpenTwoItems(ctxt, selectionType, pos1, pos2, pdi,
++                              paths, sel1, sel2, isdir, nPane, errmsg);
        else if (pDoc->m_nDirs == 3 && pos2)
-               success = GetOpenThreeItems(ctxt, pos1, pos2, pos3, &di1, &di2, &di3,
-                               paths, sel1, sel2, sel3, isdir, errmsg);
 -      {
 -              bool success = OpenThreeItems(pos1, pos2, pos3, pdi,
 -                              paths, sel1, sel2, sel3, isdir, nPane);
 -              if (!success)
 -                      return;
 -      }
++              success = GetOpenThreeItems(ctxt, pos1, pos2, pos3, pdi,
++                              paths, sel1, sel2, sel3, isdir, nPane, errmsg);
        else
 -      {
                // Only one item selected, so perform diff on its sides
-               success = GetOpenOneItem(ctxt, pos1, &di1, &di2, &di3, 
-                               paths, sel1, isdir, errmsg);
 -              bool success = OpenOneItem(pos1, pdi, paths, sel1, isdir, nPane);
++              success = GetOpenOneItem(ctxt, pos1, pdi, 
++                              paths, sel1, isdir, nPane, errmsg);
                if (!success)
 -                      return;
 +      if (!success)
 +      {
 +              if (!errmsg.empty())
 +                      AfxMessageBox(errmsg.c_str(), MB_ICONSTOP);
 +              return;
        }
  
        // Now pathLeft, pathRight, di1, di2, and isdir are all set
                {
                        theApp.m_strDescriptions[0].erase();
                        theApp.m_strDescriptions[1].erase();
-                       if (di1 == di2 && !di1->diffcode.isExists(0))
+                       if (pdi[0] == pdi[1] && !pdi[0]->diffcode.isExists(0))
                        {
                                paths[0] = _T("");
 -                              theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE);
 +                              theApp.m_strDescriptions[0] = _("Untitled left");
                        }
-                       if (di1 == di2 && !di1->diffcode.isExists(1))
+                       if (pdi[0] == pdi[1] && !pdi[0]->diffcode.isExists(1))
                        {
                                paths[1] = _T("");
 -                              theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE);
 +                              theApp.m_strDescriptions[1] = _("Untitled right");
                        }
                }
                else
                        theApp.m_strDescriptions[0].erase();
                        theApp.m_strDescriptions[1].erase();
                        theApp.m_strDescriptions[2].erase();
-                       if (di1 == di2 && di1 == di3 && !di1->diffcode.isExists(0))
+                       if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.isExists(0))
                        {
                                paths[0] = _T("");
 -                              theApp.m_strDescriptions[0] = theApp.LoadString(IDS_EMPTY_LEFT_FILE);
 +                              theApp.m_strDescriptions[0] = _("Untitled left");
                        }
-                       if (di1 == di2 && di1 == di3 && !di1->diffcode.isExists(1))
+                       if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.isExists(1))
                        {
                                paths[1] = _T("");
 -                              theApp.m_strDescriptions[1] = theApp.LoadString(IDS_EMPTY_MIDDLE_FILE);
 +                              theApp.m_strDescriptions[1] = _("Untitled middle");
                        }
-                       if (di1 == di2 && di1 == di3 && !di1->diffcode.isExists(2))
+                       if (pdi[0] == pdi[1] && pdi[0] == pdi[2] && !pdi[0]->diffcode.isExists(2))
                        {
                                paths[2] = _T("");
 -                              theApp.m_strDescriptions[2] = theApp.LoadString(IDS_EMPTY_RIGHT_FILE);
 +                              theApp.m_strDescriptions[2] = _("Untitled right");
                        }
                }
  
@@@ -1409,22 -1873,22 +1413,23 @@@ void CDirView::OpenSelectionHex(
  
        // Common variables which both code paths below are responsible for setting
        PathContext paths;
-       const DIFFITEM *di1 = NULL, *di2 = NULL, *di3 = NULL; // left & right items (di1==di2 if single selection)
 -      DIFFITEM *pdi[3] = { 0 }; // left & right items (di1==di2 if single selection)
++      const DIFFITEM *pdi[3]; // left & right items (di1==di2 if single selection)
        bool isdir = false; // set if we're comparing directories
+       int nPane[3];
 +      String errmsg;
 +      bool success;
        if (pos2)
-               success = GetOpenTwoItems(ctxt, SELECTIONTYPE_NORMAL, pos1, pos2, &di1, &di2,
-                               paths, sel1, sel2, isdir, errmsg);
++              success = GetOpenTwoItems(ctxt, SELECTIONTYPE_NORMAL, pos1, pos2, pdi,
++                              paths, sel1, sel2, isdir, nPane, errmsg);
 +      else
 +              // Only one item selected, so perform diff on its sides
-               success = GetOpenOneItem(ctxt, pos1, &di1, &di2, &di3,
-                               paths, sel1, isdir, errmsg);
++              success = GetOpenOneItem(ctxt, pos1, pdi,
++                              paths, sel1, isdir, nPane, errmsg);
 +      if (!success)
        {
 -              bool success = OpenTwoItems(SELECTIONTYPE_NORMAL, pos1, pos2, pdi,
 -                              paths, sel1, sel2, isdir, nPane);
 -              if (!success)
 -                      return;
 -      }
 -      else
 -      {
 -              // Only one item selected, so perform diff on its sides
 -              bool success = OpenOneItem(pos1, pdi, paths, sel1, isdir, nPane);
 -              if (!success)
 -                      return;
 +              if (!errmsg.empty())
 +                      AfxMessageBox(errmsg.c_str(), MB_ICONSTOP);
 +              return;
        }
  
        // Need to consider only regular file case here