OSDN Git Service

Use __super
[winmerge-jp/winmerge-jp.git] / Src / DirView.cpp
index fd81926..e21a88b 100644 (file)
@@ -2,21 +2,7 @@
 //    WinMerge:  an interactive diff/merge utility
 //    Copyright (C) 1997-2000  Thingamahoochie Software
 //    Author: Dean Grimm
-//
-//    This program is free software; you can redistribute it and/or modify
-//    it under the terms of the GNU General Public License as published by
-//    the Free Software Foundation; either version 2 of the License, or
-//    (at your option) any later version.
-//
-//    This program is distributed in the hope that it will be useful,
-//    but WITHOUT ANY WARRANTY; without even the implied warranty of
-//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//    GNU General Public License for more details.
-//
-//    You should have received a copy of the GNU General Public License
-//    along with this program; if not, write to the Free Software
-//    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-//
+//    SPDX-License-Identifier: GPL-2.0-or-later
 /////////////////////////////////////////////////////////////////////////////
 /**
  * @file  DirView.cpp
@@ -38,7 +24,7 @@
 #include "MainFrm.h"
 #include "resource.h"
 #include "FileTransform.h"
-#include "SelectUnpackerDlg.h"
+#include "SelectPluginDlg.h"
 #include "paths.h"
 #include "7zCommon.h"
 #include "OptionsDef.h"
 #include "BCMenu.h"
 #include "DirCmpReportDlg.h"
 #include "DirCmpReport.h"
-#include "DirCompProgressBar.h"
 #include "CompareStatisticsDlg.h"
 #include "LoadSaveCodepageDlg.h"
 #include "ConfirmFolderCopyDlg.h"
 #include "DirColsDlg.h"
+#include "DirAdditionalPropertiesDlg.h"
+#include "DirSelectFilesDlg.h"
 #include "UniFile.h"
 #include "ShellContextMenu.h"
 #include "DiffItem.h"
@@ -59,6 +46,9 @@
 #include "FileOrFolderSelect.h"
 #include "IntToIntMap.h"
 #include "PatchTool.h"
+#include "SyntaxColors.h"
+#include "Shell.h"
+#include "DirTravel.h"
 #include <numeric>
 #include <functional>
 
@@ -101,13 +91,8 @@ IMPLEMENT_DYNCREATE(CDirView, CListView)
 
 CDirView::CDirView()
                : m_pList(nullptr)
-               , m_nHiddenItems(0)
-               , m_bNeedSearchFirstDiffItem(true)
-               , m_bNeedSearchLastDiffItem(true)
-               , m_firstDiffItem(-1)
-               , m_lastDiffItem(-1)
-               , m_pCmpProgressBar(nullptr)
                , m_compareStart(0)
+               , m_elapsed(0)
                , m_bTreeMode(false)
                , m_dirfilter(std::bind(&COptionsMgr::GetBool, GetOptionsMgr(), _1))
                , m_pShellContextMenuLeft(nullptr)
@@ -121,11 +106,11 @@ CDirView::CDirView()
        m_dwDefaultStyle &= ~LVS_TYPEMASK;
        // Show selection all the time, so user can see current item even when
        // focus is elsewhere (ie, on file edit window)
-       m_dwDefaultStyle |= LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS;
+       m_dwDefaultStyle |= LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS | LVS_OWNERDATA;
 
        m_bTreeMode =  GetOptionsMgr()->GetBool(OPT_TREE_MODE);
        m_bExpandSubdirs = GetOptionsMgr()->GetBool(OPT_DIRVIEW_EXPAND_SUBDIRS);
-       m_bEscCloses = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
+       m_nEscCloses = GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC);
        Options::DirColors::Load(GetOptionsMgr(), m_cachedColors);
        m_bUseColors = GetOptionsMgr()->GetBool(OPT_DIRCLR_USE_COLORS);
 }
@@ -135,96 +120,209 @@ CDirView::~CDirView()
 }
 
 BEGIN_MESSAGE_MAP(CDirView, CListView)
-       ON_WM_CONTEXTMENU()
        //{{AFX_MSG_MAP(CDirView)
+       ON_WM_CONTEXTMENU()
        ON_WM_LBUTTONDBLCLK()
+       ON_WM_SIZE()
+       ON_WM_DESTROY()
+       ON_WM_CHAR()
+       ON_WM_KEYDOWN()
+       ON_WM_TIMER()
+       ON_MESSAGE(MSG_UI_UPDATE, OnUpdateUIMessage)
+       ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
+       ON_COMMAND(ID_EDIT_CUT, OnEditCut)
+       ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
+       ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
+       ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
+       ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateSave)
+       ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
+       ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemChanged)
+       ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
+       ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit)
+       ON_NOTIFY_REFLECT(LVN_ODFINDITEM, OnODFindItem)
+       ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
+       ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)
+       ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
+       // [File] menu
+       ON_COMMAND(ID_FILE_LEFT_READONLY, OnReadOnly<SIDE_LEFT>)
+       ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnReadOnly<SIDE_MIDDLE>)
+       ON_COMMAND(ID_FILE_RIGHT_READONLY, OnReadOnly<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateReadOnly<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateReadOnly<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateReadOnly<SIDE_RIGHT>)
+       ON_COMMAND(ID_FILE_ENCODING, OnFileEncoding)
+       // [Edit] menu
+       ON_COMMAND(ID_EDIT_SELECT_ALL, OnSelectAll)
+       ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateSelectAll)
+       // [View] menu
+       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENT, OnOptionsShowDifferent)
+       ON_COMMAND(ID_OPTIONS_SHOWIDENTICAL, OnOptionsShowIdentical)
+       ON_COMMAND(ID_OPTIONS_SHOWUNIQUELEFT, OnOptionsShowUniqueLeft)
+       ON_COMMAND(ID_OPTIONS_SHOWUNIQUEMIDDLE, OnOptionsShowUniqueMiddle)
+       ON_COMMAND(ID_OPTIONS_SHOWUNIQUERIGHT, OnOptionsShowUniqueRight)
+       ON_COMMAND(ID_OPTIONS_SHOWBINARIES, OnOptionsShowBinaries)
+       ON_COMMAND(ID_OPTIONS_SHOWSKIPPED, OnOptionsShowSkipped)
+       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTLEFTONLY, OnOptionsShowDifferentLeftOnly)
+       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTMIDDLEONLY, OnOptionsShowDifferentMiddleOnly)
+       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTRIGHTONLY, OnOptionsShowDifferentRightOnly)
+       ON_COMMAND(ID_OPTIONS_SHOWMISSINGLEFTONLY, OnOptionsShowMissingLeftOnly)
+       ON_COMMAND(ID_OPTIONS_SHOWMISSINGMIDDLEONLY, OnOptionsShowMissingMiddleOnly)
+       ON_COMMAND(ID_OPTIONS_SHOWMISSINGRIGHTONLY, OnOptionsShowMissingRightOnly)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENT, OnUpdateOptionsShowdifferent)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWIDENTICAL, OnUpdateOptionsShowidentical)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUELEFT, OnUpdateOptionsShowuniqueleft)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUEMIDDLE, OnUpdateOptionsShowuniquemiddle)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUERIGHT, OnUpdateOptionsShowuniqueright)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWBINARIES, OnUpdateOptionsShowBinaries)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWSKIPPED, OnUpdateOptionsShowSkipped)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTLEFTONLY, OnUpdateOptionsShowDifferentLeftOnly)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTMIDDLEONLY, OnUpdateOptionsShowDifferentMiddleOnly)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTRIGHTONLY, OnUpdateOptionsShowDifferentRightOnly)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWMISSINGLEFTONLY, OnUpdateOptionsShowMissingLeftOnly)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWMISSINGMIDDLEONLY, OnUpdateOptionsShowMissingMiddleOnly)
+       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWMISSINGRIGHTONLY, OnUpdateOptionsShowMissingRightOnly)
+       ON_COMMAND(ID_VIEW_SHOWHIDDENITEMS, OnViewShowHiddenItems)
+       ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWHIDDENITEMS, OnUpdateViewShowHiddenItems)
+       ON_COMMAND(ID_VIEW_TREEMODE, OnViewTreeMode)
+       ON_COMMAND(ID_VIEW_EXPAND_ALLSUBDIRS, OnViewExpandAllSubdirs)
+       ON_COMMAND(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnViewCollapseAllSubdirs)
+       ON_UPDATE_COMMAND_UI(ID_VIEW_TREEMODE, OnUpdateViewTreeMode)
+       ON_UPDATE_COMMAND_UI(ID_VIEW_EXPAND_ALLSUBDIRS, OnUpdateViewExpandAllSubdirs)
+       ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnUpdateViewCollapseAllSubdirs)
+       ON_COMMAND(ID_SWAPPANES_SWAP12, (OnViewSwapPanes<0, 1>))
+       ON_COMMAND(ID_SWAPPANES_SWAP23, (OnViewSwapPanes<1, 2>))
+       ON_COMMAND(ID_SWAPPANES_SWAP13, (OnViewSwapPanes<0, 2>))
+       ON_UPDATE_COMMAND_UI(ID_SWAPPANES_SWAP12, (OnUpdateViewSwapPanes<0, 1>))
+       ON_UPDATE_COMMAND_UI(ID_SWAPPANES_SWAP23, (OnUpdateViewSwapPanes<1, 2>))
+       ON_UPDATE_COMMAND_UI(ID_SWAPPANES_SWAP13, (OnUpdateViewSwapPanes<0, 2>))
+       ON_COMMAND(ID_VIEW_DIR_STATISTICS, OnViewCompareStatistics)
+       ON_COMMAND(ID_REFRESH, OnRefresh)
+       ON_UPDATE_COMMAND_UI(ID_REFRESH, OnUpdateRefresh)
+       ON_COMMAND(ID_RESCAN, OnMarkedRescan)
+       // [Merge] menu or Context menu
+       ON_COMMAND_RANGE(ID_MERGE_COMPARE, ID_MERGE_COMPARE_IN_NEW_WINDOW, OnMergeCompare)
+       ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE, ID_MERGE_COMPARE_IN_NEW_WINDOW, OnUpdateMergeCompare)
+       ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
+       ON_COMMAND(ID_LASTDIFF, OnLastdiff)
+       ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
+       ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
+       ON_COMMAND(ID_CURDIFF, OnCurdiff)
+       ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
+       ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
+       ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
+       ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
+       ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
        ON_COMMAND_RANGE(ID_L2R, ID_R2L, OnDirCopy)
        ON_UPDATE_COMMAND_UI_RANGE(ID_L2R, ID_R2L, OnUpdateDirCopy)
+       ON_COMMAND(ID_MERGE_DELETE, OnDelete)
+       ON_UPDATE_COMMAND_UI(ID_MERGE_DELETE, OnUpdateDelete)
+       // [Tools] menu
+       ON_COMMAND(ID_TOOLS_CUSTOMIZECOLUMNS, OnCustomizeColumns)
+       ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
+       ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
+       ON_MESSAGE(MSG_GENERATE_FLIE_COMPARE_REPORT, OnGenerateFileCmpReport)
+       // [Plugins] menu
+       ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
+       ON_UPDATE_COMMAND_UI(ID_OPEN_WITH_UNPACKER, OnUpdateCtxtOpenWithUnpacker)
+       // [Help] menu
+       ON_COMMAND(ID_HELP, OnHelp)
+       // Context menu
+       // Context menu -> Compare Non-horizontally
+       ON_COMMAND(ID_MERGE_COMPARE_LEFT1_LEFT2, OnMergeCompare2<SELECTIONTYPE_LEFT1LEFT2>)
+       ON_COMMAND(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnMergeCompare2<SELECTIONTYPE_RIGHT1RIGHT2>)
+       ON_COMMAND(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnMergeCompare2<SELECTIONTYPE_LEFT1RIGHT2>)
+       ON_COMMAND(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnMergeCompare2<SELECTIONTYPE_LEFT2RIGHT1>)
+       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_LEFT2, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT1LEFT2>)
+       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnUpdateMergeCompare2<SELECTIONTYPE_RIGHT1RIGHT2>)
+       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT1RIGHT2>)
+       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT2RIGHT1>)
+       ON_COMMAND(ID_MERGE_COMPARE_NONHORIZONTALLY, OnMergeCompareNonHorizontally)
+       // Context menu -> Compare As
+       ON_COMMAND_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnMergeCompareAs)
+       ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnMergeCompareAs)
+       ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_TEXT, ID_MERGE_COMPARE_WEBPAGE, OnUpdateMergeCompare)
+       ON_UPDATE_COMMAND_UI(ID_NO_UNPACKER, OnUpdateNoUnpacker)
+       // Context menu -> Copy
        ON_COMMAND(ID_DIR_COPY_LEFT_TO_RIGHT, (OnCtxtDirCopy<SIDE_LEFT, SIDE_RIGHT>))
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_RIGHT, (OnUpdateCtxtDirCopy<SIDE_LEFT, SIDE_RIGHT>))
        ON_COMMAND(ID_DIR_COPY_LEFT_TO_MIDDLE, (OnCtxtDirCopy<SIDE_LEFT, SIDE_MIDDLE>))
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_MIDDLE, (OnUpdateCtxtDirCopy<SIDE_LEFT, SIDE_MIDDLE>))
        ON_COMMAND(ID_DIR_COPY_RIGHT_TO_LEFT, (OnCtxtDirCopy<SIDE_RIGHT, SIDE_LEFT>))
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_LEFT, (OnUpdateCtxtDirCopy<SIDE_RIGHT, SIDE_LEFT>))
        ON_COMMAND(ID_DIR_COPY_RIGHT_TO_MIDDLE, (OnCtxtDirCopy<SIDE_RIGHT, SIDE_MIDDLE>))
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_MIDDLE, (OnUpdateCtxtDirCopy<SIDE_RIGHT, SIDE_MIDDLE>))
        ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_LEFT, (OnCtxtDirCopy<SIDE_MIDDLE, SIDE_LEFT>))
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_LEFT, (OnUpdateCtxtDirCopy<SIDE_MIDDLE, SIDE_LEFT>))
        ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_RIGHT, (OnCtxtDirCopy<SIDE_MIDDLE, SIDE_RIGHT>))
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_RIGHT, (OnUpdateCtxtDirCopy<SIDE_LEFT, SIDE_RIGHT>))
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_MIDDLE, (OnUpdateCtxtDirCopy<SIDE_LEFT, SIDE_MIDDLE>))
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_LEFT, (OnUpdateCtxtDirCopy<SIDE_RIGHT, SIDE_LEFT>))
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_MIDDLE, (OnUpdateCtxtDirCopy<SIDE_RIGHT, SIDE_MIDDLE>))
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_LEFT, (OnUpdateCtxtDirCopy<SIDE_MIDDLE, SIDE_LEFT>))
        ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_RIGHT, (OnUpdateCtxtDirCopy<SIDE_MIDDLE, SIDE_RIGHT>))
+       ON_COMMAND(ID_DIR_COPY_LEFT_TO_BROWSE, OnCtxtDirCopyTo<SIDE_LEFT>)
+       ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_BROWSE, OnCtxtDirCopyTo<SIDE_MIDDLE>)
+       ON_COMMAND(ID_DIR_COPY_RIGHT_TO_BROWSE, OnCtxtDirCopyTo<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
+       // Context menu -> Move
+       ON_COMMAND(ID_DIR_MOVE_LEFT_TO_BROWSE, OnCtxtDirMoveTo<SIDE_LEFT>)
+       ON_COMMAND(ID_DIR_MOVE_MIDDLE_TO_BROWSE, OnCtxtDirMoveTo<SIDE_MIDDLE>)
+       ON_COMMAND(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnCtxtDirMoveTo<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_LEFT_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_MIDDLE_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_RIGHT>)
+       // Context menu -> Delete
        ON_COMMAND(ID_DIR_DEL_LEFT, OnCtxtDirDel<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_LEFT, OnUpdateCtxtDirDel<SIDE_LEFT>)
        ON_COMMAND(ID_DIR_DEL_RIGHT, OnCtxtDirDel<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_MIDDLE, OnUpdateCtxtDirDel<SIDE_MIDDLE>)
        ON_COMMAND(ID_DIR_DEL_MIDDLE, OnCtxtDirDel<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_RIGHT, OnUpdateCtxtDirDel<SIDE_RIGHT>)
        ON_COMMAND(ID_DIR_DEL_BOTH, OnCtxtDirDelBoth)
-       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_BOTH, OnUpdateCtxtDirDelBoth)
        ON_COMMAND(ID_DIR_DEL_ALL, OnCtxtDirDelBoth)
+       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_LEFT, OnUpdateCtxtDirDel<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_MIDDLE, OnUpdateCtxtDirDel<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_RIGHT, OnUpdateCtxtDirDel<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_DEL_BOTH, OnUpdateCtxtDirDelBoth)
        ON_UPDATE_COMMAND_UI(ID_DIR_DEL_ALL, OnUpdateCtxtDirDelBoth)
+       // Context menu -> Rename, Hide Items
+       ON_COMMAND(ID_DIR_ITEM_RENAME, OnItemRename)
+       ON_UPDATE_COMMAND_UI(ID_DIR_ITEM_RENAME, OnUpdateItemRename)
+       ON_COMMAND(ID_DIR_HIDE_FILENAMES, OnHideFilenames)
+       ON_UPDATE_COMMAND_UI(ID_DIR_HIDE_FILENAMES, OnUpdateHideFilenames)
+       // Context menu -> Open Left
        ON_COMMAND(ID_DIR_OPEN_LEFT, OnCtxtDirOpen<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT, OnUpdateCtxtDirOpen<SIDE_LEFT>)
+       ON_COMMAND(ID_DIR_OPEN_LEFT_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_LEFT>)
        ON_COMMAND(ID_DIR_OPEN_LEFT_WITH, OnCtxtDirOpenWith<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITH, OnUpdateCtxtDirOpenWith<SIDE_LEFT>)
        ON_COMMAND(ID_DIR_OPEN_LEFT_PARENT_FOLDER, OnCtxtDirOpenParentFolder<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT, OnUpdateCtxtDirOpen<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITH, OnUpdateCtxtDirOpenWith<SIDE_LEFT>)
        ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_PARENT_FOLDER, OnUpdateCtxtDirOpenParentFolder<SIDE_LEFT>)
+       // Context menu -> Open Middle
        ON_COMMAND(ID_DIR_OPEN_MIDDLE, OnCtxtDirOpen<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE, OnUpdateCtxtDirOpen<SIDE_MIDDLE>)
+       ON_COMMAND(ID_DIR_OPEN_MIDDLE_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_MIDDLE>)
        ON_COMMAND(ID_DIR_OPEN_MIDDLE_WITH, OnCtxtDirOpenWith<SIDE_MIDDLE>)
+       ON_COMMAND(ID_DIR_OPEN_MIDDLE_PARENT_FOLDER, OnCtxtDirOpenParentFolder<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE, OnUpdateCtxtDirOpen<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_MIDDLE>)
        ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE_WITH, OnUpdateCtxtDirOpenWith<SIDE_MIDDLE>)
        ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE_PARENT_FOLDER, OnUpdateCtxtDirOpenParentFolder<SIDE_MIDDLE>)
-       ON_COMMAND(ID_DIR_OPEN_MIDDLE_PARENT_FOLDER, OnCtxtDirOpenParentFolder<SIDE_MIDDLE>)
+       // Context menu -> Open Right
        ON_COMMAND(ID_DIR_OPEN_RIGHT, OnCtxtDirOpen<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT, OnUpdateCtxtDirOpen<SIDE_RIGHT>)
+       ON_COMMAND(ID_DIR_OPEN_RIGHT_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_RIGHT>)
        ON_COMMAND(ID_DIR_OPEN_RIGHT_WITH, OnCtxtDirOpenWith<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITH, OnUpdateCtxtDirOpenWith<SIDE_RIGHT>)
        ON_COMMAND(ID_DIR_OPEN_RIGHT_PARENT_FOLDER, OnCtxtDirOpenParentFolder<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_PARENT_FOLDER, OnUpdateCtxtDirOpenParentFolder<SIDE_RIGHT>)
-       ON_COMMAND(ID_POPUP_OPEN_WITH_UNPACKER, OnCtxtOpenWithUnpacker)
-       ON_UPDATE_COMMAND_UI(ID_POPUP_OPEN_WITH_UNPACKER, OnUpdateCtxtOpenWithUnpacker)
-       ON_COMMAND(ID_DIR_OPEN_LEFT_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_LEFT_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_LEFT>)
-       ON_COMMAND(ID_DIR_OPEN_MIDDLE_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_MIDDLE_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_MIDDLE>)
-       ON_COMMAND(ID_DIR_OPEN_RIGHT_WITHEDITOR, OnCtxtDirOpenWithEditor<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT, OnUpdateCtxtDirOpen<SIDE_RIGHT>)
        ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITHEDITOR, OnUpdateCtxtDirOpenWithEditor<SIDE_RIGHT>)
-       ON_COMMAND(ID_DIR_COPY_LEFT_TO_BROWSE, OnCtxtDirCopyTo<SIDE_LEFT>)
-       ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_BROWSE, OnCtxtDirCopyTo<SIDE_MIDDLE>)
-       ON_COMMAND(ID_DIR_COPY_RIGHT_TO_BROWSE, OnCtxtDirCopyTo<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_BROWSE, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
-       ON_WM_DESTROY()
-       ON_WM_CHAR()
-       ON_WM_KEYDOWN()
-       ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
-       ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
-       ON_COMMAND(ID_LASTDIFF, OnLastdiff)
-       ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
-       ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
-       ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
-       ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
-       ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
-       ON_COMMAND(ID_CURDIFF, OnCurdiff)
-       ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
-       ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateSave)
-       ON_MESSAGE(MSG_UI_UPDATE, OnUpdateUIMessage)
-       ON_COMMAND(ID_REFRESH, OnRefresh)
-       ON_UPDATE_COMMAND_UI(ID_REFRESH, OnUpdateRefresh)
-       ON_WM_TIMER()
-       ON_UPDATE_COMMAND_UI(ID_STATUS_RIGHTDIR_RO, OnUpdateStatusRightRO)
-       ON_UPDATE_COMMAND_UI(ID_STATUS_MIDDLEDIR_RO, OnUpdateStatusMiddleRO)
-       ON_UPDATE_COMMAND_UI(ID_STATUS_LEFTDIR_RO, OnUpdateStatusLeftRO)
-       ON_COMMAND(ID_FILE_LEFT_READONLY, OnReadOnly<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateReadOnly<SIDE_LEFT>)
-       ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnReadOnly<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateReadOnly<SIDE_MIDDLE>)
-       ON_COMMAND(ID_FILE_RIGHT_READONLY, OnReadOnly<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateReadOnly<SIDE_RIGHT>)
-       ON_COMMAND(ID_TOOLS_CUSTOMIZECOLUMNS, OnCustomizeColumns)
-       ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
-       ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
-       ON_MESSAGE(MSG_GENERATE_FLIE_COMPARE_REPORT, OnGenerateFileCmpReport)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_WITH, OnUpdateCtxtDirOpenWith<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_OPEN_RIGHT_PARENT_FOLDER, OnUpdateCtxtDirOpenParentFolder<SIDE_RIGHT>)
+       // Context menu -> Copy Pathnames
+       ON_COMMAND(ID_DIR_COPY_PATHNAMES_LEFT, OnCopyPathnames<SIDE_LEFT>)
+       ON_COMMAND(ID_DIR_COPY_PATHNAMES_MIDDLE, OnCopyPathnames<SIDE_MIDDLE>)
+       ON_COMMAND(ID_DIR_COPY_PATHNAMES_RIGHT, OnCopyPathnames<SIDE_RIGHT>)
+       ON_COMMAND(ID_DIR_COPY_PATHNAMES_BOTH, OnCopyBothPathnames)
+       ON_COMMAND(ID_DIR_COPY_PATHNAMES_ALL, OnCopyBothPathnames)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_LEFT, OnUpdateCtxtDirCopy2<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_MIDDLE, OnUpdateCtxtDirCopy2<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_RIGHT, OnUpdateCtxtDirCopy2<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_BOTH, OnUpdateCtxtDirCopyBoth2)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_ALL, OnUpdateCtxtDirCopyBoth2)
+       // Context menu -> Zip
        ON_COMMAND(ID_DIR_ZIP_LEFT, OnCtxtDirZip<DirItemEnumerator::Left>)
        ON_COMMAND(ID_DIR_ZIP_MIDDLE, OnCtxtDirZip<DirItemEnumerator::Middle>)
        ON_COMMAND(ID_DIR_ZIP_RIGHT, OnCtxtDirZip<DirItemEnumerator::Right>)
@@ -237,118 +335,35 @@ BEGIN_MESSAGE_MAP(CDirView, CListView)
        ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_BOTH, OnUpdateCtxtDirCopyBothTo)
        ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_ALL, OnUpdateCtxtDirCopyBothTo)
        ON_UPDATE_COMMAND_UI(ID_DIR_ZIP_BOTH_DIFFS_ONLY, OnUpdateCtxtDirCopyBothDiffsOnlyTo)
+       // Context menu -> Left/Middle/Right Shell menu
        ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_LEFT, OnCtxtDirShellContextMenu<SIDE_LEFT>)
        ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_MIDDLE, OnCtxtDirShellContextMenu<SIDE_MIDDLE>)
        ON_COMMAND(ID_DIR_SHELL_CONTEXT_MENU_RIGHT, OnCtxtDirShellContextMenu<SIDE_RIGHT>)
-       ON_COMMAND(ID_EDIT_SELECT_ALL, OnSelectAll)
-       ON_UPDATE_COMMAND_UI(ID_EDIT_SELECT_ALL, OnUpdateSelectAll)
-       ON_COMMAND_RANGE(ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, OnPluginPredifferMode)
-       ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, OnUpdatePluginPredifferMode)
-       ON_COMMAND(ID_DIR_COPY_PATHNAMES_LEFT, OnCopyPathnames<SIDE_LEFT>)
-       ON_COMMAND(ID_DIR_COPY_PATHNAMES_MIDDLE, OnCopyPathnames<SIDE_MIDDLE>)
-       ON_COMMAND(ID_DIR_COPY_PATHNAMES_RIGHT, OnCopyPathnames<SIDE_RIGHT>)
-       ON_COMMAND(ID_DIR_COPY_PATHNAMES_BOTH, OnCopyBothPathnames)
-       ON_COMMAND(ID_DIR_COPY_PATHNAMES_ALL, OnCopyBothPathnames)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_LEFT, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_MIDDLE, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_RIGHT, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_BOTH, OnUpdateCtxtDirCopyBothTo)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_PATHNAMES_ALL, OnUpdateCtxtDirCopyBothTo)
+       // Context menu -> Plugin settings
+       ON_COMMAND_RANGE(ID_PREDIFFER_SETTINGS_NONE, ID_PREDIFFER_SETTINGS_SELECT, OnPluginSettings)
+       ON_COMMAND_RANGE(ID_UNPACKER_SETTINGS_NONE, ID_UNPACKER_SETTINGS_SELECT, OnPluginSettings)
+       ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFER_SETTINGS_NONE, ID_PREDIFFER_SETTINGS_SELECT, OnUpdatePluginMode)
+       ON_UPDATE_COMMAND_UI_RANGE(ID_UNPACKER_SETTINGS_NONE, ID_UNPACKER_SETTINGS_SELECT, OnUpdatePluginMode)
+       // Context menu -> Copy Filenames
        ON_COMMAND(ID_DIR_COPY_FILENAMES, OnCopyFilenames)
        ON_UPDATE_COMMAND_UI(ID_DIR_COPY_FILENAMES, OnUpdateCopyFilenames)
+       // Context menu -> Copy Items to Clipboard
        ON_COMMAND(ID_DIR_COPY_LEFT_TO_CLIPBOARD, OnCopyToClipboard<SIDE_LEFT>)
        ON_COMMAND(ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, OnCopyToClipboard<SIDE_MIDDLE>)
        ON_COMMAND(ID_DIR_COPY_RIGHT_TO_CLIPBOARD, OnCopyToClipboard<SIDE_RIGHT>)
        ON_COMMAND(ID_DIR_COPY_BOTH_TO_CLIPBOARD, OnCopyBothToClipboard)
        ON_COMMAND(ID_DIR_COPY_ALL_TO_CLIPBOARD, OnCopyBothToClipboard)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_CLIPBOARD, OnUpdateCtxtDirCopyTo<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, OnUpdateCtxtDirCopyTo<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_CLIPBOARD, OnUpdateCtxtDirCopyTo<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_BOTH_TO_CLIPBOARD, OnUpdateCtxtDirCopyBothTo)
-       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_ALL_TO_CLIPBOARD, OnUpdateCtxtDirCopyBothTo)
-       ON_COMMAND(ID_DIR_ITEM_RENAME, OnItemRename)
-       ON_UPDATE_COMMAND_UI(ID_DIR_ITEM_RENAME, OnUpdateItemRename)
-       ON_COMMAND(ID_DIR_HIDE_FILENAMES, OnHideFilenames)
-       ON_COMMAND(ID_DIR_MOVE_LEFT_TO_BROWSE, OnCtxtDirMoveTo<SIDE_LEFT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_LEFT_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_LEFT>)
-       ON_COMMAND(ID_DIR_MOVE_MIDDLE_TO_BROWSE, OnCtxtDirMoveTo<SIDE_MIDDLE>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_MIDDLE_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_MIDDLE>)
-       ON_COMMAND(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnCtxtDirMoveTo<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_MOVE_RIGHT_TO_BROWSE, OnUpdateCtxtDirMoveTo<SIDE_RIGHT>)
-       ON_UPDATE_COMMAND_UI(ID_DIR_HIDE_FILENAMES, OnUpdateHideFilenames)
-       ON_WM_SIZE()
-       ON_COMMAND(ID_MERGE_DELETE, OnDelete)
-       ON_UPDATE_COMMAND_UI(ID_MERGE_DELETE, OnUpdateDelete)
-       ON_COMMAND(ID_RESCAN, OnMarkedRescan)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_LEFT_TO_CLIPBOARD, OnUpdateCtxtDirCopy2<SIDE_LEFT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_MIDDLE_TO_CLIPBOARD, OnUpdateCtxtDirCopy2<SIDE_MIDDLE>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_RIGHT_TO_CLIPBOARD, OnUpdateCtxtDirCopy2<SIDE_RIGHT>)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_BOTH_TO_CLIPBOARD, OnUpdateCtxtDirCopyBoth2)
+       ON_UPDATE_COMMAND_UI(ID_DIR_COPY_ALL_TO_CLIPBOARD, OnUpdateCtxtDirCopyBoth2)
+       // Status bar
        ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
-       ON_COMMAND(ID_VIEW_SHOWHIDDENITEMS, OnViewShowHiddenItems)
-       ON_UPDATE_COMMAND_UI(ID_VIEW_SHOWHIDDENITEMS, OnUpdateViewShowHiddenItems)
-       ON_COMMAND(ID_MERGE_COMPARE, OnMergeCompare)
-       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE, OnUpdateMergeCompare)
-       ON_COMMAND(ID_MERGE_COMPARE_LEFT1_LEFT2, OnMergeCompare2<SELECTIONTYPE_LEFT1LEFT2>)
-       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_LEFT2, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT1LEFT2>)
-       ON_COMMAND(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnMergeCompare2<SELECTIONTYPE_RIGHT1RIGHT2>)
-       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_RIGHT1_RIGHT2, OnUpdateMergeCompare2<SELECTIONTYPE_RIGHT1RIGHT2>)
-       ON_COMMAND(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnMergeCompare2<SELECTIONTYPE_LEFT1RIGHT2>)
-       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT1_RIGHT2, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT1RIGHT2>)
-       ON_COMMAND(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnMergeCompare2<SELECTIONTYPE_LEFT2RIGHT1>)
-       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_LEFT2_RIGHT1, OnUpdateMergeCompare2<SELECTIONTYPE_LEFT2RIGHT1>)
-       ON_COMMAND(ID_MERGE_COMPARE_XML, OnMergeCompareXML)
-       ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_XML, OnUpdateMergeCompare)
-       ON_COMMAND_RANGE(ID_MERGE_COMPARE_HEX, ID_MERGE_COMPARE_IMAGE, OnMergeCompareAs)
-       ON_UPDATE_COMMAND_UI_RANGE(ID_MERGE_COMPARE_HEX, ID_MERGE_COMPARE_IMAGE, OnUpdateMergeCompare)
-       ON_COMMAND(ID_VIEW_TREEMODE, OnViewTreeMode)
-       ON_UPDATE_COMMAND_UI(ID_VIEW_TREEMODE, OnUpdateViewTreeMode)
-       ON_COMMAND(ID_VIEW_EXPAND_ALLSUBDIRS, OnViewExpandAllSubdirs)
-       ON_UPDATE_COMMAND_UI(ID_VIEW_EXPAND_ALLSUBDIRS, OnUpdateViewExpandAllSubdirs)
-       ON_COMMAND(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnViewCollapseAllSubdirs)
-       ON_UPDATE_COMMAND_UI(ID_VIEW_COLLAPSE_ALLSUBDIRS, OnUpdateViewCollapseAllSubdirs)
-       ON_COMMAND(ID_VIEW_SWAPPANES, OnViewSwapPanes)
-       ON_COMMAND(ID_VIEW_DIR_STATISTICS, OnViewCompareStatistics)
-       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENT, OnOptionsShowDifferent)
-       ON_COMMAND(ID_OPTIONS_SHOWIDENTICAL, OnOptionsShowIdentical)
-       ON_COMMAND(ID_OPTIONS_SHOWUNIQUELEFT, OnOptionsShowUniqueLeft)
-       ON_COMMAND(ID_OPTIONS_SHOWUNIQUEMIDDLE, OnOptionsShowUniqueMiddle)
-       ON_COMMAND(ID_OPTIONS_SHOWUNIQUERIGHT, OnOptionsShowUniqueRight)
-       ON_COMMAND(ID_OPTIONS_SHOWBINARIES, OnOptionsShowBinaries)
-       ON_COMMAND(ID_OPTIONS_SHOWSKIPPED, OnOptionsShowSkipped)
-       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTLEFTONLY, OnOptionsShowDifferentLeftOnly)
-       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTMIDDLEONLY, OnOptionsShowDifferentMiddleOnly)
-       ON_COMMAND(ID_OPTIONS_SHOWDIFFERENTRIGHTONLY, OnOptionsShowDifferentRightOnly)
-       ON_COMMAND(ID_OPTIONS_SHOWMISSINGLEFTONLY, OnOptionsShowMissingLeftOnly)
-       ON_COMMAND(ID_OPTIONS_SHOWMISSINGMIDDLEONLY, OnOptionsShowMissingMiddleOnly)
-       ON_COMMAND(ID_OPTIONS_SHOWMISSINGRIGHTONLY, OnOptionsShowMissingRightOnly)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENT, OnUpdateOptionsShowdifferent)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWIDENTICAL, OnUpdateOptionsShowidentical)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUELEFT, OnUpdateOptionsShowuniqueleft)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUEMIDDLE, OnUpdateOptionsShowuniquemiddle)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWUNIQUERIGHT, OnUpdateOptionsShowuniqueright)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWBINARIES, OnUpdateOptionsShowBinaries)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWSKIPPED, OnUpdateOptionsShowSkipped)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTLEFTONLY, OnUpdateOptionsShowDifferentLeftOnly)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTMIDDLEONLY, OnUpdateOptionsShowDifferentMiddleOnly)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWDIFFERENTRIGHTONLY, OnUpdateOptionsShowDifferentRightOnly)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWMISSINGLEFTONLY, OnUpdateOptionsShowMissingLeftOnly)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWMISSINGMIDDLEONLY, OnUpdateOptionsShowMissingMiddleOnly)
-       ON_UPDATE_COMMAND_UI(ID_OPTIONS_SHOWMISSINGRIGHTONLY, OnUpdateOptionsShowMissingRightOnly)
-       ON_COMMAND(ID_FILE_ENCODING, OnFileEncoding)
-       ON_COMMAND(ID_HELP, OnHelp)
-       ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
-       ON_COMMAND(ID_EDIT_CUT, OnEditCut)
-       ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
-       ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
-       ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
+       ON_UPDATE_COMMAND_UI(ID_STATUS_RIGHTDIR_RO, OnUpdateStatusRightRO)
+       ON_UPDATE_COMMAND_UI(ID_STATUS_MIDDLEDIR_RO, OnUpdateStatusMiddleRO)
+       ON_UPDATE_COMMAND_UI(ID_STATUS_LEFTDIR_RO, OnUpdateStatusLeftRO)
        //}}AFX_MSG_MAP
-       ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick)
-       ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemChanged)
-       ON_NOTIFY_REFLECT(LVN_BEGINLABELEDIT, OnBeginLabelEdit)
-       ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndLabelEdit)
-       ON_NOTIFY_REFLECT(NM_CLICK, OnClick)
-       ON_NOTIFY_REFLECT(LVN_BEGINDRAG, OnBeginDrag)
-       ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
-       ON_BN_CLICKED(IDC_COMPARISON_STOP, OnBnClickedComparisonStop)
-       ON_BN_CLICKED(IDC_COMPARISON_PAUSE, OnBnClickedComparisonPause)
-       ON_BN_CLICKED(IDC_COMPARISON_CONTINUE, OnBnClickedComparisonContinue)
 END_MESSAGE_MAP()
 
 /////////////////////////////////////////////////////////////////////////////
@@ -379,17 +394,16 @@ void CDirView::OnInitialUpdate()
                return 48;
        }();
        const int iconCY = iconCX;
-       CListView::OnInitialUpdate();
+       __super::OnInitialUpdate();
        m_pList = &GetListCtrl();
-       m_pIList.reset(new IListCtrlImpl(m_pList->m_hWnd));
-       GetDocument()->SetDirView(this);
-       m_pColItems.reset(new DirViewColItems(GetDocument()->m_nDirs));
+       m_pIList.reset(new IListCtrlImpl(m_pList->m_hWnd, m_listViewItems));
+       CDirDoc* pDoc = GetDocument();
+       pDoc->SetDirView(this);
+
+       auto properties = strutils::split<std::vector<String>>(GetOptionsMgr()->GetString(OPT_ADDITIONAL_PROPERTIES), ' ');
+       m_pColItems.reset(new DirViewColItems(pDoc->m_nDirs, properties));
 
-#ifdef _UNICODE
        m_pList->SendMessage(CCM_SETUNICODEFORMAT, TRUE, 0);
-#else
-       m_pList->SendMessage(CCM_SETUNICODEFORMAT, FALSE, 0);
-#endif
 
        // Load user-selected font
        if (GetOptionsMgr()->GetBool(OPT_FONT_DIRCMP + OPT_FONT_USECUSTOM))
@@ -398,6 +412,9 @@ void CDirView::OnInitialUpdate()
                CWnd::SetFont(&m_font, TRUE);
        }
 
+       if (m_bUseColors)
+               m_pList->SetBkColor(m_cachedColors.clrDirMargin);
+
        // Replace standard header with sort header
        HWND hWnd = ListView_GetHeader(m_pList->m_hWnd);
        if (hWnd != nullptr)
@@ -419,7 +436,8 @@ void CDirView::OnInitialUpdate()
                IDI_COMPARE_ERROR,
                IDI_FOLDERUP, IDI_FOLDERUP_DISABLE,
                IDI_COMPARE_ABORTED,
-               IDI_NOTEQUALTEXTFILE, IDI_EQUALTEXTFILE
+               IDI_NOTEQUALTEXTFILE, IDI_EQUALTEXTFILE,
+               IDI_NOTEQUALIMAGE, IDI_EQUALIMAGE, 
        };
        for (auto id : icon_ids)
                VERIFY(-1 != m_imageList.Add((HICON)LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(id), IMAGE_ICON, iconCX, iconCY, 0)));
@@ -432,7 +450,7 @@ void CDirView::OnInitialUpdate()
 
        // Restore column orders as they had them last time they ran
        m_pColItems->LoadColumnOrders(
-               (const TCHAR *)theApp.GetProfileString(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), _T("ColumnOrders")));
+               GetOptionsMgr()->GetString(pDoc->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_ORDERS : OPT_DIRVIEW3_COLUMN_ORDERS));
 
        // Display column headers (in appropriate order)
        ReloadColumns();
@@ -440,10 +458,17 @@ void CDirView::OnInitialUpdate()
        // Show selection across entire row.u
        // Also allow user to rearrange columns via drag&drop of headers.
        // Also enable infotips.
-       DWORD exstyle = LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP;
+       DWORD exstyle = LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP | LVS_EX_DOUBLEBUFFER;
        m_pList->SetExtendedStyle(exstyle);
 }
 
+BOOL CDirView::PreCreateWindow(CREATESTRUCT& cs)
+{
+       __super::PreCreateWindow(cs);
+       cs.dwExStyle &= ~WS_EX_CLIENTEDGE;
+       return TRUE;
+}
+
 /**
  * @brief Called before compare is started.
  * CDirDoc calls this function before new compare is started, so this
@@ -452,17 +477,6 @@ void CDirView::OnInitialUpdate()
  */
 void CDirView::StartCompare(CompareStats *pCompareStats)
 {
-       if (m_pCmpProgressBar == nullptr)
-               m_pCmpProgressBar.reset(new DirCompProgressBar());
-
-       if (!::IsWindow(m_pCmpProgressBar->GetSafeHwnd()))
-               m_pCmpProgressBar->Create(GetParentFrame());
-
-       m_pCmpProgressBar->SetCompareStat(pCompareStats);
-       m_pCmpProgressBar->StartUpdating();
-
-       GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), TRUE, FALSE);
-
        m_compareStart = clock();
 }
 
@@ -487,11 +501,11 @@ void CDirView::OnLButtonDblClk(UINT nFlags, CPoint point)
                }
                else
                {
-                       CWaitCursor waitstatus;
                        OpenSelection();
                }
        }
-       CListView::OnLButtonDblClk(nFlags, point);
+       if (GetFocus() == this)
+               __super::OnLButtonDblClk(nFlags, point);
 }
 
 /**
@@ -503,8 +517,8 @@ void CDirView::ReloadColumns()
 
        UpdateColumnNames();
        m_pColItems->LoadColumnWidths(
-               (const TCHAR *)theApp.GetProfileString(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), _T("ColumnWidths")),
-               std::bind(&CListCtrl::SetColumnWidth, m_pList, _1, _2), DefColumnWidth);
+               GetOptionsMgr()->GetString(GetDocument()->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_WIDTHS : OPT_DIRVIEW3_COLUMN_WIDTHS),
+               std::bind(&CListCtrl::SetColumnWidth, m_pList, _1, _2), GetDefColumnWidth());
        SetColAlignments();
 }
 
@@ -517,7 +531,6 @@ void CDirView::ReloadColumns()
  */
 void CDirView::RedisplayChildren(DIFFITEM *diffpos, int level, UINT &index, int &alldiffs)
 {
-       CDirDoc *pDoc = GetDocument();
        const CDiffContext &ctxt = GetDiffContext();
        while (diffpos != nullptr)
        {
@@ -536,14 +549,13 @@ void CDirView::RedisplayChildren(DIFFITEM *diffpos, int level, UINT &index, int
                                index++;
                                if (di.HasChildren())
                                {
-                                       m_pList->SetItemState(index - 1, INDEXTOSTATEIMAGEMASK((di.customFlags & ViewCustomFlags::EXPANDED) ? 2 : 1), LVIS_STATEIMAGEMASK);
                                        if (di.customFlags & ViewCustomFlags::EXPANDED)
                                                RedisplayChildren(ctxt.GetFirstChildDiffPosition(curdiffpos), level + 1, index, alldiffs);
                                }
                        }
                        else
                        {
-                               if (!ctxt.m_bRecursive || !di.diffcode.isDirectory() || !di.diffcode.existAll())
+                               if (!ctxt.m_bRecursive || !di.diffcode.isDirectory() || (!di.diffcode.existAll() && !di.HasChildren()))
                                {
                                        AddNewItem(index, curdiffpos, I_IMAGECALLBACK, 0);
                                        index++;
@@ -555,6 +567,8 @@ void CDirView::RedisplayChildren(DIFFITEM *diffpos, int level, UINT &index, int
                        }
                }
        }
+       m_firstDiffItem.reset();
+       m_lastDiffItem.reset();
 }
 
 /**
@@ -591,6 +605,8 @@ void CDirView::Redisplay()
                GetParentFrame()->SetLastCompareResult(alldiffs);
        SortColumnsAppropriately();
        SetRedraw(TRUE);
+       m_pList->SetItemCount(static_cast<int>(m_listViewItems.size()));
+       m_pList->Invalidate();
 }
 
 /**
@@ -676,7 +692,7 @@ static void NTAPI CheckContextMenu(BCMenu *pPopup, UINT uIDItem, BOOL bCheck)
  */
 void CDirView::ListContextMenu(CPoint point, int /*i*/)
 {
-       CDirDoc *pDoc = GetDocument();
+       CDirDocpDoc = GetDocument();
        BCMenu menu;
        VERIFY(menu.LoadMenu(IDR_POPUP_DIRVIEW));
        VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
@@ -686,6 +702,16 @@ void CDirView::ListContextMenu(CPoint point, int /*i*/)
        BCMenu *pPopup = static_cast<BCMenu*>(menu.GetSubMenu(0));
        ASSERT(pPopup != nullptr);
 
+       int sel = GetFocusedItem();
+       if (sel == -1)
+               sel = GetFirstSelectedInd();
+       if (sel == -1)
+               return;
+       const DIFFITEM& di = GetDiffItem(sel);
+       if (GetDiffContext().m_bRecursive && di.diffcode.isDirectory())
+               pPopup->RemoveMenu(ID_MERGE_COMPARE, MF_BYCOMMAND);
+       if (!di.diffcode.isDirectory())
+               pPopup->RemoveMenu(ID_MERGE_COMPARE_IN_NEW_WINDOW, MF_BYCOMMAND);
        if (pDoc->m_nDirs < 3)
        {
                pPopup->RemoveMenu(ID_DIR_COPY_LEFT_TO_MIDDLE, MF_BYCOMMAND);
@@ -713,7 +739,7 @@ void CDirView::ListContextMenu(CPoint point, int /*i*/)
                pPopup->RemoveMenu(ID_DIR_ZIP_MIDDLE, MF_BYCOMMAND);
                pPopup->RemoveMenu(ID_DIR_ZIP_ALL, MF_BYCOMMAND);
                pPopup->RemoveMenu(ID_DIR_SHELL_CONTEXT_MENU_MIDDLE, MF_BYCOMMAND);
-
+               pPopup->RemoveMenu(ID_MERGE_COMPARE_NONHORIZONTALLY, MF_BYCOMMAND);
        }
        else
        {
@@ -721,6 +747,7 @@ void CDirView::ListContextMenu(CPoint point, int /*i*/)
                pPopup->RemoveMenu(ID_DIR_COPY_BOTH_TO_CLIPBOARD, MF_BYCOMMAND);
                pPopup->RemoveMenu(ID_DIR_ZIP_BOTH, MF_BYCOMMAND);
                pPopup->RemoveMenu(ID_DIR_DEL_BOTH, MF_BYCOMMAND);
+               pPopup->RemoveMenu(2, MF_BYPOSITION); // Compare Non-horizontally
        }
 
        CMenu menuPluginsHolder;
@@ -908,6 +935,8 @@ void CDirView::DoDirAction(DirActions::method_type func, const String& status_me
                ConfirmAndPerformActions(actionScript);
        } catch (ContentsChangedException& e) {
                AfxMessageBox(e.m_msg.c_str(), MB_ICONWARNING);
+       } catch (FileOperationException& e) {
+               AfxMessageBox(e.m_msg.c_str(), MB_ICONWARNING);
        }
 }
 
@@ -933,7 +962,7 @@ void CDirView::DoDirActionTo(SIDE_TYPE stype, DirActions::method_type func, cons
        try {
                // First we build a list of desired actions
                FileActionScript actionScript;
-               actionScript.m_destBase = destPath;
+               actionScript.m_destBase = std::move(destPath);
                DirItemWithIndexIterator begin(m_pIList.get(), -1, true);
                DirItemWithIndexIterator end;
                FileActionScript *rsltScript;
@@ -990,9 +1019,12 @@ void CDirView::PerformActionList(FileActionScript & actionScript)
        actionScript.SetParentWindow(GetMainFrame()->GetSafeHwnd());
 
        theApp.AddOperation();
-       if (actionScript.Run())
+       bool succeeded = actionScript.Run();
+       if (succeeded)
                UpdateAfterFileScript(actionScript);
        theApp.RemoveOperation();
+       if (!succeeded && !actionScript.IsCanceled())
+               throw FileOperationException(_T("File operation failed"));
 }
 
 /**
@@ -1091,17 +1123,20 @@ void CDirView::OnColumnClick(NMHDR *pNMHDR, LRESULT *pResult)
 void CDirView::SortColumnsAppropriately()
 {
        int sortCol = GetOptionsMgr()->GetInt((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
-       if (sortCol == -1 || sortCol >= m_pColItems->GetColCount())
+       if (sortCol < 0 || sortCol >= m_pColItems->GetColCount())
                return;
 
        bool bSortAscending = GetOptionsMgr()->GetBool(OPT_DIRVIEW_SORT_ASCENDING);
        m_ctlSortHeader.SetSortImage(m_pColItems->ColLogToPhys(sortCol), bSortAscending);
        //sort using static CompareFunc comparison function
        CompareState cs(&GetDiffContext(), m_pColItems.get(), sortCol, bSortAscending, m_bTreeMode);
-       GetListCtrl().SortItems(cs.CompareFunc, reinterpret_cast<DWORD_PTR>(&cs));
+       std::stable_sort(m_listViewItems.begin(), m_listViewItems.end(), [&cs](const ListViewOwnerDataItem& a, const ListViewOwnerDataItem& b)
+               { return CompareState::CompareFunc(a.lParam, b.lParam, reinterpret_cast<LPARAM>(&cs)) < 0; });
 
-       m_bNeedSearchLastDiffItem = true;
-       m_bNeedSearchFirstDiffItem = true;
+       m_firstDiffItem.reset();
+       m_lastDiffItem.reset();
+       
+       m_pList->Invalidate();
 }
 
 /// Do any last minute work as view closes
@@ -1109,14 +1144,17 @@ void CDirView::OnDestroy()
 {
        DeleteAllDisplayItems();
 
-       String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
-       theApp.WriteProfileString(secname.c_str(), _T("ColumnOrders"), m_pColItems->SaveColumnOrders().c_str());
-       theApp.WriteProfileString(secname.c_str(), _T("ColumnWidths"),
-               m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1)).c_str());
-
-       CListView::OnDestroy();
+       {
+               const String keyname = GetDocument()->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_ORDERS : OPT_DIRVIEW3_COLUMN_ORDERS;
+               GetOptionsMgr()->SaveOption(keyname, m_pColItems->SaveColumnOrders());
+       }
+       {
+               const String keyname = GetDocument()->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_WIDTHS : OPT_DIRVIEW3_COLUMN_WIDTHS;
+               GetOptionsMgr()->SaveOption(keyname,
+                       m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1)));
+       }
 
-       GetMainFrame()->ClearStatusbarItemCount();
+       __super::OnDestroy();
 }
 
 /**
@@ -1139,12 +1177,11 @@ void CDirView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
                        }
                        else
                        {
-                               CWaitCursor waitstatus;
                                OpenSelection();
                        }
                }
        }
-       CListView::OnChar(nChar, nRepCnt, nFlags);
+       __super::OnChar(nChar, nRepCnt, nFlags);
 }
 
 /**
@@ -1181,7 +1218,6 @@ void CDirView::CollapseSubdir(int sel)
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
 
        dip.customFlags &= ~ViewCustomFlags::EXPANDED;
-       m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(1), LVIS_STATEIMAGEMASK);
 
        int count = m_pList->GetItemCount();
        for (int i = sel + 1; i < count; i++)
@@ -1189,6 +1225,7 @@ void CDirView::CollapseSubdir(int sel)
                const DIFFITEM& di = GetDiffItem(i);
                if (!di.IsAncestor(&dip))
                        break;
+               m_listViewItems.erase(m_listViewItems.begin() + i);
                m_pList->DeleteItem(i--);
                count--;
        }
@@ -1207,7 +1244,9 @@ void CDirView::ExpandSubdir(int sel, bool bRecursive)
                return;
 
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
-       m_pList->SetItemState(sel, INDEXTOSTATEIMAGEMASK(2), LVIS_STATEIMAGEMASK);
+
+       const int top = m_pList->GetTopIndex();
+       const size_t num = m_listViewItems.size();
 
        CDiffContext &ctxt = GetDiffContext();
        dip.customFlags |= ViewCustomFlags::EXPANDED;
@@ -1219,15 +1258,21 @@ void CDirView::ExpandSubdir(int sel, bool bRecursive)
        int alldiffs;
        RedisplayChildren(diffpos, dip.GetDepth() + 1, indext, alldiffs);
 
+       for (size_t i = 0; i < m_listViewItems.size() - num; ++i)
+               m_pList->InsertItem(sel + 1, nullptr);
+
        SortColumnsAppropriately();
 
        m_pList->SetRedraw(TRUE);       // Turn updating back on
+       m_pList->SetItemCount(static_cast<int>(m_listViewItems.size()));
+       m_pList->EnsureVisible(top, TRUE);
+       m_pList->Invalidate();
 }
 
 /**
  * @brief Open parent folder if possible.
  */
-void CDirView::OpenParentDirectory()
+void CDirView::OpenParentDirectory(CDirDoc *pDocOpen)
 {
        CDirDoc *pDoc = GetDocument();
        PathContext pathsParent;
@@ -1235,15 +1280,13 @@ void CDirView::OpenParentDirectory()
        {
        case AllowUpwardDirectory::ParentIsTempPath:
                pDoc->m_pTempPathContext = pDoc->m_pTempPathContext->DeleteHead();
-               // fall through (no break!)
+               [[fallthrough]];
        case AllowUpwardDirectory::ParentIsRegularPath: 
-       {
                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, nullptr, _T(""), GetDiffContext().m_bRecursive, (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? nullptr : pDoc);
-       }
-               // fall through (no break!)
+               GetMainFrame()->DoFileOrFolderOpen(&pathsParent, dwFlags, nullptr, _T(""), GetDiffContext().m_bRecursive, (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? nullptr : pDocOpen);
+               [[fallthrough]];
        case AllowUpwardDirectory::No:
                break;
        default:
@@ -1276,10 +1319,11 @@ bool CDirView::GetSelectedItems(int * sel1, int * sel2, int * sel3)
 
 /**
  * @brief Open special items (parent folders etc).
+ * @param [in] pDoc Pointer to CDirDoc object.
  * @param [in] pos1 First item position.
  * @param [in] pos2 Second item position.
  */
-void CDirView::OpenSpecialItems(DIFFITEM *pos1, DIFFITEM *pos2, DIFFITEM *pos3)
+void CDirView::OpenSpecialItems(CDirDoc *pDoc, DIFFITEM *pos1, DIFFITEM *pos2, DIFFITEM *pos3)
 {
        if (pos2==nullptr && pos3==nullptr)
        {
@@ -1287,7 +1331,7 @@ void CDirView::OpenSpecialItems(DIFFITEM *pos1, DIFFITEM *pos2, DIFFITEM *pos3)
                // SPECIAL_ITEM_POS is position for
                // special items, but there is currenly
                // only one (parent folder)
-               OpenParentDirectory();
+               OpenParentDirectory(pDoc);
        }
        else
        {
@@ -1314,7 +1358,7 @@ static bool CreateFoldersPair(const PathContext& paths)
                                strutils::format_string1( 
                                        _("The folder exists only in other side and cannot be opened.\n\nDo you want to create a matching folder:\n%1\nto the other side and open these folders?"),
                                        path);
-                       int res = AfxMessageBox(message.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN);
+                       int res = AfxMessageBox(message.c_str(), MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_CREATE_PAIR_FOLDER);
                        if (res == IDYES)
                                created = paths::CreateIfNeeded(path);
                }
@@ -1322,6 +1366,67 @@ static bool CreateFoldersPair(const PathContext& paths)
        return created;
 }
 
+void CDirView::Open(CDirDoc *pDoc, const PathContext& paths, DWORD dwFlags[3], FileTextEncoding encoding[3], PackingInfo * infoUnpacker)
+{
+       bool isdir = false;
+       for (auto path : paths)
+       {
+               if (paths::DoesPathExist(path) == paths::IS_EXISTING_DIR)
+                       isdir = true;
+       }
+       if (isdir)
+       {
+               // Open subfolders
+               // Don't add folders to MRU
+               GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, nullptr, _T(""), GetDiffContext().m_bRecursive,
+                       ((GetAsyncKeyState(VK_CONTROL) & 0x8000) || GetDiffContext().m_bRecursive) ? nullptr : pDoc);
+       }
+       else if (HasZipSupport() && std::count_if(paths.begin(), paths.end(), ArchiveGuessFormat) == paths.GetSize())
+       {
+               // Open archives, not adding paths to MRU
+               GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, nullptr, _T(""), GetDiffContext().m_bRecursive, nullptr, infoUnpacker, nullptr);
+       }
+       else
+       {
+               // Regular file case
+
+               // Binary attributes are set after files are unpacked
+               // so after plugins such as the MS-Office plugins have had a chance to make them textual
+               // We haven't done unpacking yet in this diff, but if a binary flag is already set,
+               // then it was set in a previous diff after unpacking, so we trust it
+
+               // Open identical and different files
+               PathContext filteredPaths;
+               FileLocation fileloc[3];
+               String strDesc[3];
+               const String sUntitled[] = { _("Untitled left"), paths.GetSize() < 3 ? _("Untitled right") : _("Untitled middle"), _("Untitled right") };
+               for (int i = 0; i < paths.GetSize(); ++i)
+               {
+                       if (paths::DoesPathExist(paths[i]) == paths::DOES_NOT_EXIST)
+                       {
+                               strDesc[i] = sUntitled[i];
+                               filteredPaths.SetPath(i, _T("NUL"), false);
+                       }
+                       else
+                       {
+                               fileloc[i].setPath(paths[i]);
+                               fileloc[i].encoding = encoding[i];
+                               filteredPaths.SetPath(i, paths[i], false);
+                       }
+               }
+
+               if (!infoUnpacker)
+               {
+                       PrediffingInfo* infoPrediffer = nullptr;
+                       String filteredFilenames = CDiffContext::GetFilteredFilenames(filteredPaths);
+                       GetDiffContext().FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
+               }
+
+               GetMainFrame()->ShowAutoMergeDoc(0, GetDocument(), paths.GetSize(), fileloc,
+                       dwFlags, strDesc, _T(""), infoUnpacker);
+       }
+}
+
 /**
  * @brief Open selected files or directories.
  *
@@ -1332,10 +1437,9 @@ static bool CreateFoldersPair(const PathContext& paths)
  * This handles the case that one item is selected
  * and the case that two items are selected (one on each side)
  */
-void CDirView::OpenSelection(SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMAL*/, PackingInfo * infoUnpacker /*= nullptr*/)
+void CDirView::OpenSelection(CDirDoc *pDoc, SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMAL*/, PackingInfo * infoUnpacker /*= nullptr*/, bool openableForDir /*= true*/)
 {
        Merge7zFormatMergePluginScope scope(infoUnpacker);
-       CDirDoc * pDoc = GetDocument();
        const CDiffContext& ctxt = GetDiffContext();
 
        // First, figure out what was selected (store into pos1 & pos2)
@@ -1360,9 +1464,9 @@ void CDirView::OpenSelection(SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMA
 
        // Now handle the various cases of what was selected
 
-       if (pos1 == (DIFFITEM *)SPECIAL_ITEM_POS)
+       if (IsDiffItemSpecial(pos1))
        {
-               OpenSpecialItems(pos1, pos2, pos3);
+               OpenSpecialItems(pDoc, pos1, pos2, pos3);
                return;
        }
 
@@ -1371,19 +1475,20 @@ void CDirView::OpenSelection(SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMA
        const DIFFITEM *pdi[3] = {0}; // left & right items (di1==di2 if single selection)
        bool isdir = false; // set if we're comparing directories
        int nPane[3];
+       FileTextEncoding encoding[3];
        String errmsg;
        bool success;
        if (pos2 && !pos3)
                success = GetOpenTwoItems(ctxt, selectionType, pos1, pos2, pdi,
-                               paths, sel1, sel2, isdir, nPane, errmsg);
+                               paths, sel1, sel2, isdir, nPane, encoding, errmsg, openableForDir);
        else if (pos2 && pos3)
                success = GetOpenThreeItems(ctxt, pos1, pos2, pos3, pdi,
-                               paths, sel1, sel2, sel3, isdir, nPane, errmsg);
+                               paths, sel1, sel2, sel3, isdir, nPane, encoding, errmsg, openableForDir);
        else
        {
                // Only one item selected, so perform diff on its sides
                success = GetOpenOneItem(ctxt, pos1, pdi, 
-                               paths, sel1, isdir, nPane, errmsg);
+                               paths, sel1, isdir, nPane, encoding, errmsg, openableForDir);
                if (isdir)
                        CreateFoldersPair(paths);
        }
@@ -1399,43 +1504,14 @@ void CDirView::OpenSelection(SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMA
 
        DWORD dwFlags[3];
        for (int nIndex = 0; nIndex < paths.GetSize(); nIndex++)
-               dwFlags[nIndex] = FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nPane[nIndex]) ? FFILEOPEN_READONLY : 0);
-       if (isdir)
-       {
-               // Open subfolders
-               // Don't add folders to MRU
-               GetMainFrame()->DoFileOpen(&paths, dwFlags, nullptr, _T(""), GetDiffContext().m_bRecursive, (GetAsyncKeyState(VK_CONTROL) & 0x8000) ? nullptr : pDoc);
-       }
-       else if (HasZipSupport() && std::count_if(paths.begin(), paths.end(), ArchiveGuessFormat) == paths.GetSize())
-       {
-               // Open archives, not adding paths to MRU
-               GetMainFrame()->DoFileOpen(&paths, dwFlags, nullptr, _T(""), GetDiffContext().m_bRecursive, nullptr, _T(""), infoUnpacker);
-       }
-       else
-       {
-               // Regular file case
-
-               // Binary attributes are set after files are unpacked
-               // so after plugins such as the MS-Office plugins have had a chance to make them textual
-               // We haven't done unpacking yet in this diff, but if a binary flag is already set,
-               // then it was set in a previous diff after unpacking, so we trust it
+               dwFlags[nIndex] = FFILEOPEN_NOMRU | (GetDocument()->GetReadOnly(nPane[nIndex]) ? FFILEOPEN_READONLY : 0);
 
-               // Open identical and different files
-               FileLocation fileloc[3];
-               String strDesc[3];
-               const String sUntitled[] = { _("Untitled left"), paths.GetSize() < 3 ? _("Untitled right") : _("untitled middle"), _("Untitled right") };
-               for (int i = 0; i < paths.GetSize(); ++i)
-               {
-                       if (!pdi[0]->diffcode.exists(i) &&
-                               std::count(pdi, pdi + paths.GetSize(), pdi[0]) == paths.GetSize())
-                               strDesc[i] = sUntitled[i];
-                       else
-                               fileloc[i].setPath(paths[i]);
-               }
+       Open(pDoc, paths, dwFlags, encoding, infoUnpacker);
+}
 
-               GetMainFrame()->ShowAutoMergeDoc(pDoc, paths.GetSize(), fileloc,
-                       dwFlags, strDesc, _T(""), infoUnpacker);
-       }
+void CDirView::OpenSelection(SELECTIONTYPE selectionType /*= SELECTIONTYPE_NORMAL*/, PackingInfo* infoUnpacker /*= nullptr*/, bool openableForDir /*= true*/)
+{
+       OpenSelection(GetDocument(), selectionType, infoUnpacker, openableForDir);
 }
 
 void CDirView::OpenSelectionAs(UINT id)
@@ -1443,12 +1519,12 @@ void CDirView::OpenSelectionAs(UINT id)
        CDirDoc * pDoc = GetDocument();
        const CDiffContext& ctxt = GetDiffContext();
 
-       // First, figure out what was selected (store into pos1 & pos2)
-       DIFFITEM *pos1 = nullptr, *pos2 = nullptr;
+       // First, figure out what was selected (store into pos1 & pos2 & pos3)
+       DIFFITEM *pos1 = nullptr, *pos2 = nullptr, *pos3 = nullptr;
        int sel1 = -1, sel2 = -1, sel3 = -1;
        if (!GetSelectedItems(&sel1, &sel2, &sel3))
        {
-               // Must have 1 or 2 items selected
+               // Must have 1 or 2 or 3 items selected
                // Not valid action
                return;
        }
@@ -1456,11 +1532,16 @@ void CDirView::OpenSelectionAs(UINT id)
        pos1 = GetItemKey(sel1);
        ASSERT(pos1);
        if (sel2 != -1)
+       {
                pos2 = GetItemKey(sel2);
+               ASSERT(pos2 != nullptr);
+               if (sel3 != -1)
+                       pos3 = GetItemKey(sel3);
+       }
 
        // Now handle the various cases of what was selected
 
-       if (pos1 == (DIFFITEM *)SPECIAL_ITEM_POS)
+       if (IsDiffItemSpecial(pos1))
        {
                ASSERT(false);
                return;
@@ -1471,16 +1552,20 @@ void CDirView::OpenSelectionAs(UINT id)
        const DIFFITEM *pdi[3]; // left & right items (di1==di2 if single selection)
        bool isdir = false; // set if we're comparing directories
        int nPane[3];
+       FileTextEncoding encoding[3];
        String errmsg;
        bool success;
-       if (pos2)
+       if (pos2 && !pos3)
                success = GetOpenTwoItems(ctxt, SELECTIONTYPE_NORMAL, pos1, pos2, pdi,
-                               paths, sel1, sel2, isdir, nPane, errmsg);
+                               paths, sel1, sel2, isdir, nPane, encoding, errmsg, false);
+       else if (pos2 && pos3)
+               success = GetOpenThreeItems(ctxt, pos1, pos2, pos3, pdi,
+                               paths, sel1, sel2, sel3, isdir, nPane, encoding, errmsg, false);
        else
        {
                // Only one item selected, so perform diff on its sides
                success = GetOpenOneItem(ctxt, pos1, pdi,
-                               paths, sel1, isdir, nPane, errmsg);
+                               paths, sel1, isdir, nPane, encoding, errmsg, false);
        }
        if (!success)
        {
@@ -1490,17 +1575,40 @@ void CDirView::OpenSelectionAs(UINT id)
        }
 
        // Open identical and different files
+       const String sUntitled[] = { _("Untitled left"), paths.GetSize() < 3 ? _("Untitled right") : _("Untitled middle"), _("Untitled right") };
        DWORD dwFlags[3] = { 0 };
+       String strDesc[3];
+       PathContext filteredPaths;
        FileLocation fileloc[3];
        for (int pane = 0; pane < paths.GetSize(); pane++)
        {
-               fileloc[pane].setPath(paths[pane]);
+               if (paths::DoesPathExist(paths[pane]) == paths::DOES_NOT_EXIST)
+               {
+                       strDesc[pane] = sUntitled[pane];
+                       filteredPaths.SetPath(pane, _T("NUL"), false);
+               }
+               else
+               {
+                       fileloc[pane].setPath(paths[pane]);
+                       fileloc[pane].encoding = encoding[pane];
+                       filteredPaths.SetPath(pane, paths[pane]);
+               }
                dwFlags[pane] |= FFILEOPEN_NOMRU | (pDoc->GetReadOnly(nPane[pane]) ? FFILEOPEN_READONLY : 0);
        }
-       if (id == ID_MERGE_COMPARE_HEX)
-               GetMainFrame()->ShowHexMergeDoc(pDoc, paths.GetSize(), fileloc, dwFlags, nullptr);
+       if (ID_UNPACKERS_FIRST <= id && id <= ID_UNPACKERS_LAST)
+       {
+               PackingInfo infoUnpacker(
+                               CMainFrame::GetPluginPipelineByMenuId(id, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
+               GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
+                       ctxt.m_bRecursive, nullptr, &infoUnpacker, nullptr, 0);
+       }
        else
-               GetMainFrame()->ShowImgMergeDoc(pDoc, paths.GetSize(), fileloc, dwFlags, nullptr);
+       {
+               PackingInfo* infoUnpacker = nullptr;
+               PrediffingInfo* infoPrediffer = nullptr;
+               GetDiffContext().FetchPluginInfos(CDiffContext::GetFilteredFilenames(filteredPaths), &infoUnpacker, &infoPrediffer);
+               GetMainFrame()->ShowMergeDoc(id, pDoc, paths.GetSize(), fileloc, dwFlags, strDesc, _T(""), infoUnpacker);
+       }
 }
 
 /// User chose (context menu) delete left
@@ -1559,13 +1667,32 @@ void CDirView::OnUpdateCtxtDirCopyBothDiffsOnlyTo(CCmdUI* pCmdUI)
 }
        
 /**
+ * @brief Update "Copy | Left/Right/Both " item
+ */
+template<SIDE_TYPE stype>
+void CDirView::OnUpdateCtxtDirCopy2(CCmdUI* pCmdUI)
+{
+       Counts counts = Count(&DirActions::IsItemCopyableToOn<stype>);
+       pCmdUI->Enable(counts.count > 0);
+       pCmdUI->SetText(FormatMenuItemString(stype, counts.count, counts.total).c_str());
+}
+
+void CDirView::OnUpdateCtxtDirCopyBoth2(CCmdUI* pCmdUI)
+{
+       Counts counts = Count(&DirActions::IsItemCopyableBothToOn);
+       pCmdUI->Enable(counts.count > 0);
+       pCmdUI->SetText(FormatMenuItemStringAll(GetDocument()->m_nDirs, counts.count, counts.total).c_str());
+}
+
+/**
  * @brief Get keydata associated with item in given index.
  * @param [in] idx Item's index to list in UI.
  * @return Key for item in given index.
  */
 DIFFITEM *CDirView::GetItemKey(int idx) const
 {
-       return (DIFFITEM *) m_pList->GetItemData(idx);
+       ASSERT(idx >= 0 && idx < static_cast<int>(m_listViewItems.size()));
+       return reinterpret_cast<DIFFITEM*>(m_listViewItems[idx].lParam);
 }
 
 // SetItemKey & GetItemKey encapsulate how the display list items
@@ -1591,7 +1718,7 @@ DIFFITEM &CDirView::GetDiffItem(int sel)
        DIFFITEM *diffpos = GetItemKey(sel);
 
        // If it is special item, return empty DIFFITEM
-       if (diffpos == (DIFFITEM *)SPECIAL_ITEM_POS)
+       if (IsDiffItemSpecial(diffpos))
        {
                return *DIFFITEM::GetEmptyItem();
        }
@@ -1600,20 +1727,45 @@ DIFFITEM &CDirView::GetDiffItem(int sel)
 
 void CDirView::DeleteItem(int sel, bool removeDIFFITEM)
 {
+       DIFFITEM *diffpos = GetItemKey(sel);
+       if (IsDiffItemSpecial(diffpos))
+               return;
        if (m_bTreeMode)
+       {
                CollapseSubdir(sel);
-       if (removeDIFFITEM)
+               m_listViewItems.erase(m_listViewItems.begin() + sel);
+               m_pList->DeleteItem(sel);
+       }
+       else if (GetDiffContext().m_bRecursive || diffpos->HasChildren())
        {
-               DIFFITEM *diffpos = GetItemKey(sel);
-               if (diffpos != (DIFFITEM *)SPECIAL_ITEM_POS)
+               DirItemIterator it;
+               for (it = RevBegin(); it != RevEnd(); )
                {
-                       if (diffpos->HasChildren())
-                               diffpos->RemoveChildren();
-                       diffpos->DelinkFromSiblings();
-                       delete diffpos;
+                       DIFFITEM& di = *it;
+                       int cursel = it.m_sel;
+                       ++it;
+                       if (di.IsAncestor(diffpos) || diffpos == &di)
+                       {
+                               m_listViewItems.erase(m_listViewItems.begin() + cursel);
+                               m_pList->DeleteItem(cursel);
+                       }
                }
        }
-       m_pList->DeleteItem(sel);
+       else
+       {
+               m_listViewItems.erase(m_listViewItems.begin() + sel);
+               m_pList->DeleteItem(sel);
+       }
+       if (removeDIFFITEM)
+       {
+               if (diffpos->HasChildren())
+                       diffpos->RemoveChildren();
+               diffpos->DelinkFromSiblings();
+               delete diffpos;
+       }
+
+       m_firstDiffItem.reset();
+       m_lastDiffItem.reset();
 }
 
 void CDirView::DeleteAllDisplayItems()
@@ -1621,6 +1773,10 @@ void CDirView::DeleteAllDisplayItems()
        // item data are just positions (diffposes)
        // that is, they contain no memory needing to be freed
        m_pList->DeleteAllItems();
+       m_listViewItems.clear();
+
+       m_firstDiffItem.reset();
+       m_lastDiffItem.reset();
 }
 
 /**
@@ -1629,11 +1785,12 @@ void CDirView::DeleteAllDisplayItems()
  */
 int CDirView::GetItemIndex(DIFFITEM *key)
 {
-       LVFINDINFO findInfo;
-
-       findInfo.flags = LVFI_PARAM;  // Search for itemdata
-       findInfo.lParam = (LPARAM)key;
-       return m_pList->FindItem(&findInfo);
+       for (size_t i = 0; i < m_listViewItems.size(); ++i)
+       {
+               if (m_listViewItems[i].lParam == reinterpret_cast<LPARAM>(key))
+                       return static_cast<int>(i);
+       }
+       return 0;
 }
 
 /**
@@ -1643,7 +1800,7 @@ int CDirView::GetItemIndex(DIFFITEM *key)
 void CDirView::GetItemFileNames(int sel, String& strLeft, String& strRight) const
 {
        DIFFITEM *diffpos = GetItemKey(sel);
-       if (diffpos == (DIFFITEM *)SPECIAL_ITEM_POS)
+       if (IsDiffItemSpecial(diffpos))
        {
                strLeft.erase();
                strRight.erase();
@@ -1662,7 +1819,7 @@ void CDirView::GetItemFileNames(int sel, String& strLeft, String& strRight) cons
 void CDirView::GetItemFileNames(int sel, PathContext * paths) const
 {
        DIFFITEM *diffpos = GetItemKey(sel);
-       if (diffpos == (DIFFITEM *)SPECIAL_ITEM_POS)
+       if (IsDiffItemSpecial(diffpos))
        {
                for (int nIndex = 0; nIndex < GetDocument()->m_nDirs; nIndex++)
                        paths->SetPath(nIndex, _T(""));
@@ -1689,11 +1846,7 @@ void CDirView::DoOpen(SIDE_TYPE stype)
        DirItemIterator dirBegin = SelBegin();
        String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
        if (file.empty()) return;
-       HINSTANCE rtn = ShellExecute(::GetDesktopWindow(), _T("edit"), file.c_str(), 0, 0, SW_SHOWNORMAL);
-       if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
-               rtn = ShellExecute(::GetDesktopWindow(), _T("open"), file.c_str(), 0, 0, SW_SHOWNORMAL);
-       if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
-               DoOpenWith(stype);
+       shell::Edit(file.c_str());
 }
 
 /// Open with dialog for file on selected side
@@ -1704,11 +1857,7 @@ void CDirView::DoOpenWith(SIDE_TYPE stype)
        DirItemIterator dirBegin = SelBegin();
        String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
        if (file.empty()) return;
-       CString sysdir;
-       if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH)) return;
-       sysdir.ReleaseBuffer();
-       CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + file.c_str();
-       ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg, sysdir, SW_SHOWNORMAL);
+       shell::OpenWith(file.c_str());
 }
 
 /// Open selected file  on specified side to external editor
@@ -1720,7 +1869,7 @@ void CDirView::DoOpenWithEditor(SIDE_TYPE stype)
        String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
        if (file.empty()) return;
 
-       theApp.OpenFileToExternalEditor(file);
+       CMergeApp::OpenFileToExternalEditor(file);
 }
 
 void CDirView::DoOpenParentFolder(SIDE_TYPE stype)
@@ -1730,8 +1879,7 @@ void CDirView::DoOpenParentFolder(SIDE_TYPE stype)
        DirItemIterator dirBegin = SelBegin();
        String file = GetSelectedFileName(dirBegin, stype, GetDiffContext());
        if (file.empty()) return;
-       String parentFolder = paths::GetParentPath(file);
-       ShellExecute(::GetDesktopWindow(), _T("open"), parentFolder.c_str(), 0, 0, SW_SHOWNORMAL);
+       shell::OpenParentFolder(file.c_str());
 }
 
 /// User chose (context menu) open left
@@ -1805,7 +1953,7 @@ void CDirView::OnUpdateCtxtDirOpenParentFolder(CCmdUI* pCmdUI)
 }
 
 // Used for Open
-void CDirView::DoUpdateOpen(SELECTIONTYPE selectionType, CCmdUI* pCmdUI)
+void CDirView::DoUpdateOpen(SELECTIONTYPE selectionType, CCmdUI* pCmdUI, bool openableForDir /*= true*/)
 {
        int sel1 = -1, sel2 = -1, sel3 = -1;
        if (!GetSelectedItems(&sel1, &sel2, &sel3))
@@ -1822,13 +1970,22 @@ void CDirView::DoUpdateOpen(SELECTIONTYPE selectionType, CCmdUI* pCmdUI)
                        pCmdUI->Enable(FALSE);
                        return;
                }
+               if (!openableForDir)
+               {
+                       const DIFFITEM& di1 = GetDiffItem(sel1);
+                       if (di1.diffcode.isDirectory() || IsDiffItemSpecial(GetItemKey(sel1)))
+                       {
+                               pCmdUI->Enable(FALSE);
+                               return;
+                       }
+               }
        }
        else if (sel3 == -1)
        {
                // Two items selected
                const DIFFITEM& di1 = GetDiffItem(sel1);
                const DIFFITEM& di2 = GetDiffItem(sel2);
-               if (!AreItemsOpenable(GetDiffContext(), selectionType, di1, di2))
+               if (!AreItemsOpenable(GetDiffContext(), selectionType, di1, di2, openableForDir))
                {
                        pCmdUI->Enable(FALSE);
                        return;
@@ -1840,7 +1997,7 @@ void CDirView::DoUpdateOpen(SELECTIONTYPE selectionType, CCmdUI* pCmdUI)
                const DIFFITEM& di1 = GetDiffItem(sel1);
                const DIFFITEM& di2 = GetDiffItem(sel2);
                const DIFFITEM& di3 = GetDiffItem(sel3);
-               if (selectionType != SELECTIONTYPE_NORMAL || !::AreItemsOpenable(GetDiffContext(), di1, di2, di3))
+               if (selectionType != SELECTIONTYPE_NORMAL || !::AreItemsOpenable(GetDiffContext(), di1, di2, di3, openableForDir))
                {
                        pCmdUI->Enable(FALSE);
                        return;
@@ -1966,6 +2123,123 @@ void CDirView::OpenPrevDiff()
        }
 }
 
+void CDirView::OpenFirstFile()
+{
+       int currentInd = GetFocusedItem();
+       int firstFileInd = 0;
+       // Skip directories
+       while (firstFileInd <= currentInd)
+       {
+               DIFFITEM& dip = GetDiffItem(firstFileInd);
+               if (!dip.diffcode.isDirectory())
+               {
+                       MoveFocus(currentInd, firstFileInd, 1);
+                       OpenSelection();
+                       break;
+               }               
+               firstFileInd++;
+       }
+}
+
+bool CDirView::IsFirstFile()
+{
+       int currentInd = GetFocusedItem();
+       int firstFileInd = 0;
+       while (firstFileInd <= currentInd)
+       {
+               DIFFITEM& dip = GetDiffItem(firstFileInd);
+               if (!dip.diffcode.isDirectory())
+               {
+                       if (currentInd == firstFileInd)
+                               return true;
+                       else
+                               return false;
+               }
+               firstFileInd++;
+       }
+       return false;
+}
+
+void CDirView::OpenLastFile()
+{
+       const int count = m_pList->GetItemCount();
+       int currentInd = GetFocusedItem();
+       int lastFileInd = count - 1;
+       // Skip directories
+       while (lastFileInd >= 0)
+       {
+               DIFFITEM& dip = GetDiffItem(lastFileInd);
+               if (!dip.diffcode.isDirectory())
+               {
+                       MoveFocus(currentInd, lastFileInd, 1);
+                       OpenSelection();
+                       break;
+               }
+               lastFileInd--;
+       }
+}
+
+bool CDirView::IsLastFile()
+{
+       const int count = m_pList->GetItemCount();
+       int currentInd = GetFocusedItem();
+       int lastFileInd = count - 1;
+       while (lastFileInd >= currentInd)
+       {
+               DIFFITEM& dip = GetDiffItem(lastFileInd);
+               if (!dip.diffcode.isDirectory())
+               {
+                       if (currentInd == lastFileInd)
+                               return true;
+                       else
+                               return false;
+               }
+               lastFileInd--;
+       }
+       return false;
+}
+
+void CDirView::OpenNextFile()
+{
+       const int count = m_pList->GetItemCount();
+       int currentInd = GetFocusedItem();
+       int nextInd = currentInd + 1;
+       if (currentInd >= 0)
+       {
+               while (nextInd < count)
+               {
+                       DIFFITEM& dip = GetDiffItem(nextInd);
+                       MoveFocus(nextInd - 1, nextInd, 1);
+                       if (!dip.diffcode.isDirectory())
+                       {                               
+                               OpenSelection();
+                               break;
+                       }
+                       nextInd++;
+               }
+       }
+}
+
+void CDirView::OpenPrevFile()
+{
+       int currentInd = GetFocusedItem();
+       int prevInd = currentInd - 1;
+       if (currentInd >= 0)
+       {
+               while (prevInd >= 0)
+               {
+                       DIFFITEM& dip = GetDiffItem(prevInd);
+                       MoveFocus(prevInd + 1, prevInd, 1);
+                       if (!dip.diffcode.isDirectory())
+                       {
+                               OpenSelection();
+                               break;
+                       }
+                       prevInd--;
+               }
+       }
+}
+
 void CDirView::SetActivePane(int pane)
 {
        if (m_nActivePane >= 0)
@@ -2036,28 +2310,24 @@ int CDirView::GetFocusedItem()
 
 int CDirView::GetFirstDifferentItem()
 {
-       if (!m_bNeedSearchFirstDiffItem)
-               return m_firstDiffItem;
-
-       DirItemIterator it =
-               std::find_if(Begin(), End(), MakeDirActions(&DirActions::IsItemNavigableDiff));
-       m_firstDiffItem = it.m_sel;
-       m_bNeedSearchFirstDiffItem = false;
-
-       return m_firstDiffItem;
+       if (!m_firstDiffItem.has_value())
+       {
+               DirItemIterator it =
+                       std::find_if(Begin(), End(), MakeDirActions(&DirActions::IsItemNavigableDiff));
+               m_firstDiffItem = it.m_sel;
+       }
+       return m_firstDiffItem.value();
 }
 
 int CDirView::GetLastDifferentItem()
 {
-       if (!m_bNeedSearchLastDiffItem)
-               return m_lastDiffItem;
-
-       DirItemIterator it =
-               std::find_if(RevBegin(), RevEnd(), MakeDirActions(&DirActions::IsItemNavigableDiff));
-       m_lastDiffItem = it.m_sel;
-       m_bNeedSearchLastDiffItem = false;
-
-       return m_lastDiffItem;
+       if (!m_lastDiffItem.has_value())
+       {
+               DirItemIterator it =
+                       std::find_if(RevBegin(), RevEnd(), MakeDirActions(&DirActions::IsItemNavigableDiff));
+               m_lastDiffItem = it.m_sel;
+       }
+       return m_lastDiffItem.value();
 }
 
 /**
@@ -2075,6 +2345,7 @@ void CDirView::MoveFocus(int currentInd, int i, int selCount)
                m_pList->SetItemState(currentInd, 0, LVIS_SELECTED);
                m_pList->SetItemState(currentInd, 0, LVIS_FOCUSED);
                m_pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
+               m_pList->SetSelectionMark(i);
        }
 
        // Move focus to specified item
@@ -2092,7 +2363,7 @@ CDirFrame * CDirView::GetParentFrame()
 {
        // can't verify cast without introducing more coupling
        // (CDirView doesn't include DirFrame.h)
-       return static_cast<CDirFrame *>(CListView::GetParentFrame());
+       return static_cast<CDirFrame *>(__super::GetParentFrame());
 }
 
 void CDirView::OnRefresh()
@@ -2111,13 +2382,13 @@ BOOL CDirView::PreTranslateMessage(MSG* pMsg)
                        // Check if we got 'ESC pressed' -message
                        if (pMsg->wParam == VK_ESCAPE)
                        {
-                               if (m_pCmpProgressBar != nullptr)
+                               if (GetDocument()->m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPARING)
                                {
-                                       OnBnClickedComparisonStop();
+                                       GetDocument()->AbortCurrentScan();
                                        return TRUE;
                                }
 
-                               if (m_bEscCloses)
+                               if (m_nEscCloses != 0)
                                {
                                        AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_FILE_CLOSE);
                                        return FALSE;
@@ -2135,7 +2406,7 @@ BOOL CDirView::PreTranslateMessage(MSG* pMsg)
                        {
                                if (!GetDiffContext().m_bRecursive)
                                {
-                                       OpenParentDirectory();
+                                       OpenParentDirectory(GetDocument());
                                        return FALSE;
                                }
                                else if (m_bTreeMode && sel >= 0)
@@ -2203,7 +2474,7 @@ BOOL CDirView::PreTranslateMessage(MSG* pMsg)
                        }
                }
        }
-       return CListView::PreTranslateMessage(pMsg);
+       return __super::PreTranslateMessage(pMsg);
 }
 
 void CDirView::OnUpdateRefresh(CCmdUI* pCmdUI)
@@ -2224,12 +2495,19 @@ LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
        CDirDoc * pDoc = GetDocument();
        ASSERT(pDoc != nullptr);
 
+       // Since the Collect thread deletes the DiffItems in the rescan by "Update selection",
+       // the UI update process should not be executed until the Collect thread process is completed 
+       // to avoid accessing the deleted DiffItem.
+       if (pDoc->m_diffThread.IsMarkedRescan() && pDoc->m_diffThread.GetCollectThreadState() != CDiffThread::THREAD_COMPLETED)
+       {
+               ASSERT(0);
+               return 0;       // return value unused
+       }
+
        if (wParam == CDiffThread::EVENT_COMPARE_COMPLETED)
        {
-               // Close and destroy the dialog after compare
-               if (m_pCmpProgressBar != nullptr)
-                       GetParentFrame()->ShowControlBar(m_pCmpProgressBar.get(), FALSE, FALSE);
-               m_pCmpProgressBar.reset();
+               if (pDoc->GetDiffContext().m_pPropertySystem && pDoc->GetDiffContext().m_pPropertySystem->HasHashProperties())
+                       pDoc->GetDiffContext().CreateDuplicateValueMap();
 
                pDoc->CompareReady();
 
@@ -2248,11 +2526,9 @@ LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
                        MoveFocus(0, 0, 0);
 
                // If compare took more than TimeToSignalCompare seconds, notify user
-               clock_t elapsed = clock() - m_compareStart;
-               GetParentFrame()->SetStatus(
-                       strutils::format(_("Elapsed time: %ld ms"), elapsed).c_str()
-               );
-               if (elapsed > TimeToSignalCompare * CLOCKS_PER_SEC)
+               m_elapsed = clock() - m_compareStart;
+               SetTimer(STATUSBAR_UPDATE, 150, nullptr);
+               if (m_elapsed > TimeToSignalCompare * CLOCKS_PER_SEC)
                        MessageBeep(IDOK);
                GetMainFrame()->StartFlashing();
        }
@@ -2275,12 +2551,13 @@ LRESULT CDirView::OnUpdateUIMessage(WPARAM wParam, LPARAM lParam)
                        else
                                Redisplay();
                }
+
+               HideItems(GetDiffContext().m_vCurrentlyHiddenItems);
        }
 
        return 0; // return value unused
 }
 
-
 BOOL CDirView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
 {
        NMHDR * hdr = reinterpret_cast<NMHDR *>(lParam);
@@ -2289,7 +2566,7 @@ BOOL CDirView::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
        if (hdr->code == HDN_BEGINDRAG)
                return OnHeaderBeginDrag((LPNMHEADER)hdr, pResult);
 
-       return CListView::OnNotify(wParam, lParam, pResult);
+       return __super::OnNotify(wParam, lParam, pResult);
 }
 
 BOOL CDirView::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
@@ -2307,7 +2584,7 @@ BOOL CDirView::OnChildNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* p
                        return TRUE;
                }
        }
-       return CListView::OnChildNotify(uMsg, wParam, lParam, pResult);
+       return __super::OnChildNotify(uMsg, wParam, lParam, pResult);
 }
 
 /**
@@ -2317,8 +2594,8 @@ bool CDirView::OnHeaderBeginDrag(LPNMHEADER hdr, LRESULT* pResult)
 {
        // save column widths before user reorders them
        // so we can reload them on the end drag
-       String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
-       theApp.WriteProfileString(secname.c_str(), _T("ColumnWidths"),
+       const String keyname = GetDocument()->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_WIDTHS : OPT_DIRVIEW3_COLUMN_WIDTHS;
+       GetOptionsMgr()->SaveOption(keyname,
                m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1)).c_str());
        return true;
 }
@@ -2408,18 +2685,28 @@ void CDirView::OnTimer(UINT_PTR nIDEvent)
                // Now redraw screen
                UpdateColumnNames();
                m_pColItems->LoadColumnWidths(
-                       (const TCHAR *)theApp.GetProfileString(GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3"), _T("ColumnWidths")),
-                       std::bind(&CListCtrl::SetColumnWidth, m_pList, _1, _2), DefColumnWidth);
+                       GetOptionsMgr()->GetString(GetDocument()->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_WIDTHS : OPT_DIRVIEW3_COLUMN_WIDTHS),
+                       std::bind(&CListCtrl::SetColumnWidth, m_pList, _1, _2), GetDefColumnWidth());
                Redisplay();
        }
        else if (nIDEvent == STATUSBAR_UPDATE)
        {
+               KillTimer(STATUSBAR_UPDATE);
                int items = GetSelectedCount();
-               String msg = (items == 1) ? _("1 item selected") : strutils::format_string1(_("%1 items selected"), strutils::to_str(items));
+               String msg;
+               if (m_elapsed != 0)
+               {
+                       msg = strutils::format(_("Elapsed time: %ld ms"), m_elapsed);
+                       m_elapsed = 0;
+               }
+               else
+               {
+                       msg = (items == 1) ? _("1 item selected") : strutils::format_string1(_("%1 items selected"), strutils::to_str(items));
+               }
                GetParentFrame()->SetStatus(msg.c_str());
        }
        
-       CListView::OnTimer(nIDEvent);
+       __super::OnTimer(nIDEvent);
 }
 
 /**
@@ -2487,26 +2774,28 @@ void CDirView::OnCustomizeColumns()
 {
        // Located in DirViewColHandler.cpp
        OnEditColumns();
-       String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
-       theApp.WriteProfileString(secname.c_str(), _T("ColumnOrders"), m_pColItems->SaveColumnOrders().c_str());
+       const String keyname = GetDocument()->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_ORDERS : OPT_DIRVIEW3_COLUMN_ORDERS;
+       GetOptionsMgr()->SaveOption(keyname, m_pColItems->SaveColumnOrders());
 }
 
-void CDirView::OnCtxtOpenWithUnpacker()
+void CDirView::OnOpenWithUnpacker()
 {
        int sel = -1;
        sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
        if (sel != -1)
        {
+               PackingInfo* infoUnpacker = nullptr;
+               PrediffingInfo* infoPrediffer = nullptr;
+               CDiffContext& ctxt = GetDiffContext();
+               String filteredFilenames = ctxt.GetFilteredFilenames(GetDiffItem(sel));
+               ctxt.FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
                // let the user choose a handler
-               CSelectUnpackerDlg dlg(GetDiffItem(sel).diffFileInfo[0].filename, this);
-               // create now a new infoUnpacker to initialize the manual/automatic flag
-               PackingInfo infoUnpacker(PLUGIN_AUTO);
-               dlg.SetInitialInfoHandler(&infoUnpacker);
-
+               CSelectPluginDlg dlg(infoUnpacker->GetPluginPipeline(), filteredFilenames,
+                       CSelectPluginDlg::PluginType::Unpacker, false, this);
                if (dlg.DoModal() == IDOK)
                {
-                       infoUnpacker = dlg.GetInfoHandler();
-                       OpenSelection(SELECTIONTYPE_NORMAL, &infoUnpacker);
+                       PackingInfo infoUnpackerNew(dlg.GetPluginPipeline());
+                       OpenSelection(SELECTIONTYPE_NORMAL, &infoUnpackerNew, false);
                }
        }
 
@@ -2528,6 +2817,11 @@ void CDirView::OnUpdateCtxtOpenWithUnpacker(CCmdUI* pCmdUI)
                int sel = -1;
                sel = m_pList->GetNextItem(sel, LVNI_SELECTED);
                const DIFFITEM& di = GetDiffItem(sel);
+               if (di.diffcode.isDirectory())
+               {
+                       pCmdUI->Enable(FALSE);
+                       return;
+               }
                pCmdUI->Enable(IsItemDeletableOnBoth(GetDiffContext(), di));
        }
 }
@@ -2550,7 +2844,8 @@ std::vector<String> CDirView::GetCurrentColRegKeys()
 struct FileCmpReport: public IFileCmpReport
 {
        explicit FileCmpReport(CDirView *pDirView) : m_pDirView(pDirView) {}
-       bool operator()(REPORT_TYPE nReportType, IListCtrl *pList, int nIndex, const String &sDestDir, String &sLinkPath)
+       ~FileCmpReport() override {}
+       bool operator()(REPORT_TYPE nReportType, IListCtrl *pList, int nIndex, const String &sDestDir, String &sLinkPath) override
        {
                const CDiffContext& ctxt = m_pDirView->GetDiffContext();
                const DIFFITEM &di = m_pDirView->GetDiffItem(nIndex);
@@ -2563,14 +2858,28 @@ struct FileCmpReport: public IFileCmpReport
                        return false;
                }
 
-               sLinkPath = di.diffFileInfo[0].GetFile();
+               sLinkPath = strutils::format(_T("%d_"), nIndex) + di.diffFileInfo[0].GetFile();
 
                strutils::replace(sLinkPath, _T("\\"), _T("_"));
                sLinkPath += _T(".html");
                String sReportPath = paths::ConcatPath(sDestDir, sLinkPath);
+               bool completed = false;
 
                m_pDirView->MoveFocus(m_pDirView->GetFirstSelectedInd(), nIndex, m_pDirView->GetSelectedCount());
-               m_pDirView->SendMessage(MSG_GENERATE_FLIE_COMPARE_REPORT, reinterpret_cast<WPARAM>(sReportPath.c_str()), 0);
+               m_pDirView->PostMessage(MSG_GENERATE_FLIE_COMPARE_REPORT,
+                       reinterpret_cast<WPARAM>(sReportPath.c_str()), 
+                       reinterpret_cast<LPARAM>(&completed));
+
+               while (!completed)
+               {
+                       MSG msg;
+                       while (::PeekMessage(&msg, nullptr, NULL, NULL, PM_NOREMOVE))
+                       {
+                               if (!AfxGetApp()->PumpMessage())
+                                       break;
+                       }
+                       Sleep(5);
+               }
 
                return true;
        }
@@ -2582,14 +2891,12 @@ private:
 LRESULT CDirView::OnGenerateFileCmpReport(WPARAM wParam, LPARAM lParam)
 {
        OpenSelection();
-       CFrameWnd * pFrame = GetMainFrame()->GetActiveFrame();
-       IMergeDoc * pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
-       if (pMergeDoc == nullptr)
-               pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
 
-       if (pMergeDoc != nullptr)
+       auto *pReportFileName = reinterpret_cast<const TCHAR *>(wParam);
+       bool *pCompleted = reinterpret_cast<bool *>(lParam);
+       if (IMergeDoc * pMergeDoc = GetMainFrame()->GetActiveIMergeDoc())
        {
-               pMergeDoc->GenerateReport(reinterpret_cast<TCHAR *>(wParam));
+               pMergeDoc->GenerateReport(pReportFileName);
                pMergeDoc->CloseNow();
        }
        MSG msg;
@@ -2597,6 +2904,7 @@ LRESULT CDirView::OnGenerateFileCmpReport(WPARAM wParam, LPARAM lParam)
                if (!AfxGetApp()->PumpMessage())
                        break;
        GetMainFrame()->OnUpdateFrameTitle(FALSE);
+       *pCompleted = true;
        return 0;
 }
 
@@ -2628,7 +2936,7 @@ void CDirView::OnToolsGenerateReport()
        pReport->SetRootPaths(paths);
        pReport->SetColumns(m_pColItems->GetDispColCount());
        pReport->SetFileCmpReport(new FileCmpReport(this));
-       pReport->SetList(new IListCtrlImpl(m_pList->m_hWnd));
+       pReport->SetList(new IListCtrlImpl(m_pList->m_hWnd, m_listViewItems));
        pReport->SetReportType(dlg.m_nReportType);
        pReport->SetReportFile(dlg.m_sReportFile);
        pReport->SetCopyToClipboard(dlg.m_bCopyToClipboard);
@@ -2660,12 +2968,6 @@ void CDirView::OnToolsGeneratePatch()
                                MB_DONT_DISPLAY_AGAIN, IDS_CANNOT_CREATE_BINARYPATCH);
                        bValidFiles = false;
                }
-               else if (item.diffcode.isDirectory())
-               {
-                       LangMessageBox(IDS_CANNOT_CREATE_DIRPATCH, MB_ICONWARNING |
-                               MB_DONT_DISPLAY_AGAIN, IDS_CANNOT_CREATE_DIRPATCH);
-                       bValidFiles = false;
-               }
 
                if (bValidFiles)
                {
@@ -2711,11 +3013,11 @@ int CDirView::AddSpecialItems()
        {
        case AllowUpwardDirectory::No:
                bEnable = false;
-               // fall through
+               [[fallthrough]];
        default:
                AddParentFolderItem(bEnable);
                retVal = 1;
-               // fall through
+               [[fallthrough]];
        case AllowUpwardDirectory::Never:
                break;
        }
@@ -2808,7 +3110,7 @@ void CDirView::OnSelectAll()
                {
                        // Don't select special items (SPECIAL_ITEM_POS)
                        DIFFITEM *diffpos = GetItemKey(i);
-                       if (diffpos != (DIFFITEM *)SPECIAL_ITEM_POS)
+                       if (!IsDiffItemSpecial(diffpos))
                                m_pList->SetItemState(i, LVIS_SELECTED, LVIS_SELECTED);
                }
        }
@@ -2826,16 +3128,44 @@ void CDirView::OnUpdateSelectAll(CCmdUI* pCmdUI)
 /**
  * @brief Handle clicks in plugin context view in list
  */
-void CDirView::OnPluginPredifferMode(UINT nID)
+void CDirView::OnPluginSettings(UINT nID)
 {
-       ApplyPluginPrediffSetting(SelBegin(), SelEnd(), GetDiffContext(), 
-               (nID == ID_PREDIFF_AUTO) ? PLUGIN_AUTO : PLUGIN_MANUAL);
+       bool unpacker = (ID_UNPACKER_SETTINGS_NONE <= nID && nID <= ID_UNPACKER_SETTINGS_SELECT);
+       String pluginPipeline;
+       switch (nID)
+       {
+       case ID_PREDIFFER_SETTINGS_NONE:
+       case ID_UNPACKER_SETTINGS_NONE:
+               pluginPipeline.clear();
+               break;
+       case ID_PREDIFFER_SETTINGS_AUTO:
+       case ID_UNPACKER_SETTINGS_AUTO:
+               pluginPipeline = _T("<Automatic>");
+               break;
+       case ID_PREDIFFER_SETTINGS_SELECT:
+       case ID_UNPACKER_SETTINGS_SELECT:
+               int sel = m_pList->GetNextItem(-1, LVNI_SELECTED);
+               PackingInfo* infoUnpacker = nullptr;
+               PrediffingInfo* infoPrediffer = nullptr;
+               CDiffContext& ctxt = GetDiffContext();
+               String filteredFilenames = ctxt.GetFilteredFilenames(GetDiffItem(sel));
+               ctxt.FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
+               GetDiffContext().FetchPluginInfos(filteredFilenames, &infoUnpacker, &infoPrediffer);
+               CSelectPluginDlg dlg(infoUnpacker->GetPluginPipeline(), filteredFilenames,
+                       unpacker ? CSelectPluginDlg::PluginType::Unpacker : CSelectPluginDlg::PluginType::Prediffer, false, this);
+               if (dlg.DoModal() != IDOK)
+                       return;
+               pluginPipeline = dlg.GetPluginPipeline();
+               break;
+       }
+       ApplyPluginPipeline(SelBegin(), SelEnd(), GetDiffContext(), unpacker, pluginPipeline);
+       Invalidate();
 }
 
 /**
  * @brief Updates just before displaying plugin context view in list
  */
-void CDirView::OnUpdatePluginPredifferMode(CCmdUI* pCmdUI)
+void CDirView::OnUpdatePluginMode(CCmdUI* pCmdUI)
 {
        // 2004-04-03, Perry
        // CMainFrame::OnUpdatePluginUnpackMode handles this for global unpacking
@@ -2850,10 +3180,12 @@ void CDirView::OnUpdatePluginPredifferMode(CCmdUI* pCmdUI)
        if (pPopup == nullptr)
                return;
 
-       std::pair<int, int> counts = CountPredifferYesNo(SelBegin(), SelEnd(), GetDiffContext());
+       bool unpacker = (ID_UNPACKER_SETTINGS_NONE <= pCmdUI->m_nID && pCmdUI->m_nID <= ID_UNPACKER_SETTINGS_SELECT);
+       auto counts = CountPluginNoneAutoOthers(SelBegin(), SelEnd(), GetDiffContext(), unpacker);
 
-       CheckContextMenu(pPopup, ID_PREDIFF_AUTO, (counts.first > 0));
-       CheckContextMenu(pPopup, ID_PREDIFF_MANUAL, (counts.second > 0));
+       CheckContextMenu(pPopup, unpacker ? ID_UNPACKER_SETTINGS_NONE   : ID_PREDIFFER_SETTINGS_NONE,   (std::get<0>(counts) > 0));
+       CheckContextMenu(pPopup, unpacker ? ID_UNPACKER_SETTINGS_AUTO   : ID_PREDIFFER_SETTINGS_AUTO,   (std::get<1>(counts) > 0));
+       CheckContextMenu(pPopup, unpacker ? ID_UNPACKER_SETTINGS_SELECT : ID_PREDIFFER_SETTINGS_SELECT, (std::get<2>(counts) > 0));
 }
 
 /**
@@ -2861,10 +3193,12 @@ void CDirView::OnUpdatePluginPredifferMode(CCmdUI* pCmdUI)
  */
 void CDirView::RefreshOptions()
 {
-       m_bEscCloses = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
+       m_nEscCloses = GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC);
        m_bExpandSubdirs = GetOptionsMgr()->GetBool(OPT_DIRVIEW_EXPAND_SUBDIRS);
        Options::DirColors::Load(GetOptionsMgr(), m_cachedColors);
        m_bUseColors = GetOptionsMgr()->GetBool(OPT_DIRCLR_USE_COLORS);
+       m_pList->SetBkColor(m_bUseColors ? m_cachedColors.clrDirMargin : GetSysColor(COLOR_WINDOW));
+       Invalidate();
 }
 
 /**
@@ -2943,7 +3277,12 @@ void CDirView::OnItemRename()
 void CDirView::OnUpdateItemRename(CCmdUI* pCmdUI)
 {
        bool bEnabled = (1 == m_pList->GetSelectedCount());
-       pCmdUI->Enable(bEnabled && SelBegin() != SelEnd());
+       if (bEnabled)
+       {
+               Counts counts = Count(&DirActions::IsItemRenamable);
+               bEnabled = (counts.count > 0 && counts.total == 1);
+       }
+       pCmdUI->Enable(bEnabled);
 }
 
 /**
@@ -2951,15 +3290,64 @@ void CDirView::OnUpdateItemRename(CCmdUI* pCmdUI)
  */
 void CDirView::OnHideFilenames()
 {
+       CDiffContext& ctxt = GetDiffContext();
+       int selection_index;
+       String hiddden_item_path;
+       
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
        DirItemIterator it;
+
        while ((it = SelRevBegin()) != SelRevEnd())
        {
                DIFFITEM &di = *it;
+               selection_index = it.m_sel;
+               hiddden_item_path = di.getItemRelativePath();
                SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
-               DeleteItem(it.m_sel);
-               m_nHiddenItems++;
+               DeleteItem(selection_index);
+               ctxt.m_vCurrentlyHiddenItems.push_back(hiddden_item_path);
+       }
+       m_pList->SetRedraw(TRUE);       // Turn updating back on
+}
+
+/**
+ * @brief determine if an item-relative-path is contained in the list of items to hide
+ */
+bool CDirView::IsItemToHide(const String& currentItem, const std::vector<String>& ItemsToHide) const
+{
+       return std::find(ItemsToHide.begin(), ItemsToHide.end(), currentItem) != ItemsToHide.end();
+}
+
+/**
+ * @brief hides items specified in the .winmerge file
+ */
+
+void CDirView::HideItems(const std::vector<String>& ItemsToHide)
+{
+       CDiffContext& ctxt = GetDiffContext();
+       DIFFITEM *diffpos = ctxt.GetFirstDiffPosition();
+       while (diffpos != nullptr)
+       {
+               DIFFITEM &di = ctxt.GetNextDiffRefPosition(diffpos);
+               if (IsItemToHide(di.getItemRelativePath(), ItemsToHide))
+                       SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
+       }
+
+       m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
+
+       int num_hidden = 0;
+       const size_t num_to_hide = ItemsToHide.size();
+       DirItemIterator it = RevBegin();
+       while((num_hidden < num_to_hide) && (it != RevEnd()))
+       {
+               DIFFITEM& di = *it;
+               if (di.customFlags & ViewCustomFlags::HIDDEN)
+               {
+                       DeleteItem(it.m_sel);
+                       num_hidden++;
+               }
+               ++it;
        }
+
        m_pList->SetRedraw(TRUE);       // Turn updating back on
 }
 
@@ -2994,7 +3382,7 @@ void CDirView::OnUpdateCtxtDirMoveTo(CCmdUI* pCmdUI)
  */
 void CDirView::OnSize(UINT nType, int cx, int cy)
 {
-       CListView::OnSize(nType, cx, cy);
+       __super::OnSize(nType, cx, cy);
        GetDocument()->SetTitle(nullptr);
 }
 
@@ -3027,9 +3415,7 @@ void CDirView::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult)
        if ((pNMListView->uOldState & LVIS_SELECTED) !=
                        (pNMListView->uNewState & LVIS_SELECTED))
        {
-               int items = GetSelectedCount();
-               String msg = (items == 1) ? _("1 item selected") : strutils::format_string1(_("%1 items selected"), strutils::to_str(items));
-               GetParentFrame()->SetStatus(msg.c_str());
+               SetTimer(STATUSBAR_UPDATE, 100, nullptr);
        }
        *pResult = 0;
 }
@@ -3041,7 +3427,8 @@ void CDirView::OnItemChanged(NMHDR* pNMHDR, LRESULT* pResult)
  */
 afx_msg void CDirView::OnBeginLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
 {
-       *pResult = (SelBegin() == SelEnd());
+       Counts counts = Count(&DirActions::IsItemRenamable);
+       *pResult = !(counts.count > 0 && counts.total == 1);
 
        // If label edit is allowed.
        if (*pResult == FALSE)
@@ -3095,16 +3482,75 @@ afx_msg void CDirView::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
                CString sText;
                pEdit->GetWindowText(sText);
 
-               if (!sText.IsEmpty())
+               if (!sText.IsEmpty() && paths::IsValidName(String(sText)))
                {
                        try {
-                               DirItemIterator dirBegin = SelBegin();
-                               *pResult = DoItemRename(dirBegin, GetDiffContext(), String(sText));
+                               DirItemIterator it(m_pIList.get(), reinterpret_cast<NMLVDISPINFO *>(pNMHDR)->item.iItem);
+                               DIFFITEM& di = *it;
+                               unsigned sideFlags = (di.diffcode.diffcode & DIFFCODE::SIDEFLAGS);
+                               *pResult = DoItemRename(it, GetDiffContext(), String(sText));
+                               // Rescan the item if side flags change due to renaming.
+                               if (*pResult)
+                               {
+                                       if ((di.diffcode.diffcode & DIFFCODE::SIDEFLAGS) != sideFlags)
+                                       {
+                                               // Delete the item with the same file name as after renaming.
+                                               if (di.HasParent())
+                                               {
+                                                       for (DIFFITEM* pItem = di.GetParentLink()->GetFirstChild(); pItem != nullptr; pItem = pItem->GetFwdSiblingLink())
+                                                       {
+                                                               if ((pItem != &di) && (pItem->diffcode.isDirectory() == di.diffcode.isDirectory()) && (collstr(pItem->diffFileInfo[0].filename, di.diffFileInfo[0].filename, false) == 0))
+                                                               {
+                                                                       pItem->DelinkFromSiblings();
+                                                                       delete pItem;
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                               // Rescan the item.
+                                               MarkForRescan(di);
+                                               m_pSavedTreeState.reset(SaveTreeState(GetDiffContext()));
+                                               GetDocument()->SetMarkedRescan();
+                                               GetDocument()->Rescan();
+                                       }
+                                       else {
+                                               int nDirs = GetDiffContext().GetCompareDirs();
+                                               assert(nDirs == 2 || nDirs == 3);
+                                               UpdatePaths(nDirs, di);
+
+                                               int nIdx = reinterpret_cast<NMLVDISPINFO*>(pNMHDR)->item.iItem;
+                                               UpdateDiffItemStatus(nIdx);
+                                       }
+                               }
                        } catch (ContentsChangedException& e) {
                                AfxMessageBox(e.m_msg.c_str(), MB_ICONWARNING);
                        }
                }
+               else
+               {
+                       LangMessageBox(IDS_ERROR_INVALID_DIR_FILE_NAME, MB_ICONWARNING);
+               }
+       }
+}
+
+void CDirView::OnODFindItem(NMHDR* pNMHDR, LRESULT* pResult)
+{
+       NMLVFINDITEM* pFindItem = reinterpret_cast<NMLVFINDITEM*>(pNMHDR);
+       if (pFindItem->lvfi.flags & LVFI_STRING)
+       {
+               String text = strutils::makelower(pFindItem->lvfi.psz);
+               for (size_t i = pFindItem->iStart; i < m_listViewItems.size(); ++i)
+               {
+                       DIFFITEM *di = GetItemKey(static_cast<int>(i));
+                       String filename = strutils::makelower(di->diffFileInfo[0].filename);
+                       if (di && _tcsncmp(text.c_str(), filename.c_str(), text.length()) == 0)
+                       {
+                               *pResult = i;
+                               return;
+                       }
+               }
        }
+       *pResult = -1;
 }
 
 /**
@@ -3142,7 +3588,7 @@ void CDirView::OnUpdateStatusNum(CCmdUI* pCmdUI)
        {
                // Don't show number to special items
                DIFFITEM *pos = GetItemKey(focusItem);
-               if (pos != (DIFFITEM *)SPECIAL_ITEM_POS)
+               if (!IsDiffItemSpecial(pos))
                {
                        // If compare is non-recursive reduce special items count
                        bool bRecursive = GetDiffContext().m_bRecursive;
@@ -3164,8 +3610,9 @@ void CDirView::OnUpdateStatusNum(CCmdUI* pCmdUI)
  */
 void CDirView::OnViewShowHiddenItems()
 {
+       CDiffContext& ctxt = GetDiffContext();
        SetItemViewFlag(GetDiffContext(), ViewCustomFlags::VISIBLE, ViewCustomFlags::VISIBILITY);
-       m_nHiddenItems = 0;
+       ctxt.m_vCurrentlyHiddenItems.clear();
        Redisplay();
 }
 
@@ -3174,7 +3621,8 @@ void CDirView::OnViewShowHiddenItems()
  */
 void CDirView::OnUpdateViewShowHiddenItems(CCmdUI* pCmdUI)
 {
-       pCmdUI->Enable(m_nHiddenItems > 0);
+       const CDiffContext& ctxt = GetDiffContext();
+       pCmdUI->Enable(ctxt.m_vCurrentlyHiddenItems.size() > 0);
 }
 
 /**
@@ -3238,12 +3686,20 @@ void CDirView::OnUpdateViewCollapseAllSubdirs(CCmdUI* pCmdUI)
        pCmdUI->Enable(m_bTreeMode && GetDiffContext().m_bRecursive);
 }
 
+template <int pane1, int pane2>
 void CDirView::OnViewSwapPanes()
 {
-       GetDocument()->Swap(0, GetDocument()->m_nDirs - 1);
+       GetDocument()->Swap(pane1, pane2);
        Redisplay();
 }
 
+template <int pane1, int pane2>
+void CDirView::OnUpdateViewSwapPanes(CCmdUI* pCmdUI)
+{
+       pCmdUI->Enable(pane2 < GetDocument()->m_nDirs &&
+               GetDocument()->m_diffThread.GetThreadState() == CDiffThread::THREAD_COMPLETED);
+}
+
 /**
  * @brief Show/Hide different files/directories
  */
@@ -3446,35 +3902,62 @@ void CDirView::OnUpdateOptionsShowMissingRightOnly(CCmdUI* pCmdUI)
        pCmdUI->SetCheck(m_dirfilter.show_missing_right_only);
 }
 
-void CDirView::OnMergeCompare()
+void CDirView::OnMergeCompare(UINT nID)
 {
-       CWaitCursor waitstatus;
-       OpenSelection();
+       OpenSelection(nID == ID_MERGE_COMPARE ? GetDocument() : nullptr);
 }
 
 template<SELECTIONTYPE seltype>
 void CDirView::OnMergeCompare2()
 {
-       CWaitCursor waitstatus;
        OpenSelection(seltype);
 }
 
-void CDirView::OnMergeCompareXML()
+void CDirView::OnMergeCompareNonHorizontally()
 {
-       CWaitCursor waitstatus;
-       PackingInfo packingInfo(PLUGIN_BUILTIN_XML);
-       OpenSelection(SELECTIONTYPE_NORMAL, &packingInfo);
+       int sel1, sel2, sel3;
+       if (!GetSelectedItems(&sel1, &sel2, &sel3))
+               return;
+       DirSelectFilesDlg dlg;
+       if (sel1 != -1)
+               dlg.m_pdi[0] = &GetDiffItem(sel1);
+       if (sel2 != -1)
+               dlg.m_pdi[1] = &GetDiffItem(sel2);
+       if (sel3 != -1)
+               dlg.m_pdi[2] = &GetDiffItem(sel3);
+       if (dlg.DoModal() == IDOK && dlg.m_selectedButtons.size() > 0)
+       {
+               CDirDoc *pDoc = GetDocument();
+               FileTextEncoding encoding[3];
+               DWORD dwFlags[3] = {};
+               PathContext paths;
+               for (int nIndex = 0; nIndex < static_cast<int>(dlg.m_selectedButtons.size()); ++nIndex)
+               {
+                       int n = dlg.m_selectedButtons[nIndex];
+                       dwFlags[nIndex] = FFILEOPEN_NOMRU | (pDoc->GetReadOnly(n % 3) ? FFILEOPEN_READONLY : 0);
+                       if (dlg.m_pdi[n / 3])
+                       {
+                               paths.SetPath(nIndex, GetItemFileName(pDoc->GetDiffContext(), *dlg.m_pdi[n / 3], n % 3));
+                               encoding[nIndex] = dlg.m_pdi[n / 3]->diffFileInfo[n % 3].encoding;
+                       }
+               }
+               if (paths.GetSize() == 1)
+                       paths.SetRight(_T(""));
+               Open(GetDocument(), paths, dwFlags, encoding);
+       }
 }
 
 void CDirView::OnMergeCompareAs(UINT nID)
 {
-       CWaitCursor waitstatus;
        OpenSelectionAs(nID);
 }
 
 void CDirView::OnUpdateMergeCompare(CCmdUI *pCmdUI)
 {
-       DoUpdateOpen(SELECTIONTYPE_NORMAL, pCmdUI);
+       bool openableForDir = !((pCmdUI->m_nID >= ID_MERGE_COMPARE_TEXT && pCmdUI->m_nID <= ID_MERGE_COMPARE_WEBPAGE) ||
+               (pCmdUI->m_nID >= ID_UNPACKERS_FIRST && pCmdUI->m_nID <= ID_UNPACKERS_LAST));
+
+       DoUpdateOpen(SELECTIONTYPE_NORMAL, pCmdUI, openableForDir);
 }
 
 template<SELECTIONTYPE seltype>
@@ -3483,6 +3966,20 @@ void CDirView::OnUpdateMergeCompare2(CCmdUI *pCmdUI)
        DoUpdateOpen(seltype, pCmdUI);
 }
 
+void CDirView::OnUpdateNoUnpacker(CCmdUI *pCmdUI)
+{
+       pCmdUI->Enable();
+       pCmdUI->m_pMenu->DeleteMenu(pCmdUI->m_nID, MF_BYCOMMAND);
+
+       int sel = GetSingleSelectedItem();
+       if (sel == -1 || GetItemKey(sel) == reinterpret_cast<DIFFITEM *>(SPECIAL_ITEM_POS))
+               return;
+
+       String filteredFilenames = GetDiffContext().GetFilteredFilenames(*GetItemKey(sel));
+       CMainFrame::AppendPluginMenus(pCmdUI->m_pMenu, filteredFilenames,
+               FileTransform::UnpackerEventNames, true, ID_UNPACKERS_FIRST);
+}
+
 void CDirView::OnViewCompareStatistics()
 {
        CompareStatisticsDlg dlg(GetDocument()->GetCompareStats());
@@ -3627,7 +4124,6 @@ void CDirView::OnUpdateEditUndo(CCmdUI* pCmdUI)
        CEdit *pEdit = m_pList->GetEditControl();
        pCmdUI->Enable(pEdit && pEdit->CanUndo());
 }
-
 /**
  * @brief Returns CShellContextMenu object that owns given HMENU.
  *
@@ -3687,7 +4183,7 @@ LRESULT CDirView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
                pMenu->HandleMenuMessage(message, wParam, lParam, res);
        }
 
-       return CListView::WindowProc(message, wParam, lParam);
+       return __super::WindowProc(message, wParam, lParam);
 }
 
 /**
@@ -3725,27 +4221,6 @@ void CDirView::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
        }
 }
 
-void CDirView::OnBnClickedComparisonStop()
-{
-       if (m_pCmpProgressBar != nullptr)
-               m_pCmpProgressBar->EndUpdating();
-       GetDocument()->AbortCurrentScan();
-}
-
-void CDirView::OnBnClickedComparisonPause()
-{
-       if (m_pCmpProgressBar != nullptr)
-               m_pCmpProgressBar->SetPaused(true);
-       GetDocument()->PauseCurrentScan();
-}
-
-void CDirView::OnBnClickedComparisonContinue()
-{
-       if (m_pCmpProgressBar != nullptr)
-               m_pCmpProgressBar->SetPaused(false);
-       GetDocument()->ContinueCurrentScan();
-}
-
 /**
  * @brief Populate colors for items in view, depending on difference status
  */
@@ -3755,8 +4230,8 @@ void CDirView::GetColors (int nRow, int nCol, COLORREF& clrBk, COLORREF& clrText
 
        if (di.isEmpty())
        {
-               clrText = ::GetSysColor (COLOR_WINDOWTEXT);
-               clrBk = ::GetSysColor (COLOR_WINDOW);
+               clrText = theApp.GetMainSyntaxColors()->GetColor(COLORINDEX_NORMALTEXT);
+               clrBk = theApp.GetMainSyntaxColors()->GetColor(COLORINDEX_BKGND);
        }
        else if (di.diffcode.isResultFiltered())
        {
@@ -3780,8 +4255,8 @@ void CDirView::GetColors (int nRow, int nCol, COLORREF& clrBk, COLORREF& clrText
        }
        else
        {
-               clrText = ::GetSysColor (COLOR_WINDOWTEXT);
-               clrBk = ::GetSysColor (COLOR_WINDOW);
+               clrText = theApp.GetMainSyntaxColors()->GetColor(COLORINDEX_NORMALTEXT);
+               clrBk = theApp.GetMainSyntaxColors()->GetColor(COLORINDEX_BKGND);
        }
 }
 
@@ -3790,15 +4265,18 @@ void CDirView::OnSearch()
        CDirDoc *pDoc = GetDocument();
        m_pList->SetRedraw(FALSE);      // Turn off updating (better performance)
        int nRows = m_pList->GetItemCount();
+       CDiffContext& ctxt = GetDiffContext();
+
        for (int currRow = nRows - 1; currRow >= 0; currRow--)
        {
                DIFFITEM *pos = GetItemKey(currRow);
-               if (pos == (DIFFITEM *)SPECIAL_ITEM_POS)
+               if (IsDiffItemSpecial(pos))
                        continue;
 
                bool bFound = false;
                DIFFITEM &di = GetDiffItem(currRow);
                PathContext paths;
+
                for (int i = 0; i < pDoc->m_nDirs; i++)
                {
                        if (di.diffcode.exists(i) && !di.diffcode.isDirectory())
@@ -3835,9 +4313,10 @@ void CDirView::OnSearch()
                }
                if (!bFound)
                {
+                       String hiddden_item_path = di.getItemRelativePath();
                        SetItemViewFlag(di, ViewCustomFlags::HIDDEN, ViewCustomFlags::VISIBILITY);
                        DeleteItem(currRow);
-                       m_nHiddenItems++;
+                       ctxt.m_vCurrentlyHiddenItems.push_back(hiddden_item_path);
                }
        }
        m_pList->SetRedraw(TRUE);       // Turn updating back on
@@ -3848,8 +4327,6 @@ void CDirView::OnSearch()
  */
 void CDirView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult) 
 {
-       COleDataSource *DropData = new COleDataSource();
-
        std::list<String> list;
        CopyPathnamesForDragAndDrop(SelBegin(), SelEnd(), std::back_inserter(list), GetDiffContext());
        String filesForDroping = strutils::join(list.begin(), list.end(), _T("\n")) + _T("\n");
@@ -3860,11 +4337,8 @@ void CDirView::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
        HGLOBAL hMem = GlobalReAlloc(file.Detach(), (filesForDroping.length() + 1) * sizeof(TCHAR), 0);
        if (hMem != nullptr) 
        {
-#ifdef _UNICODE
+               COleDataSource* DropData = new COleDataSource();
                DropData->CacheGlobalData(CF_UNICODETEXT, hMem);
-#else
-               DropData->CacheGlobalData(CF_TEXT, hMem);
-#endif
                DROPEFFECT de = DropData->DoDragDrop(DROPEFFECT_COPY | DROPEFFECT_MOVE, nullptr);
        }
 
@@ -3877,7 +4351,7 @@ void CDirView::NameColumn(const DirColInfo *col, int subitem)
        int phys = m_pColItems->ColLogToPhys(subitem);
        if (phys>=0)
        {
-               String s = tr(col->idNameContext, col->idName);
+               String s = col->GetDisplayName();
                LV_COLUMN lvc;
                lvc.mask = LVCF_TEXT;
                lvc.pszText = const_cast<LPTSTR>(s.c_str());
@@ -3892,7 +4366,8 @@ void CDirView::UpdateColumnNames()
        for (int i=0; i<ncols; ++i)
        {
                const DirColInfo* col = m_pColItems->GetDirColInfo(i);
-               NameColumn(col, i);
+               if (col)
+                       NameColumn(col, i);
        }
 }
 
@@ -3905,10 +4380,13 @@ void CDirView::SetColAlignments()
        for (int i=0; i<ncols; ++i)
        {
                const DirColInfo * col = m_pColItems->GetDirColInfo(i);
-               LVCOLUMN lvc;
-               lvc.mask = LVCF_FMT;
-               lvc.fmt = col->alignment;
-               m_pList->SetColumn(m_pColItems->ColLogToPhys(i), &lvc);
+               if (col)
+               {
+                       LVCOLUMN lvc;
+                       lvc.mask = LVCF_FMT;
+                       lvc.fmt = col->alignment;
+                       m_pList->SetColumn(m_pColItems->ColLogToPhys(i), &lvc);
+               }
        }
 }
 
@@ -3942,17 +4420,16 @@ int CALLBACK CDirView::CompareState::CompareFunc(LPARAM lParam1, LPARAM lParam2,
 }
 
 /// Add new item to list view
-int CDirView::AddNewItem(int i, DIFFITEM *diffpos, int iImage, int iIndent)
+void CDirView::AddNewItem(int i, DIFFITEM *diffpos, int iImage, int iIndent)
 {
-       LV_ITEM lvItem;
-       lvItem.mask = LVIF_TEXT | LVIF_PARAM | LVIF_IMAGE | LVIF_INDENT;
-       lvItem.iItem = i;
+       ListViewOwnerDataItem lvItem;
        lvItem.iIndent = iIndent;
-       lvItem.iSubItem = 0;
-       lvItem.pszText = LPSTR_TEXTCALLBACK;
        lvItem.lParam = (LPARAM)diffpos;
        lvItem.iImage = iImage;
-       return GetListCtrl().InsertItem(&lvItem);
+       if (i == static_cast<int>(m_listViewItems.size()))
+               m_listViewItems.push_back(lvItem);
+       else
+               m_listViewItems.insert(m_listViewItems.begin() + i, lvItem);
 }
 
 /**
@@ -3962,6 +4439,24 @@ int CDirView::AddNewItem(int i, DIFFITEM *diffpos, int iImage, int iIndent)
 void CDirView::UpdateDiffItemStatus(UINT nIdx)
 {
        GetListCtrl().RedrawItems(nIdx, nIdx);
+       const DIFFITEM& di = GetDiffItem(nIdx);
+       if (di.diffcode.isDirectory())
+       {
+               DirItemIterator it;
+               for (it = RevBegin(); it != RevEnd(); )
+               {
+                       DIFFITEM& di2 = *it;
+                       int cursel = it.m_sel;
+                       ++it;
+                       if (di2.IsAncestor(&di))
+                       {
+                               if ((di2.diffcode.diffcode & DIFFCODE::SIDEFLAGS) == 0)
+                                       DeleteItem(cursel, true);
+                               else
+                                       GetListCtrl().RedrawItems(cursel, cursel);
+                       }
+               }
+       }
 }
 
 static String rgDispinfoText[2]; // used in function below
@@ -3989,10 +4484,13 @@ static LPTSTR NTAPI AllocDispinfoText(const String &s)
 void CDirView::ReflectGetdispinfo(NMLVDISPINFO *pParam)
 {
        int nIdx = pParam->item.iItem;
+       if (nIdx >= static_cast<int>(m_listViewItems.size()))
+               return;
+       DIFFITEM *key = reinterpret_cast<DIFFITEM*>(m_listViewItems[nIdx].lParam);
        int i = m_pColItems->ColPhysToLog(pParam->item.iSubItem);
-       DIFFITEM *key = GetItemKey(nIdx);
-       if (key == (DIFFITEM *)SPECIAL_ITEM_POS)
+       if (IsDiffItemSpecial(key))
        {
+               pParam->item.iImage = m_listViewItems[nIdx].iImage;
                if (m_pColItems->IsColName(i))
                {
                        pParam->item.pszText = _T("..");
@@ -4011,10 +4509,23 @@ void CDirView::ReflectGetdispinfo(NMLVDISPINFO *pParam)
        if (pParam->item.mask & LVIF_IMAGE)
        {
                pParam->item.iImage = GetColImage(di);
+               if ((pParam->item.mask & LVIF_STATE) == 0)
+               {
+                       // for WinXP
+                       pParam->item.mask |= LVIF_STATE;
+                       pParam->item.state = m_pList->GetItemState(nIdx, static_cast<UINT>(~LVIS_STATEIMAGEMASK));
+               }
+       }
+       if (pParam->item.mask & LVIF_INDENT)
+       {
+               pParam->item.iIndent = m_listViewItems[nIdx].iIndent;
+       }
+       if (pParam->item.mask & LVIF_STATE)
+       {
+               pParam->item.stateMask |= LVIS_STATEIMAGEMASK;
+               if (di.HasChildren())
+                       pParam->item.state |= INDEXTOSTATEIMAGEMASK((di.customFlags & ViewCustomFlags::EXPANDED) ? 2 : 1);
        }
-
-       m_bNeedSearchLastDiffItem = true;
-       m_bNeedSearchFirstDiffItem = true;
 }
 
 /**
@@ -4022,40 +4533,67 @@ void CDirView::ReflectGetdispinfo(NMLVDISPINFO *pParam)
  */
 void CDirView::OnEditColumns()
 {
-       CDirColsDlg dlg;
-       // List all the currently displayed columns
-       for (int col=0; col<GetListCtrl().GetHeaderCtrl()->GetItemCount(); ++col)
-       {
-               int l = m_pColItems->ColPhysToLog(col);
-               dlg.AddColumn(m_pColItems->GetColDisplayName(l), m_pColItems->GetColDescription(l), l, col);
-       }
-       // Now add all the columns not currently displayed
-       int l=0;
-       for (l=0; l<m_pColItems->GetColCount(); ++l)
+       bool bReset = false;
+       CDirColsDlg::ColumnArray cols;
+
+       for (;;)
        {
-               if (m_pColItems->ColLogToPhys(l)==-1)
+               CDirColsDlg dlg;
+               // List all the currently displayed columns
+               for (int col=0; col<GetListCtrl().GetHeaderCtrl()->GetItemCount(); ++col)
                {
-                       dlg.AddColumn(m_pColItems->GetColDisplayName(l), m_pColItems->GetColDescription(l), l);
+                       int l = m_pColItems->ColPhysToLog(col);
+                       dlg.AddColumn(m_pColItems->GetColDisplayName(l), m_pColItems->GetColDescription(l), l, col);
                }
-       }
+               // Now add all the columns not currently displayed
+               int l=0;
+               for (l=0; l<m_pColItems->GetColCount(); ++l)
+               {
+                       if (m_pColItems->ColLogToPhys(l)==-1)
+                       {
+                               dlg.AddColumn(m_pColItems->GetColDisplayName(l), m_pColItems->GetColDescription(l), l);
+                       }
+               }
+               assert(m_pColItems->GetColCount() == dlg.GetColumns().size());
 
-       // Add default order of columns for resetting to defaults
-       for (l = 0; l < m_pColItems->GetColCount(); ++l)
-       {
-               int phy = m_pColItems->GetColDefaultOrder(l);
-               dlg.AddDefColumn(m_pColItems->GetColDisplayName(l), l, phy);
-       }
+               // Add default order of columns for resetting to defaults
+               for (l = 0; l < m_pColItems->GetColCount(); ++l)
+               {
+                       int phy = m_pColItems->GetColDefaultOrder(l);
+                       dlg.AddDefColumn(m_pColItems->GetColDisplayName(l), l, phy);
+               }
 
-       if (dlg.DoModal() != IDOK)
-               return;
+               if (dlg.DoModal() != IDOK)
+                       return;
+
+               if (!dlg.GetShowAdditionalProperties())
+               {
+                       bReset = dlg.m_bReset;
+                       cols = dlg.GetColumns();
+                       break;
+               }
+               
+               CDirAdditionalPropertiesDlg dlgAdditionalProperties(m_pColItems->GetAdditionalPropertyNames());
+               if (dlgAdditionalProperties.DoModal() == IDOK)
+               {
+                       auto& selectedCanonicalNames = dlgAdditionalProperties.GetSelectedCanonicalNames();
+                       GetOptionsMgr()->SaveOption(OPT_ADDITIONAL_PROPERTIES,
+                               strutils::join(selectedCanonicalNames.begin(), selectedCanonicalNames.end(), _T(" ")));
+                       m_pColItems->SetAdditionalPropertyNames(selectedCanonicalNames);
+                       m_pColItems->SaveColumnOrders();
+                       GetDiffContext().m_pPropertySystem.reset(new PropertySystem(m_pColItems->GetAdditionalPropertyNames()));
+                       GetDiffContext().ClearAllAdditionalProperties();
+                       auto* pDoc = GetDocument();
+                       ReloadColumns();
+               }
+       } 
 
-       String secname = GetDocument()->m_nDirs < 3 ? _T("DirView") : _T("DirView3");
-       theApp.WriteProfileString(secname.c_str(), _T("ColumnWidths"),
-               (dlg.m_bReset ? m_pColItems->ResetColumnWidths(DefColumnWidth) :
-                               m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1))).c_str());
+       const String keyname = GetDocument()->m_nDirs < 3 ? OPT_DIRVIEW_COLUMN_WIDTHS : OPT_DIRVIEW3_COLUMN_WIDTHS;
+       GetOptionsMgr()->SaveOption(keyname,
+               (bReset ? m_pColItems->ResetColumnWidths(GetDefColumnWidth()) :
+                               m_pColItems->SaveColumnWidths(std::bind(&CListCtrl::GetColumnWidth, m_pList, _1))));
 
        // Reset our data to reflect the new data from the dialog
-       const CDirColsDlg::ColumnArray & cols = dlg.GetColumns();
        m_pColItems->ClearColumnOrders();
        const int sortColumn = GetOptionsMgr()->GetInt((GetDocument()->m_nDirs < 3) ? OPT_DIRVIEW_SORT_COLUMN : OPT_DIRVIEW_SORT_COLUMN3);
        std::vector<int> colorder(m_pColItems->GetColCount(), -1);
@@ -4078,14 +4616,12 @@ void CDirView::OnEditColumns()
 
        if (m_pColItems->GetDispColCount() < 1)
        {
-               // Ignore them if they didn't leave a column showing
+               // Set them back to default if they didn't leave a column showing
+               // (However, if none of the items are checked, this process will not be executed because the "OK" button in the "Display Columns" dialog cannot be pressed.)
                m_pColItems->ResetColumnOrdering();
        }
-       else
-       {
-               ReloadColumns();
-               Redisplay();
-       }
+       ReloadColumns();
+       Redisplay();
 }
 
 DirActions CDirView::MakeDirActions(DirActions::method_type func) const