OSDN Git Service

Fix issue #784: Error on try to show differences between two different gif
[winmerge-jp/winmerge-jp.git] / Src / MainFrm.cpp
index f9df628..708ddb4 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  MainFrm.cpp
@@ -36,7 +22,7 @@
 #include "BCMenu.h"
 #include "OpenFrm.h"
 #include "DirFrame.h"          // Include type information
-#include "ChildFrm.h"
+#include "MergeEditFrm.h"
 #include "HexMergeFrm.h"
 #include "DirView.h"
 #include "DirDoc.h"
 #include "HexMergeView.h"
 #include "ImgMergeFrm.h"
 #include "LineFiltersList.h"
+#include "SubstitutionFiltersList.h"
 #include "ConflictFileParser.h"
 #include "LineFiltersDlg.h"
+#include "SubstitutionFiltersDlg.h"
 #include "paths.h"
 #include "Environment.h"
 #include "PatchTool.h"
 #include "VersionInfo.h"
 #include "Bitmap.h"
 #include "CCrystalTextMarkers.h"
+#include "utils/hqbitmap.h"
+#include "UniFile.h"
+#include "TFile.h"
+#include "Shell.h"
+#include "WindowsManagerDialog.h"
 
 using std::vector;
 using boost::begin;
@@ -82,17 +75,23 @@ using boost::end;
 #define new DEBUG_NEW
 #endif
 
-static void LoadToolbarImageList(int imageWidth, UINT nIDResource, UINT nIDResourceMask, bool bGrayscale, CImageList& ImgList);
+static void LoadToolbarImageList(int orgImageWidth, int newImageHeight, UINT nIDResource, bool bGrayscale, CImageList& ImgList);
 static CPtrList &GetDocList(CMultiDocTemplate *pTemplate);
 template<class DocClass>
-DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles);
+DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles, bool bMakeVisible = true);
 
 /**
  * @brief A table associating menuitem id, icon and menus to apply.
  */
 const CMainFrame::MENUITEM_ICON CMainFrame::m_MenuIcons[] = {
        { ID_FILE_OPENCONFLICT,                 IDB_FILE_OPENCONFLICT,                  CMainFrame::MENU_ALL },
-       { ID_FILE_NEW3,                 IDB_FILE_NEW3,                  CMainFrame::MENU_ALL },
+       { ID_FILE_NEW_TABLE,                    IDB_FILE_NEW_TABLE,                             CMainFrame::MENU_ALL },
+       { ID_FILE_NEW_HEX,                              IDB_FILE_NEW_HEX,                               CMainFrame::MENU_ALL },
+       { ID_FILE_NEW_IMAGE,                    IDB_FILE_NEW_IMAGE,                             CMainFrame::MENU_ALL },
+       { ID_FILE_NEW3,                                 IDB_FILE_NEW3,                                  CMainFrame::MENU_ALL },
+       { ID_FILE_NEW3_TABLE,                   IDB_FILE_NEW3_TABLE,                    CMainFrame::MENU_ALL },
+       { ID_FILE_NEW3_HEX,                             IDB_FILE_NEW3_HEX,                              CMainFrame::MENU_ALL },
+       { ID_FILE_NEW3_IMAGE,                   IDB_FILE_NEW3_IMAGE,                    CMainFrame::MENU_ALL },
        { ID_EDIT_COPY,                                 IDB_EDIT_COPY,                                  CMainFrame::MENU_ALL },
        { ID_EDIT_CUT,                                  IDB_EDIT_CUT,                                   CMainFrame::MENU_ALL },
        { ID_EDIT_PASTE,                                IDB_EDIT_PASTE,                                 CMainFrame::MENU_ALL },
@@ -112,8 +111,6 @@ const CMainFrame::MENUITEM_ICON CMainFrame::m_MenuIcons[] = {
        { ID_TOOLS_CUSTOMIZECOLUMNS,    IDB_TOOLS_COLUMNS,                              CMainFrame::MENU_ALL },
        { ID_TOOLS_GENERATEPATCH,               IDB_TOOLS_GENERATEPATCH,                CMainFrame::MENU_ALL },
        { ID_PLUGINS_LIST,                              IDB_PLUGINS_LIST,                               CMainFrame::MENU_ALL },
-       { ID_COPY_FROM_LEFT,                    IDB_COPY_FROM_LEFT,                             CMainFrame::MENU_ALL },
-       { ID_COPY_FROM_RIGHT,                   IDB_COPY_FROM_RIGHT,                    CMainFrame::MENU_ALL },
        { ID_FILE_PRINT,                                IDB_FILE_PRINT,                                 CMainFrame::MENU_FILECMP },
        { ID_TOOLS_GENERATEREPORT,              IDB_TOOLS_GENERATEREPORT,               CMainFrame::MENU_FILECMP },
        { ID_EDIT_TOGGLE_BOOKMARK,              IDB_EDIT_TOGGLE_BOOKMARK,               CMainFrame::MENU_FILECMP },
@@ -122,6 +119,12 @@ const CMainFrame::MENUITEM_ICON CMainFrame::m_MenuIcons[] = {
        { ID_EDIT_CLEAR_ALL_BOOKMARKS,  IDB_EDIT_CLEAR_ALL_BOOKMARKS,   CMainFrame::MENU_FILECMP },
        { ID_VIEW_ZOOMIN,                               IDB_VIEW_ZOOMIN,                                CMainFrame::MENU_FILECMP },
        { ID_VIEW_ZOOMOUT,                              IDB_VIEW_ZOOMOUT,                               CMainFrame::MENU_FILECMP },
+       { ID_COPY_FROM_LEFT,                    IDB_COPY_FROM_LEFT,                             CMainFrame::MENU_FILECMP },
+       { ID_COPY_FROM_RIGHT,                   IDB_COPY_FROM_RIGHT,                    CMainFrame::MENU_FILECMP },
+       { ID_LINES_R2L,                                 IDB_COPY_SELECTED_LINES_TO_LEFT,        CMainFrame::MENU_FILECMP },
+       { ID_LINES_L2R,                                 IDB_COPY_SELECTED_LINES_TO_RIGHT,       CMainFrame::MENU_FILECMP },
+       { ID_COPY_LINES_FROM_LEFT,              IDB_COPY_SELECTED_LINES_FROM_LEFT,      CMainFrame::MENU_FILECMP },
+       { ID_COPY_LINES_FROM_RIGHT,             IDB_COPY_SELECTED_LINES_FROM_RIGHT,     CMainFrame::MENU_FILECMP },
        { ID_MERGE_COMPARE,                             IDB_MERGE_COMPARE,                              CMainFrame::MENU_FOLDERCMP },
        { ID_MERGE_COMPARE_LEFT1_LEFT2,         IDB_MERGE_COMPARE_LEFT1_LEFT2,  CMainFrame::MENU_FOLDERCMP },
        { ID_MERGE_COMPARE_RIGHT1_RIGHT2,       IDB_MERGE_COMPARE_RIGHT1_RIGHT2,CMainFrame::MENU_FOLDERCMP },
@@ -184,6 +187,7 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
        ON_COMMAND(ID_HELP_CONTENTS, OnHelpContents)
        ON_WM_CLOSE()
        ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
+       ON_WM_TIMER()
        ON_WM_DESTROY()
        ON_COMMAND_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnPluginUnpackMode)
        ON_UPDATE_COMMAND_UI_RANGE(ID_UNPACK_MANUAL, ID_UNPACK_AUTO, OnUpdatePluginUnpackMode)
@@ -192,8 +196,14 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
        ON_UPDATE_COMMAND_UI(ID_RELOAD_PLUGINS, OnUpdateReloadPlugins)
        ON_COMMAND(ID_RELOAD_PLUGINS, OnReloadPlugins)
        ON_COMMAND(ID_HELP_GETCONFIG, OnSaveConfigData)
-       ON_COMMAND(ID_FILE_NEW, OnFileNew)
-       ON_COMMAND(ID_FILE_NEW3, OnFileNew3)
+       ON_COMMAND(ID_FILE_NEW, (OnFileNew<2, FRAME_FILE>))
+       ON_COMMAND(ID_FILE_NEW_TABLE, (OnFileNew<2, FRAME_FILE, true>))
+       ON_COMMAND(ID_FILE_NEW_HEX, (OnFileNew<2, FRAME_HEXFILE>))
+       ON_COMMAND(ID_FILE_NEW_IMAGE, (OnFileNew<2, FRAME_IMGFILE>))
+       ON_COMMAND(ID_FILE_NEW3, (OnFileNew<3, FRAME_FILE>))
+       ON_COMMAND(ID_FILE_NEW3_TABLE, (OnFileNew<2, FRAME_FILE, true>))
+       ON_COMMAND(ID_FILE_NEW3_HEX, (OnFileNew<3, FRAME_HEXFILE>))
+       ON_COMMAND(ID_FILE_NEW3_IMAGE, (OnFileNew<3, FRAME_IMGFILE>))
        ON_COMMAND(ID_TOOLS_FILTERS, OnToolsFilters)
        ON_COMMAND(ID_VIEW_STATUS_BAR, OnViewStatusBar)
        ON_UPDATE_COMMAND_UI(ID_VIEW_TAB_BAR, OnUpdateViewTabBar)
@@ -216,13 +226,15 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
        ON_COMMAND(ID_FILE_OPENCONFLICT, OnFileOpenConflict)
        ON_COMMAND(ID_PLUGINS_LIST, OnPluginsList)
        ON_UPDATE_COMMAND_UI(ID_STATUS_PLUGIN, OnUpdatePluginName)
-       ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnDiffOptionsDropDown)
+       ON_NOTIFY(TBN_DROPDOWN, AFX_IDW_TOOLBAR, OnToolbarButtonDropDown)
        ON_COMMAND_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnDiffWhitespace)
        ON_UPDATE_COMMAND_UI_RANGE(IDC_DIFF_WHITESPACE_COMPARE, IDC_DIFF_WHITESPACE_IGNOREALL, OnUpdateDiffWhitespace)
-       ON_COMMAND(IDC_DIFF_CASESENSITIVE, OnDiffCaseSensitive)
-       ON_UPDATE_COMMAND_UI(IDC_DIFF_CASESENSITIVE, OnUpdateDiffCaseSensitive)
+       ON_COMMAND(IDC_DIFF_IGNORECASE, OnDiffIgnoreCase)
+       ON_UPDATE_COMMAND_UI(IDC_DIFF_IGNORECASE, OnUpdateDiffIgnoreCase)
        ON_COMMAND(IDC_DIFF_IGNOREEOL, OnDiffIgnoreEOL)
        ON_UPDATE_COMMAND_UI(IDC_DIFF_IGNOREEOL, OnUpdateDiffIgnoreEOL)
+       ON_COMMAND(IDC_DIFF_IGNORECP, OnDiffIgnoreCP)
+       ON_UPDATE_COMMAND_UI(IDC_DIFF_IGNORECP, OnUpdateDiffIgnoreCP)
        ON_COMMAND(IDC_RECURS_CHECK, OnIncludeSubfolders)
        ON_UPDATE_COMMAND_UI(IDC_RECURS_CHECK, OnUpdateIncludeSubfolders)
        ON_COMMAND_RANGE(ID_COMPMETHOD_FULL_CONTENTS, ID_COMPMETHOD_SIZE, OnCompareMethod)
@@ -230,6 +242,19 @@ BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
        ON_COMMAND_RANGE(ID_MRU_FIRST, ID_MRU_LAST, OnMRUs)
        ON_UPDATE_COMMAND_UI(ID_MRU_FIRST, OnUpdateNoMRUs)
        ON_UPDATE_COMMAND_UI(ID_NO_MRU, OnUpdateNoMRUs)
+       ON_COMMAND(ID_FIRSTFILE, OnFirstFile)
+       ON_UPDATE_COMMAND_UI(ID_FIRSTFILE, OnUpdateFirstFile)
+       ON_COMMAND(ID_PREVFILE, OnPrevFile)
+       ON_UPDATE_COMMAND_UI(ID_PREVFILE, OnUpdatePrevFile)
+       ON_COMMAND(ID_NEXTFILE, OnNextFile)
+       ON_UPDATE_COMMAND_UI(ID_NEXTFILE, OnUpdateNextFile)
+       ON_COMMAND(ID_LASTFILE, OnLastFile)
+       ON_UPDATE_COMMAND_UI(ID_LASTFILE, OnUpdateLastFile)
+       ON_COMMAND(ID_ACCEL_QUIT, &CMainFrame::OnAccelQuit)
+       ON_MESSAGE(WMU_CHILDFRAMEADDED, &CMainFrame::OnChildFrameAdded)
+       ON_MESSAGE(WMU_CHILDFRAMEREMOVED, &CMainFrame::OnChildFrameRemoved)
+       ON_MESSAGE(WMU_CHILDFRAMEACTIVATE, &CMainFrame::OnChildFrameActivate)
+       ON_MESSAGE(WMU_CHILDFRAMEACTIVATED, &CMainFrame::OnChildFrameActivated)
        //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
@@ -269,8 +294,10 @@ static CPtrList &GetDocList(CMultiDocTemplate *pTemplate)
  */
 CMainFrame::CMainFrame()
 : m_bFirstTime(true)
-, m_pDropHandler(NULL)
+, m_pDropHandler(nullptr)
 , m_bShowErrors(false)
+, m_lfDiff(Options::Font::Load(GetOptionsMgr(), OPT_FONT_FILECMP))
+, m_lfDir(Options::Font::Load(GetOptionsMgr(), OPT_FONT_DIRCMP))
 {
 }
 
@@ -278,13 +305,12 @@ CMainFrame::~CMainFrame()
 {
        GetOptionsMgr()->SaveOption(OPT_TABBAR_AUTO_MAXWIDTH, m_wndTabBar.GetAutoMaxWidth());
        strdiff::Close();
+
+       m_arrChild.RemoveAll();
 }
 
-#ifdef _UNICODE
 const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassW");
-#else
-const TCHAR CMainFrame::szClassName[] = _T("WinMergeWindowClassA");
-#endif
+
 /**
  * @brief Change MainFrame window class name
  *        see http://support.microsoft.com/kb/403825/ja
@@ -313,9 +339,8 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
        if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
                return -1;
 
-       m_lfDiff = Options::Font::Load(GetOptionsMgr(), OPT_FONT_FILECMP);
-       m_lfDir = Options::Font::Load(GetOptionsMgr(), OPT_FONT_DIRCMP);
-       
+       m_wndMDIClient.SubclassWindow(m_hWndMDIClient);
+
        if (!CreateToolbar())
        {
                TRACE0("Failed to create toolbar\n");
@@ -338,7 +363,7 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
                return -1;      // fail to create
        }
        theApp.SetIndicators(m_wndStatusBar, StatusbarIndicators,
-                       countof(StatusbarIndicators));
+                       static_cast<int>(std::size(StatusbarIndicators)));
 
        const int lpx = CClientDC(this).GetDeviceCaps(LOGPIXELSX);
        auto pointToPixel = [lpx](int point) { return MulDiv(point, lpx, 72); };
@@ -353,40 +378,68 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
        m_pDropHandler = new DropHandler(std::bind(&CMainFrame::OnDropFiles, this, std::placeholders::_1));
        RegisterDragDrop(m_hWnd, m_pDropHandler);
 
+       m_wndMDIClient.ModifyStyleEx(WS_EX_CLIENTEDGE, 0);
+
        return 0;
 }
 
+void CMainFrame::OnTimer(UINT_PTR nIDEvent)
+{
+       CMDIFrameWnd::OnTimer(nIDEvent);
+
+       if (nIDEvent == IDT_UPDATEMAINMENU)
+       {
+               KillTimer(nIDEvent);
+
+               BOOL bMaximized;
+               MDIGetActive(&bMaximized);
+
+               // When MDI maximized the window icon is drawn on the menu bar, so we
+               // need to notify it that our icon has changed.
+               if (bMaximized)
+                       DrawMenuBar();
+
+               OnUpdateFrameTitle(FALSE);
+       }
+}
+
 void CMainFrame::OnDestroy(void)
 {
-       if (m_pDropHandler)
+       if (m_pDropHandler != nullptr)
                RevokeDragDrop(m_hWnd);
 }
 
-static HMENU GetSubmenu(HMENU mainMenu, UINT nIDFirstMenuItem, bool bFirstSubmenu)
+static HMENU GetSubmenu(HMENU menu, bool bFirstSubmenu)
 {
-       int i;
-       for (i = 0 ; i < ::GetMenuItemCount(mainMenu) ; i++)
-               if (::GetMenuItemID(::GetSubMenu(mainMenu, i), 0) == nIDFirstMenuItem)
-                       break;
-       HMENU menu = ::GetSubMenu(mainMenu, i);
-
        if (!bFirstSubmenu)
        {
                // look for last submenu
-               for (i = ::GetMenuItemCount(menu) ; i >= 0  ; i--)
-                       if (::GetSubMenu(menu, i) != NULL)
+               for (int i = ::GetMenuItemCount(menu) ; i >= 0  ; i--)
+                       if (::GetSubMenu(menu, i) != nullptr)
                                return ::GetSubMenu(menu, i);
        }
        else
        {
                // look for first submenu
-               for (i = 0 ; i < ::GetMenuItemCount(menu) ; i++)
-                       if (::GetSubMenu(menu, i) != NULL)
+               for (int i = 0 ; i < ::GetMenuItemCount(menu) ; i++)
+                       if (::GetSubMenu(menu, i) != nullptr)
                                return ::GetSubMenu(menu, i);
        }
 
        // error, submenu not found
-       return NULL;
+       return nullptr;
+}
+
+static HMENU GetSubmenu(HMENU mainMenu, UINT nIDFirstMenuItem, bool bFirstSubmenu)
+{
+       int i;
+       for (i = 0 ; i < ::GetMenuItemCount(mainMenu) ; i++)
+               if (::GetMenuItemID(::GetSubMenu(mainMenu, i), 0) == nIDFirstMenuItem)
+                       break;
+       HMENU menu = ::GetSubMenu(mainMenu, i);
+       if (!menu)
+               return nullptr;
+       return GetSubmenu(menu, bFirstSubmenu);
 }
 
 /** 
@@ -419,12 +472,12 @@ HMENU CMainFrame::GetPrediffersSubmenu(HMENU mainMenu)
  */
 HMENU CMainFrame::NewMenu(int view, int ID)
 {
-       int menu_view, index;
-       if (m_pMenus[view] == NULL)
+       int menu_view;
+       if (m_pMenus[view] == nullptr)
        {
                m_pMenus[view].reset(new BCMenu());
-               if (m_pMenus[view] == NULL)
-                       return NULL;
+               if (m_pMenus[view] == nullptr)
+                       return nullptr;
        }
 
        switch (view)
@@ -444,22 +497,22 @@ HMENU CMainFrame::NewMenu(int view, int ID)
        if (!m_pMenus[view]->LoadMenu(ID))
        {
                ASSERT(false);
-               return NULL;
+               return nullptr;
        }
 
        if (view == MENU_IMGMERGEVIEW)
        {
-               BCMenu *pMenu = new BCMenu;
-               pMenu->LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMGMERGEVIEW));
-               m_pMenus[view]->InsertMenu(4, MF_BYPOSITION | MF_POPUP, (UINT_PTR)pMenu->GetSubMenu(0)->m_hMenu, const_cast<TCHAR *>(LoadResString(IDS_IMAGE_MENU).c_str())); 
+               m_pImageMenu.reset(new BCMenu);
+               m_pImageMenu->LoadMenu(MAKEINTRESOURCE(IDR_POPUP_IMGMERGEVIEW));
+               m_pMenus[view]->InsertMenu(4, MF_BYPOSITION | MF_POPUP, (UINT_PTR)m_pImageMenu->GetSubMenu(0)->m_hMenu, const_cast<TCHAR *>(LoadResString(IDS_IMAGE_MENU).c_str())); 
        }
 
        // Load bitmaps to menuitems
-       for (index = 0; index < countof(m_MenuIcons); index ++)
+       for (auto& menu_icon: m_MenuIcons)
        {
-               if (menu_view == (m_MenuIcons[index].menusToApply & menu_view))
+               if (menu_view == (menu_icon.menusToApply & menu_view))
                {
-                       m_pMenus[view]->ModifyODMenu(NULL, m_MenuIcons[index].menuitemID, m_MenuIcons[index].iconResID);
+                       m_pMenus[view]->ModifyODMenu(nullptr, menu_icon.menuitemID, menu_icon.iconResID);
                }
        }
 
@@ -557,7 +610,7 @@ LRESULT CMainFrame::OnMenuChar(UINT nChar, UINT nFlags,
                lresult=BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu);
        else
                lresult=CMDIFrameWnd::OnMenuChar(nChar, nFlags, pMenu);
-       return(lresult);
+       return lresult;
 }
 
 /**
@@ -592,28 +645,67 @@ void CMainFrame::OnFileOpen()
 static void
 FileLocationGuessEncodings(FileLocation & fileloc, int iGuessEncoding)
 {
-       fileloc.encoding = GuessCodepageEncoding(fileloc.filepath, iGuessEncoding);
+       fileloc.encoding = codepage_detect::Guess(fileloc.filepath, iGuessEncoding);
 }
 
 bool CMainFrame::ShowAutoMergeDoc(CDirDoc * pDirDoc,
        int nFiles, const FileLocation ifileloc[],
-       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
-       const PackingInfo * infoUnpacker /*= NULL*/)
+       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
+       const PackingInfo * infoUnpacker /*= nullptr*/)
 {
-       int pane;
+       ASSERT(pDirDoc != nullptr);
+
+       if (sReportFile.empty() && pDirDoc->CompareFilesIfFilesAreLarge(nFiles, ifileloc))
+               return false;
+
+       String unpackedFileExtension;
+       if (infoUnpacker && GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
+       {
+               std::vector<String> filepaths(nFiles);
+               std::transform(ifileloc, ifileloc + nFiles, filepaths.begin(),
+                       [](auto& file) { return file.filepath; });
+               String filteredFilenames = strutils::join(filepaths.begin(), filepaths.end(), _T("|"));
+               unpackedFileExtension = FileTransform::GetUnpackedFileExtension(filteredFilenames, infoUnpacker);
+       }
        FileFilterHelper filterImg, filterBin;
        filterImg.UseMask(true);
        filterImg.SetMask(GetOptionsMgr()->GetString(OPT_CMP_IMG_FILEPATTERNS));
        filterBin.UseMask(true);
        filterBin.SetMask(GetOptionsMgr()->GetString(OPT_CMP_BIN_FILEPATTERNS));
-       for (pane = 0; pane < nFiles; ++pane)
+       for (int pane = 0; pane < nFiles; ++pane)
        {
-               if (filterImg.includeFile(ifileloc[pane].filepath) && CImgMergeFrame::IsLoadable())
+               String filepath = ifileloc[pane].filepath + unpackedFileExtension;
+               if (filterImg.includeFile(filepath) && CImgMergeFrame::IsLoadable())
                        return ShowImgMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
-               else if (filterBin.includeFile(ifileloc[pane].filepath) && CHexMergeView::IsLoadable())
+               else if (filterBin.includeFile(filepath) && CHexMergeView::IsLoadable())
                        return ShowHexMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
        }
-       return ShowMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
+       return ShowTextOrTableMergeDoc({}, pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
+}
+
+bool CMainFrame::ShowMergeDoc(UINT nID, CDirDoc* pDirDoc,
+       int nFiles, const FileLocation ifileloc[],
+       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
+       const PackingInfo* infoUnpacker /*= nullptr*/)
+{
+       switch (nID)
+       {
+       case ID_MERGE_COMPARE_TEXT:
+               return GetMainFrame()->ShowTextMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags,
+                       strDesc, sReportFile, infoUnpacker);
+       case ID_MERGE_COMPARE_TABLE:
+               return GetMainFrame()->ShowTableMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags,
+                       strDesc, sReportFile, infoUnpacker);
+       case ID_MERGE_COMPARE_HEX:
+               return GetMainFrame()->ShowHexMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags,
+                       strDesc, sReportFile, infoUnpacker);
+       case ID_MERGE_COMPARE_IMAGE:
+               return GetMainFrame()->ShowImgMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags,
+                       strDesc, sReportFile, infoUnpacker);
+       default:
+               return GetMainFrame()->ShowAutoMergeDoc(pDirDoc, nFiles, ifileloc, dwFlags,
+                       strDesc, sReportFile, infoUnpacker);
+       }
 }
 
 std::array<bool, 3> GetROFromFlags(int nFiles, const DWORD dwFlags[])
@@ -648,21 +740,21 @@ int GetActivePaneFromFlags(int nFiles, const DWORD dwFlags[])
  * @param [in] infoUnpacker Plugin info.
  * @return success/failure
  */
-bool CMainFrame::ShowMergeDoc(CDirDoc * pDirDoc,
+bool CMainFrame::ShowTextOrTableMergeDoc(std::optional<bool> table, CDirDoc * pDirDoc,
        int nFiles, const FileLocation ifileloc[],
-       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
-       const PackingInfo * infoUnpacker /*= NULL*/)
+       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
+       const PackingInfo * infoUnpacker /*= nullptr*/)
 {
-       if (!m_pMenus[MENU_MERGEVIEW])
+       if (m_pMenus[MENU_MERGEVIEW] == nullptr)
                theApp.m_pDiffTemplate->m_hMenuShared = NewMergeViewMenu();
-       CMergeDoc * pMergeDoc = GetMergeDocForDiff<CMergeDoc>(theApp.m_pDiffTemplate, pDirDoc, nFiles);
+       CMergeDoc * pMergeDoc = GetMergeDocForDiff<CMergeDoc>(theApp.m_pDiffTemplate, pDirDoc, nFiles, false);
 
        // Make local copies, so we can change encoding if we guess it below
        FileLocation fileloc[3];
        std::copy_n(ifileloc, nFiles, fileloc);
 
-       ASSERT(pMergeDoc);              // must ASSERT to get an answer to the question below ;-)
-       if (!pMergeDoc)
+       ASSERT(pMergeDoc != nullptr);           // must ASSERT to get an answer to the question below ;-)
+       if (pMergeDoc == nullptr)
                return false; // when does this happen ?
 
        // if an unpacker is selected, it must be used during LoadFromFile
@@ -681,36 +773,22 @@ bool CMainFrame::ShowMergeDoc(CDirDoc * pDirDoc,
                {
                        FileLocationGuessEncodings(fileloc[pane], iGuessEncodingType);
                }
-
-               // TODO (Perry, 2005-12-04)
-               // Should we do any unification if unicodings are different?
-
-
-#ifndef _UNICODE
-               // In ANSI (8-bit) build, character loss can occur in merging
-               // if the two buffers use different encodings
-               if (pane > 0 && fileloc[pane - 1].encoding.m_codepage != fileloc[pane].encoding.m_codepage)
-               {
-                       CString msg;
-                       msg.Format(theApp.LoadString(IDS_SUGGEST_IGNORECODEPAGE).c_str(), fileloc[pane - 1].encoding.m_codepage,fileloc[pane].encoding.m_codepage);
-                       int msgflags = MB_YESNO | MB_ICONQUESTION | MB_DONT_ASK_AGAIN;
-                       // Two files with different codepages
-                       // Warn and propose to use the default codepage for both
-                       int userChoice = AfxMessageBox(msg, msgflags);
-                       if (userChoice == IDYES)
-                       {
-                               fileloc[pane - 1].encoding.SetCodepage(ucr::getDefaultCodepage());
-                               fileloc[pane - 1].encoding.m_bom = false;
-                               fileloc[pane].encoding.SetCodepage(ucr::getDefaultCodepage());
-                               fileloc[pane].encoding.m_bom = false;
-                       }
-               }
-#endif
        }
 
+       pMergeDoc->SetEnableTableEditing(table);
+
        // Note that OpenDocs() takes care of closing compare window when needed.
-       if (!pMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags)))
+       bool bResult = pMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc);
+       if (bResult)
+       {
+               if (CMergeEditFrame *pFrame = pMergeDoc->GetParentFrame())
+                       if (!pFrame->IsActivated())
+                               pFrame->InitialUpdateFrame(pMergeDoc, true);
+       }
+       else
+       {
                return false;
+       }
 
        for (int pane = 0; pane < nFiles; pane++)
        {
@@ -719,7 +797,7 @@ bool CMainFrame::ShowMergeDoc(CDirDoc * pDirDoc,
                        bool bModified = (dwFlags[pane] & FFILEOPEN_MODIFIED) > 0;
                        if (bModified)
                        {
-                               pMergeDoc->m_ptBuf[pane]->SetModified(TRUE);
+                               pMergeDoc->m_ptBuf[pane]->SetModified(true);
                                pMergeDoc->UpdateHeaderPath(pane);
                        }
                        if (dwFlags[pane] & FFILEOPEN_AUTOMERGE)
@@ -729,24 +807,46 @@ bool CMainFrame::ShowMergeDoc(CDirDoc * pDirDoc,
                }
        }
 
+       pMergeDoc->MoveOnLoad(GetActivePaneFromFlags(nFiles, dwFlags));
+
        if (!sReportFile.empty())
                pMergeDoc->GenerateReport(sReportFile);
 
        return true;
 }
 
+bool CMainFrame::ShowTextMergeDoc(CDirDoc* pDirDoc,
+       int nFiles, const FileLocation ifileloc[],
+       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
+       const PackingInfo* infoUnpacker /*= nullptr*/)
+{
+       return ShowTextOrTableMergeDoc(false, pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
+}
+
+bool CMainFrame::ShowTableMergeDoc(CDirDoc* pDirDoc,
+       int nFiles, const FileLocation ifileloc[],
+       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
+       const PackingInfo* infoUnpacker /*= nullptr*/)
+{
+       return ShowTextOrTableMergeDoc(true, pDirDoc, nFiles, ifileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
+}
+
 bool CMainFrame::ShowHexMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocation fileloc[],
-       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
-       const PackingInfo * infoUnpacker /*= NULL*/)
+       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
+       const PackingInfo * infoUnpacker /*= nullptr*/)
 {
-       if (!m_pMenus[MENU_HEXMERGEVIEW])
+       if (m_pMenus[MENU_HEXMERGEVIEW] == nullptr)
                theApp.m_pHexMergeTemplate->m_hMenuShared = NewHexMergeViewMenu();
        CHexMergeDoc *pHexMergeDoc = GetMergeDocForDiff<CHexMergeDoc>(theApp.m_pHexMergeTemplate, pDirDoc, nFiles);
-       if (!pHexMergeDoc)
+       if (pHexMergeDoc == nullptr)
                return false;
 
-       if (!pHexMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags)))
+       pHexMergeDoc->SetUnpacker(infoUnpacker);
+
+       if (!pHexMergeDoc->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc))
                return false;
+
+       pHexMergeDoc->MoveOnLoad(GetActivePaneFromFlags(nFiles, dwFlags));
        
        if (!sReportFile.empty())
                pHexMergeDoc->GenerateReport(sReportFile);
@@ -755,19 +855,19 @@ bool CMainFrame::ShowHexMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocati
 }
 
 bool CMainFrame::ShowImgMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocation fileloc[],
-       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*=_T("")*/,
-       const PackingInfo * infoUnpacker/* = NULL*/)
+       const DWORD dwFlags[], const String strDesc[], const String& sReportFile /*= _T("")*/,
+       const PackingInfo * infoUnpacker /*= nullptr*/)
 {
        CImgMergeFrame *pImgMergeFrame = new CImgMergeFrame();
        if (!CImgMergeFrame::menu.m_hMenu)
                CImgMergeFrame::menu.m_hMenu = NewImgMergeViewMenu();
        pImgMergeFrame->SetSharedMenu(CImgMergeFrame::menu.m_hMenu);
-
+       pImgMergeFrame->SetUnpacker(infoUnpacker);
        pImgMergeFrame->SetDirDoc(pDirDoc);
        pDirDoc->AddMergeDoc(pImgMergeFrame);
                
-       if (!pImgMergeFrame->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, GetActivePaneFromFlags(nFiles, dwFlags), this))
-               return ShowMergeDoc(pDirDoc, nFiles, fileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
+       if (!pImgMergeFrame->OpenDocs(nFiles, fileloc, GetROFromFlags(nFiles, dwFlags).data(), strDesc, this))
+               return ShowTextMergeDoc(pDirDoc, nFiles, fileloc, dwFlags, strDesc, sReportFile, infoUnpacker);
 
        for (int pane = 0; pane < nFiles; pane++)
        {
@@ -775,19 +875,44 @@ bool CMainFrame::ShowImgMergeDoc(CDirDoc * pDirDoc, int nFiles, const FileLocati
                        pImgMergeFrame->DoAutoMerge(pane);
        }
 
+       pImgMergeFrame->MoveOnLoad(GetActivePaneFromFlags(nFiles, dwFlags));
+
        if (!sReportFile.empty())
                pImgMergeFrame->GenerateReport(sReportFile);
 
        return true;
 }
 
+bool CMainFrame::ShowTextMergeDoc(CDirDoc* pDirDoc, int nBuffers, const String text[],
+               const String strDesc[], const String& strFileExt)
+{
+       FileLocation fileloc[3];
+       DWORD dwFlags[3] = {};
+       CDirDoc* pDirDoc2 = pDirDoc->GetMainView() ? pDirDoc :
+               static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
+       for (int nBuffer = 0; nBuffer < nBuffers; ++nBuffer)
+       {
+               TempFilePtr wTemp(new TempFile());
+               String workFile = wTemp->Create(_T("text_"), strFileExt);
+               m_tempFiles.push_back(wTemp);
+               wTemp->Create(_T(""), strFileExt);
+               UniStdioFile file;
+               if (file.OpenCreateUtf8(workFile))
+               {
+                       file.WriteString(text[nBuffer]);
+               }
+               fileloc[nBuffer].setPath(workFile);
+       }
+       return ShowTextMergeDoc(pDirDoc2, nBuffers, fileloc, dwFlags, strDesc);
+}
+
 /**
  * @brief Show GNU licence information in notepad (local file) or in Web Browser
  */
 void CMainFrame::OnHelpGnulicense() 
 {
        const String spath = paths::ConcatPath(env::GetProgPath(), LicenseFile);
-       theApp.OpenFileOrUrl(spath.c_str(), LicenceUrl);
+       shell::OpenFileOrUrl(spath.c_str(), LicenceUrl);
 }
 
 /**
@@ -804,7 +929,7 @@ void CMainFrame::OnOptions()
                LANGID lang = static_cast<LANGID>(GetOptionsMgr()->GetInt(OPT_SELECTED_LANGUAGE));
                if (lang != theApp.m_pLangDlg->GetLangId())
                {
-                       theApp.m_pLangDlg->SetLanguage(lang, TRUE);
+                       theApp.m_pLangDlg->SetLanguage(lang, true);
        
                        // Update status bar inicator texts
                        theApp.SetIndicators(m_wndStatusBar, 0, 0);
@@ -825,6 +950,9 @@ void CMainFrame::OnOptions()
                String filterPath = GetOptionsMgr()->GetString(OPT_FILTER_USERPATH);
                theApp.m_pGlobalFileFilter->SetUserFilterPath(filterPath);
 
+               CCrystalTextView::RENDERING_MODE nRenderingMode = static_cast<CCrystalTextView::RENDERING_MODE>(GetOptionsMgr()->GetInt(OPT_RENDERING_MODE));
+               CCrystalTextView::SetRenderingModeDefault(nRenderingMode);
+
                theApp.UpdateCodepageModule();
 
                strdiff::SetBreakChars(GetOptionsMgr()->GetString(OPT_BREAK_SEPARATORS).c_str());
@@ -868,9 +996,15 @@ static bool AddToRecentDocs(const PathContext& paths, const unsigned flags[], bo
                params += _T("/r ");
        if (!filter.empty())
                params += _T("/f \"") + filter + _T("\" ");
-       return JumpList::AddToRecentDocs(_T(""), params, title, params, 0);
-}
 
+       Concurrent::CreateTask([params, title](){
+                       CoInitialize(nullptr);
+                       JumpList::AddToRecentDocs(_T(""), params, title, params, 0);
+                       CoUninitialize();
+                       return 0;
+               });
+       return true;
+}
 /**
  * @brief Begin a diff: open dirdoc if it is directories, else open a mergedoc for editing.
  * @param [in] pszLeft Left-side path.
@@ -882,20 +1016,20 @@ static bool AddToRecentDocs(const PathContext& paths, const unsigned flags[], bo
  * @param [in] prediffer Prediffer plugin name.
  * @return `true` if opening files and compare succeeded, `false` otherwise.
  */
-bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
-       const DWORD dwFlags[] /*=NULL*/, const String strDesc[] /*=NULL*/, const String& sReportFile /*=T("")*/, bool bRecurse /*= false*/, CDirDoc *pDirDoc/*=NULL*/,
-       String prediffer /*=_T("")*/, const PackingInfo *infoUnpacker/*=NULL*/)
+bool CMainFrame::DoFileOpen(const PathContext * pFiles /*= nullptr*/,
+       const DWORD dwFlags[] /*= nullptr*/, const String strDesc[] /*= nullptr*/, const String& sReportFile /*= T("")*/, bool bRecurse /*= false*/, CDirDoc *pDirDoc/*= nullptr*/,
+       String prediffer /*= _T("")*/, const PackingInfo *infoUnpacker /*= nullptr*/)
 {
-       if (pDirDoc && !pDirDoc->CloseMergeDocs())
-               return FALSE;
+       if (pDirDoc != nullptr && !pDirDoc->CloseMergeDocs())
+               return false;
 
-       FileTransform::g_bUnpackerMode = theApp.GetProfileInt(_T("Settings"), _T("UnpackerMode"), PLUGIN_MANUAL);
-       FileTransform::g_bPredifferMode = theApp.GetProfileInt(_T("Settings"), _T("PredifferMode"), PLUGIN_MANUAL);
+       FileTransform::g_UnpackerMode = static_cast<PLUGIN_MODE>(GetOptionsMgr()->GetInt(OPT_PLUGINS_UNPACKER_MODE));
+       FileTransform::g_PredifferMode = static_cast<PLUGIN_MODE>(GetOptionsMgr()->GetInt(OPT_PLUGINS_PREDIFFER_MODE));
 
        Merge7zFormatMergePluginScope scope(infoUnpacker);
 
        PathContext tFiles;
-       if (pFiles)
+       if (pFiles != nullptr)
                tFiles = *pFiles;
        bool bRO[3] = {0};
        if (dwFlags)
@@ -909,7 +1043,7 @@ bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
        paths::PATH_EXISTENCE pathsType = paths::GetPairComparability(tFiles, IsArchiveFile);
        if (pathsType == paths::DOES_NOT_EXIST)
        {
-               if (!m_pMenus[MENU_OPENVIEW])
+               if (m_pMenus[MENU_OPENVIEW] == nullptr)
                        theApp.m_pOpenTemplate->m_hMenuShared = NewOpenViewMenu();
                COpenDoc *pOpenDoc = static_cast<COpenDoc *>(theApp.m_pOpenTemplate->CreateNewDocument());
                if (dwFlags)
@@ -922,9 +1056,9 @@ bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
                pOpenDoc->m_bRecurse = bRecurse;
                if (infoUnpacker)
                        pOpenDoc->m_infoHandler = *infoUnpacker;
-               CFrameWnd *pFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, NULL);
+               CFrameWnd *pFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, nullptr);
                theApp.m_pOpenTemplate->InitialUpdateFrame(pFrame, pOpenDoc);
-               return TRUE;
+               return true;
        }
        else
        {
@@ -951,7 +1085,7 @@ bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
                }
        }
 
-       CTempPathContext *pTempPathContext = NULL;
+       CTempPathContext *pTempPathContext = nullptr;
        if (pathsType == paths::IS_EXISTING_DIR)
        {
                DecompressResult res= DecompressArchive(m_hWnd, tFiles);
@@ -965,14 +1099,14 @@ bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
 
        // Determine if we want a new dirview open, now that we know if it was
        // an archive. Don't open a new dirview if we are comparing files.
-       if (!pDirDoc)
+       if (pDirDoc == nullptr)
        {
                if (pathsType == paths::IS_EXISTING_DIR)
                {
                        CDirDoc::m_nDirsTemp = tFiles.GetSize();
-                       if (!m_pMenus[MENU_DIRVIEW])
+                       if (m_pMenus[MENU_DIRVIEW] == nullptr)
                                theApp.m_pDirTemplate->m_hMenuShared = NewDirViewMenu();
-                       pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->OpenDocumentFile(NULL));
+                       pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->OpenDocumentFile(nullptr));
                }
                else
                {
@@ -983,7 +1117,7 @@ bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
        // open the diff
        if (pathsType == paths::IS_EXISTING_DIR)
        {
-               if (pDirDoc)
+               if (pDirDoc != nullptr)
                {
                        // Anything that can go wrong inside InitCompare() will yield an
                        // exception. There is no point in checking return value.
@@ -991,7 +1125,7 @@ bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
 
                        pDirDoc->SetReportFile(sReportFile);
                        pDirDoc->SetDescriptions(strDesc);
-                       pDirDoc->SetTitle(NULL);
+                       pDirDoc->SetTitle(nullptr);
                        for (int nIndex = 0; nIndex < tFiles.GetSize(); nIndex++)
                                pDirDoc->SetReadOnly(nIndex, bRO[nIndex]);
 
@@ -1015,13 +1149,24 @@ bool CMainFrame::DoFileOpen(const PathContext * pFiles /*=NULL*/,
                                infoUnpacker);
        }
 
-       if (pFiles && (!dwFlags || !(dwFlags[0] & FFILEOPEN_NOMRU)))
+       if (pFiles != nullptr && (!dwFlags || !(dwFlags[0] & FFILEOPEN_NOMRU)))
        {
                String filter = GetOptionsMgr()->GetString(OPT_FILEFILTER_CURRENT);
                AddToRecentDocs(*pFiles, (unsigned *)dwFlags, bRecurse, filter);
        }
 
-       return TRUE;
+       return true;
+}
+
+bool CMainFrame::DoFileOpen(UINT nID, const PathContext* pFiles /*= nullptr*/,
+       const DWORD dwFlags[] /*= nullptr*/, const String strDesc[] /*= nullptr*/)
+{
+       CDirDoc* pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
+       FileLocation fileloc[3];
+       for (int pane = 0; pane < pFiles->GetSize(); pane++)
+               fileloc[pane].setPath((*pFiles)[pane]);
+       return ShowMergeDoc(nID, pDirDoc, pFiles->GetSize(), fileloc,
+               dwFlags, strDesc);
 }
 
 void CMainFrame::UpdateFont(FRAMETYPE frame)
@@ -1030,10 +1175,10 @@ void CMainFrame::UpdateFont(FRAMETYPE frame)
        {
                for (auto pDoc : GetAllDirDocs())
                {
-                       if (pDoc)
+                       if (pDoc != nullptr)
                        {
                                CDirView *pView = pDoc->GetMainView();
-                               if (pView)
+                               if (pView != nullptr)
                                        pView->SetFont(m_lfDir);
                        }
                }
@@ -1043,7 +1188,7 @@ void CMainFrame::UpdateFont(FRAMETYPE frame)
                for (auto pDoc : GetAllMergeDocs())
                {
                        CMergeDoc *pMergeDoc = dynamic_cast<CMergeDoc *>(pDoc);
-                       if (pMergeDoc)
+                       if (pMergeDoc != nullptr)
                                for (auto& pView: pMergeDoc->GetViewList())
                                        pView->SetFont(m_lfDiff);
                }
@@ -1063,7 +1208,7 @@ void CMainFrame::OnViewSelectfont()
 {
        FRAMETYPE frame = GetFrameType(GetActiveFrame());
        CHOOSEFONT cf = { sizeof CHOOSEFONT };
-       LOGFONT *lf = NULL;
+       LOGFONT *lf = nullptr;
        cf.Flags = CF_INITTOLOGFONTSTRUCT|CF_FORCEFONTEXIST|CF_SCREENFONTS;
        if (frame == FRAME_FILE)
                cf.Flags |= CF_FIXEDPITCHONLY; // Only fixed-width fonts for merge view
@@ -1123,6 +1268,8 @@ void CMainFrame::UpdateResources()
                pDoc->UpdateResources();
        for (auto pDoc : GetAllOpenDocs())
                pDoc->UpdateResources();
+       for (auto pFrame: GetAllImgMergeFrames())
+               pFrame->UpdateResources();
 }
 
 /**
@@ -1156,7 +1303,7 @@ void CMainFrame::ActivateFrame(int nCmdShow)
 
        m_bFirstTime = false;
 
-       WINDOWPLACEMENT wp;
+       WINDOWPLACEMENT wp = {};
        wp.length = sizeof(WINDOWPLACEMENT);
        GetWindowPlacement(&wp);
        wp.rcNormalPosition.left=theApp.GetProfileInt(_T("Settings"), _T("MainLeft"),0);
@@ -1214,7 +1361,7 @@ void CMainFrame::OnClose()
        GetOptionsMgr()->SaveOption(OPT_FILEFILTER_CURRENT, filter);
 
        // save main window position
-       WINDOWPLACEMENT wp;
+       WINDOWPLACEMENT wp = {};
        wp.length = sizeof(WINDOWPLACEMENT);
        GetWindowPlacement(&wp);
        theApp.WriteProfileInt(_T("Settings"), _T("MainLeft"),wp.rcNormalPosition.left);
@@ -1223,17 +1370,10 @@ void CMainFrame::OnClose()
        theApp.WriteProfileInt(_T("Settings"), _T("MainBottom"),wp.rcNormalPosition.bottom);
        theApp.WriteProfileInt(_T("Settings"), _T("MainMax"), (wp.showCmd == SW_MAXIMIZE));
 
-       // Close Non-Document/View frame with confirmation
-       CMDIChildWnd *pChild = static_cast<CMDIChildWnd *>(CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD));
-       while (pChild)
+       for (auto pFrame: GetAllImgMergeFrames())
        {
-               CMDIChildWnd *pNextChild = static_cast<CMDIChildWnd *>(pChild->GetWindow(GW_HWNDNEXT));
-               if (GetFrameType(pChild) == FRAME_IMGFILE)
-               {
-                       if (!static_cast<CImgMergeFrame *>(pChild)->CloseNow())
-                               return;
-               }
-               pChild = pNextChild;
+               if (!pFrame->CloseNow())
+                       return;
        }
 
        CMDIFrameWnd::OnClose();
@@ -1268,7 +1408,7 @@ void CMainFrame::ApplyDiffOptions()
                // Re-read MergeDoc settings (also updates view settings)
                // and rescan using new options
                pMergeDoc->RefreshOptions();
-               pMergeDoc->FlushAndRescan(TRUE);
+               pMergeDoc->FlushAndRescan(true);
        }
 }
 
@@ -1296,17 +1436,32 @@ HexMergeDocList &CMainFrame::GetAllHexMergeDocs()
        return static_cast<HexMergeDocList &>(GetDocList(theApp.m_pHexMergeTemplate));
 }
 
+std::list<CImgMergeFrame *> CMainFrame::GetAllImgMergeFrames()
+{
+       std::list<CImgMergeFrame *> list;
+       // Close Non-Document/View frame with confirmation
+       CMDIChildWnd *pChild = static_cast<CMDIChildWnd *>(CWnd::FromHandle(m_hWndMDIClient)->GetWindow(GW_CHILD));
+       while (pChild != nullptr)
+       {
+               CMDIChildWnd *pNextChild = static_cast<CMDIChildWnd *>(pChild->GetWindow(GW_HWNDNEXT));
+               if (GetFrameType(pChild) == FRAME_IMGFILE)
+                       list.push_back(static_cast<CImgMergeFrame *>(pChild));
+               pChild = pNextChild;
+       }
+       return list;
+}
+
 /**
  * @brief Obtain a merge doc to display a difference in files.
  * @return Pointer to CMergeDoc to use. 
  */
 template<class DocClass>
-DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles)
+DocClass * GetMergeDocForDiff(CMultiDocTemplate *pTemplate, CDirDoc *pDirDoc, int nFiles, bool bMakeVisible)
 {
        // Create a new merge doc
        DocClass::m_nBuffersTemp = nFiles;
-       DocClass *pMergeDoc = static_cast<DocClass*>(pTemplate->OpenDocumentFile(NULL));
-       if (pMergeDoc)
+       DocClass *pMergeDoc = static_cast<DocClass*>(pTemplate->OpenDocumentFile(nullptr, bMakeVisible));
+       if (pMergeDoc != nullptr)
        {
                pDirDoc->AddMergeDoc(pMergeDoc);
                pMergeDoc->SetDirDoc(pDirDoc);
@@ -1338,8 +1493,10 @@ void CMainFrame::OnDropFiles(const std::vector<String>& dropped_files)
        PathContext tFiles(dropped_files);
        const size_t fileCount = tFiles.GetSize();
 
-       // If Ctrl pressed, do recursive compare
-       bool recurse = !!::GetAsyncKeyState(VK_CONTROL) || GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
+       bool recurse = GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
+       // Do a reverse comparison with the current 'Include Subfolders' settings when pressing Control key
+       if (::GetAsyncKeyState(VK_CONTROL) & 0x8000)
+               recurse = !recurse;
 
        // If user has <Shift> pressed with one file selected,
        // assume it is an archive and set filenames to same
@@ -1364,7 +1521,7 @@ void CMainFrame::OnDropFiles(const std::vector<String>& dropped_files)
                }
        }
 
-       DoFileOpen(&tFiles, dwFlags, NULL, _T(""), recurse);
+       DoFileOpen(&tFiles, dwFlags, nullptr, _T(""), recurse);
 }
 
 void CMainFrame::OnPluginUnpackMode(UINT nID )
@@ -1372,13 +1529,13 @@ void CMainFrame::OnPluginUnpackMode(UINT nID )
        switch (nID)
        {
        case ID_UNPACK_MANUAL:
-               FileTransform::g_bUnpackerMode = PLUGIN_MANUAL;
+               FileTransform::g_UnpackerMode = PLUGIN_MODE::PLUGIN_MANUAL;
                break;
        case ID_UNPACK_AUTO:
-               FileTransform::g_bUnpackerMode = PLUGIN_AUTO;
+               FileTransform::g_UnpackerMode = PLUGIN_MODE::PLUGIN_AUTO;
                break;
        }
-       theApp.WriteProfileInt(_T("Settings"), _T("UnpackerMode"), FileTransform::g_bUnpackerMode);
+       GetOptionsMgr()->SaveOption(OPT_PLUGINS_UNPACKER_MODE, static_cast<int>(FileTransform::g_UnpackerMode));
 }
 
 void CMainFrame::OnUpdatePluginUnpackMode(CCmdUI* pCmdUI) 
@@ -1386,27 +1543,27 @@ void CMainFrame::OnUpdatePluginUnpackMode(CCmdUI* pCmdUI)
        pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
 
        if (pCmdUI->m_nID == ID_UNPACK_MANUAL)
-               pCmdUI->SetRadio(PLUGIN_MANUAL == FileTransform::g_bUnpackerMode);
+               pCmdUI->SetRadio(PLUGIN_MODE::PLUGIN_MANUAL == FileTransform::g_UnpackerMode);
        if (pCmdUI->m_nID == ID_UNPACK_AUTO)
-               pCmdUI->SetRadio(PLUGIN_AUTO == FileTransform::g_bUnpackerMode);
+               pCmdUI->SetRadio(PLUGIN_MODE::PLUGIN_AUTO == FileTransform::g_UnpackerMode);
 }
 void CMainFrame::OnPluginPrediffMode(UINT nID )
 {
        switch (nID)
        {
        case ID_PREDIFFER_MANUAL:
-               FileTransform::g_bPredifferMode = PLUGIN_MANUAL;
+               FileTransform::g_PredifferMode = PLUGIN_MODE::PLUGIN_MANUAL;
                break;
        case ID_PREDIFFER_AUTO:
-               FileTransform::g_bPredifferMode = PLUGIN_AUTO;
+               FileTransform::g_PredifferMode = PLUGIN_MODE::PLUGIN_AUTO;
                break;
        }
        PrediffingInfo infoPrediffer;
        for (auto pMergeDoc : GetAllMergeDocs())
                pMergeDoc->SetPrediffer(&infoPrediffer);
        for (auto pDirDoc : GetAllDirDocs())
-               pDirDoc->GetPluginManager().SetPrediffSettingAll(FileTransform::g_bPredifferMode);
-       theApp.WriteProfileInt(_T("Settings"), _T("PredifferMode"), FileTransform::g_bPredifferMode);
+               pDirDoc->GetPluginManager().SetPrediffSettingAll(FileTransform::g_PredifferMode);
+       GetOptionsMgr()->SaveOption(OPT_PLUGINS_PREDIFFER_MODE, static_cast<int>(FileTransform::g_PredifferMode));
 }
 
 void CMainFrame::OnUpdatePluginPrediffMode(CCmdUI* pCmdUI) 
@@ -1414,9 +1571,9 @@ void CMainFrame::OnUpdatePluginPrediffMode(CCmdUI* pCmdUI)
        pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED));
 
        if (pCmdUI->m_nID == ID_PREDIFFER_MANUAL)
-               pCmdUI->SetRadio(PLUGIN_MANUAL == FileTransform::g_bPredifferMode);
+               pCmdUI->SetRadio(PLUGIN_MODE::PLUGIN_MANUAL == FileTransform::g_PredifferMode);
        if (pCmdUI->m_nID == ID_PREDIFFER_AUTO)
-               pCmdUI->SetRadio(PLUGIN_AUTO == FileTransform::g_bPredifferMode);
+               pCmdUI->SetRadio(PLUGIN_MODE::PLUGIN_AUTO == FileTransform::g_PredifferMode);
 }
 /**
  * @brief Called when "Reload Plugins" item is updated
@@ -1434,7 +1591,7 @@ void CMainFrame::OnReloadPlugins()
 
        // update the editor scripts submenu
        HMENU scriptsSubmenu = GetScriptsSubmenu(m_hMenuDefault);
-       if (scriptsSubmenu != NULL)
+       if (scriptsSubmenu != nullptr)
                CMergeEditView::createScriptsSubmenu(scriptsSubmenu);
        UpdatePrediffersMenu();
 }
@@ -1444,15 +1601,15 @@ CMergeEditView * CMainFrame::GetActiveMergeEditView()
 {
        // NB: GetActiveDocument does not return the Merge Doc 
        //     even when the merge edit view is in front
-       // NB: CChildFrame::GetActiveView returns NULL when location view active
+       // NB: CMergeEditFrame::GetActiveView returns `nullptr` when location view active
        // So we have this rather complicated logic to try to get a merge edit view
        // We look at the front child window, which should be a frame
-       // and we can get a MergeEditView from it, if it is a CChildFrame
+       // and we can get a MergeEditView from it, if it is a CMergeEditFrame
        // (DirViews use a different frame type)
-       CChildFrame * pFrame = dynamic_cast<CChildFrame *>(GetActiveFrame());
-       if (!pFrame) return 0;
+       CMergeEditFrame * pFrame = dynamic_cast<CMergeEditFrame *>(GetActiveFrame());
+       if (pFrame == nullptr) return nullptr;
        // Try to get the active MergeEditView (ie, left or right)
-       if (pFrame->GetActiveView() && pFrame->GetActiveView()->IsKindOf(RUNTIME_CLASS(CMergeEditView)))
+       if (pFrame->GetActiveView() != nullptr && pFrame->GetActiveView()->IsKindOf(RUNTIME_CLASS(CMergeEditView)))
        {
                return dynamic_cast<CMergeEditView *>(pFrame->GetActiveView());
        }
@@ -1462,17 +1619,17 @@ CMergeEditView * CMainFrame::GetActiveMergeEditView()
 void CMainFrame::UpdatePrediffersMenu()
 {
        CMenu* menu = GetMenu();
-       if (menu == NULL)
+       if (menu == nullptr)
        {
                return;
        }
 
        HMENU hMainMenu = menu->m_hMenu;
        HMENU prediffersSubmenu = GetPrediffersSubmenu(hMainMenu);
-       if (prediffersSubmenu != NULL)
+       if (prediffersSubmenu != nullptr)
        {
                CMergeEditView * pEditView = GetActiveMergeEditView();
-               if (pEditView)
+               if (pEditView != nullptr)
                        pEditView->createPrediffersSubmenu(prediffersSubmenu);
                else
                {
@@ -1480,7 +1637,7 @@ void CMainFrame::UpdatePrediffersMenu()
                        int i = GetMenuItemCount(prediffersSubmenu);
                        while (i --)
                                ::DeleteMenu(prediffersSubmenu, 0, MF_BYPOSITION);
-                       ::AppendMenu(prediffersSubmenu, MF_SEPARATOR, 0, NULL);
+                       ::AppendMenu(prediffersSubmenu, MF_SEPARATOR, 0, nullptr);
                }
        }
 }
@@ -1496,7 +1653,7 @@ void CMainFrame::OnSaveConfigData()
        if (configLog.WriteLogFile(sError))
        {
                String sFileName = configLog.GetFileName();
-               theApp.OpenFileToExternalEditor(sFileName);
+               CMergeApp::OpenFileToExternalEditor(sFileName);
        }
        else
        {
@@ -1516,7 +1673,7 @@ void CMainFrame::OnSaveConfigData()
  * @sa CMergeDoc::OpenDocs()
  * @sa CMergeDoc::TrySaveAs()
  */
-void CMainFrame::FileNew(int nPanes) 
+void CMainFrame::FileNew(int nPanes, FRAMETYPE frameType, bool table
 {
        CDirDoc *pDirDoc = static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
        
@@ -1531,7 +1688,6 @@ void CMainFrame::FileNew(int nPanes)
                strDesc[1] = _("Untitled right");
                fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
                fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
-               ShowMergeDoc(pDirDoc, 2, fileloc, dwFlags, strDesc);
        }
        else
        {
@@ -1541,8 +1697,15 @@ void CMainFrame::FileNew(int nPanes)
                fileloc[0].encoding.SetCodepage(ucr::getDefaultCodepage());
                fileloc[1].encoding.SetCodepage(ucr::getDefaultCodepage());
                fileloc[2].encoding.SetCodepage(ucr::getDefaultCodepage());
-               ShowMergeDoc(pDirDoc, 3, fileloc, dwFlags, strDesc);
        }
+       UINT nID = ID_MERGE_COMPARE_TEXT;
+       switch (frameType)
+       {
+       case FRAME_FILE: nID = !table ? ID_MERGE_COMPARE_TEXT : ID_MERGE_COMPARE_TABLE; break;
+       case FRAME_HEXFILE: nID = ID_MERGE_COMPARE_HEX; break;
+       case FRAME_IMGFILE: nID = ID_MERGE_COMPARE_IMAGE; break;
+       }
+       ShowMergeDoc(nID, pDirDoc, nPanes, fileloc, dwFlags, strDesc);
 }
 
 /**
@@ -1555,14 +1718,10 @@ void CMainFrame::FileNew(int nPanes)
  * @sa CMergeDoc::OpenDocs()
  * @sa CMergeDoc::TrySaveAs()
  */
+template <int nFiles, CMainFrame::FRAMETYPE frameType, bool table>
 void CMainFrame::OnFileNew() 
 {
-       FileNew(2);
-}
-
-void CMainFrame::OnFileNew3() 
-{
-       FileNew(3);
+       FileNew(nFiles, frameType, table);
 }
 
 /**
@@ -1573,20 +1732,21 @@ void CMainFrame::OnToolsFilters()
        String title = _("Filters");
        CPropertySheet sht(title.c_str());
        LineFiltersDlg lineFiltersDlg;
+       SubstitutionFiltersDlg substitutionFiltersDlg;
        FileFiltersDlg fileFiltersDlg;
-       vector<FileFilterInfo> fileFilters;
        std::unique_ptr<LineFiltersList> lineFilters(new LineFiltersList());
+       std::unique_ptr<SubstitutionFiltersList> SubstitutionFilters(new SubstitutionFiltersList());
        String selectedFilter;
        const String origFilter = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
        sht.AddPage(&fileFiltersDlg);
        sht.AddPage(&lineFiltersDlg);
+       sht.AddPage(&substitutionFiltersDlg);
        sht.m_psh.dwFlags |= PSH_NOAPPLYNOW; // Hide 'Apply' button since we don't need it
 
        // Make sure all filters are up-to-date
        theApp.m_pGlobalFileFilter->ReloadUpdatedFilters();
 
-       theApp.m_pGlobalFileFilter->GetFileFilters(&fileFilters, selectedFilter);
-       fileFiltersDlg.SetFilterArray(&fileFilters);
+       fileFiltersDlg.SetFilterArray(theApp.m_pGlobalFileFilter->GetFileFilters(selectedFilter));
        fileFiltersDlg.SetSelected(selectedFilter);
        const bool lineFiltersEnabledOrig = GetOptionsMgr()->GetBool(OPT_LINEFILTER_ENABLED);
        lineFiltersDlg.m_bIgnoreRegExp = lineFiltersEnabledOrig;
@@ -1594,6 +1754,11 @@ void CMainFrame::OnToolsFilters()
        lineFilters->CloneFrom(theApp.m_pLineFilters.get());
        lineFiltersDlg.SetList(lineFilters.get());
 
+       SubstitutionFilters->CloneFrom(theApp.m_pSubstitutionFiltersList.get());
+       substitutionFiltersDlg.SetList(SubstitutionFilters.get());
+
+       sht.SetActivePage(AfxGetApp()->GetProfileInt(_T("Settings"), _T("FilterStartPage"), 0));
+
        if (sht.DoModal() == IDOK)
        {
                String strNone = _("<None>");
@@ -1625,8 +1790,12 @@ void CMainFrame::OnToolsFilters()
                FRAMETYPE frame = GetFrameType(pFrame);
                if (frame == FRAME_FILE)
                {
-                       if (lineFiltersEnabledOrig != linefiltersEnabled ||
-                                       !theApp.m_pLineFilters->Compare(lineFilters.get()))
+                       if
+                       (
+                                  linefiltersEnabled != lineFiltersEnabledOrig
+                               || !lineFilters->Compare(theApp.m_pLineFilters.get())
+                               || !SubstitutionFilters->Compare(theApp.m_pSubstitutionFiltersList.get())
+                       )
                        {
                                bFileCompareRescan = true;
                        }
@@ -1647,10 +1816,13 @@ void CMainFrame::OnToolsFilters()
                theApp.m_pLineFilters->CloneFrom(lineFilters.get());
                theApp.m_pLineFilters->SaveFilters();
 
+               theApp.m_pSubstitutionFiltersList->CloneFrom(SubstitutionFilters.get());
+               theApp.m_pSubstitutionFiltersList->SaveFilters();
+
                if (bFileCompareRescan)
                {
                        for (auto pMergeDoc : GetAllMergeDocs())
-                               pMergeDoc->FlushAndRescan(TRUE);
+                               pMergeDoc->FlushAndRescan(true);
                }
                else if (bFolderCompareRescan)
                {
@@ -1683,15 +1855,24 @@ BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
        {
                if (theApp.m_bEscShutdown && m_wndTabBar.GetItemCount() <= 1)
                {
-                       AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
+                       AfxGetMainWnd()->SendMessage(WM_COMMAND, ID_APP_EXIT);
                        return TRUE;
                }
-               else if (GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC) && m_wndTabBar.GetItemCount() == 0)
+               else if (GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC) == 1 && m_wndTabBar.GetItemCount() == 0)
                {
                        AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
                        return FALSE;
                }
        }
+
+       if (WM_KEYDOWN == pMsg->message && VK_TAB == pMsg->wParam && GetAsyncKeyState(VK_CONTROL) < 0 && m_arrChild.GetSize() > 1)
+       {
+               CWindowsManagerDialog* pDlg = new CWindowsManagerDialog;
+               pDlg->Create(CWindowsManagerDialog::IDD, this);
+               pDlg->ShowWindow(SW_SHOW);
+               return TRUE;
+       }
+
        return CMDIFrameWnd::PreTranslateMessage(pMsg);
 }
 
@@ -1743,7 +1924,7 @@ void CMainFrame::OnResizePanes()
        GetOptionsMgr()->SaveOption(OPT_RESIZE_PANES, bResize);
        // TODO: Introduce a common merge frame superclass?
        CFrameWnd *pActiveFrame = GetActiveFrame();
-       if (CChildFrame *pFrame = DYNAMIC_DOWNCAST(CChildFrame, pActiveFrame))
+       if (CMergeEditFrame *pFrame = DYNAMIC_DOWNCAST(CMergeEditFrame, pActiveFrame))
        {
                pFrame->UpdateAutoPaneResize();
                if (bResize)
@@ -1774,7 +1955,7 @@ void CMainFrame::OnFileOpenProject()
        // store this as the new project path
        GetOptionsMgr()->SaveOption(OPT_PROJECTS_PATH, strProjectPath);
 
-       theApp.LoadAndOpenProjectFile(sFilepath.c_str());
+       theApp.LoadAndOpenProjectFile(sFilepath);
 }
 
 /**
@@ -1801,15 +1982,8 @@ LRESULT CMainFrame::OnCopyData(WPARAM wParam, LPARAM lParam)
 
 LRESULT CMainFrame::OnUser1(WPARAM wParam, LPARAM lParam)
 {
-       CFrameWnd * pFrame = GetActiveFrame();
-       if (pFrame)
-       {
-               IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
-               if (!pMergeDoc)
-                       pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
-               if (pMergeDoc)
-                       pMergeDoc->CheckFileChanged();
-       }
+       if (IMergeDoc *pMergeDoc = GetActiveIMergeDoc())
+               pMergeDoc->CheckFileChanged();
        return 0;
 }
 
@@ -1821,10 +1995,10 @@ LRESULT CMainFrame::OnUser1(WPARAM wParam, LPARAM lParam)
 void CMainFrame::OnWindowCloseAll()
 {
        CMDIChildWnd *pChild = MDIGetActive();
-       while (pChild)
+       while (pChild != nullptr)
        {
                CDocument* pDoc;
-               if ((pDoc = pChild->GetActiveDocument()) != NULL)
+               if ((pDoc = pChild->GetActiveDocument()) != nullptr)
                {
                        if (!pDoc->SaveModified())
                                return;
@@ -1849,15 +2023,7 @@ void CMainFrame::OnWindowCloseAll()
  */ 
 void CMainFrame::OnUpdateWindowCloseAll(CCmdUI* pCmdUI)
 {
-       const MergeDocList &mergedocs = GetAllMergeDocs();
-       if (!mergedocs.IsEmpty())
-       {
-               pCmdUI->Enable(TRUE);
-               return;
-       }
-
-       const DirDocList &dirdocs = GetAllDirDocs();
-       pCmdUI->Enable(!dirdocs.IsEmpty());
+       pCmdUI->Enable(MDIGetActive() != nullptr);
 }
 
 /**
@@ -1879,7 +2045,7 @@ CMainFrame * GetMainFrame()
  */
 void CMainFrame::OnSaveProject()
 {
-       if (!m_pMenus[MENU_OPENVIEW])
+       if (m_pMenus[MENU_OPENVIEW] == nullptr)
                theApp.m_pOpenTemplate->m_hMenuShared = NewOpenViewMenu();
        COpenDoc *pOpenDoc = static_cast<COpenDoc *>(theApp.m_pOpenTemplate->CreateNewDocument());
 
@@ -1891,7 +2057,7 @@ void CMainFrame::OnSaveProject()
        {
                CMergeDoc * pMergeDoc = static_cast<CMergeDoc *>(pFrame->GetActiveDocument());
                pOpenDoc->m_files = pMergeDoc->m_filePaths;
-               for (size_t pane = 0; pane < pOpenDoc->m_files.size(); ++pane)
+               for (int pane = 0; pane < pOpenDoc->m_files.GetSize(); ++pane)
                        pOpenDoc->m_dwFlags[pane] = FFILEOPEN_PROJECT | (pMergeDoc->m_ptBuf[pane]->GetReadOnly() ? FFILEOPEN_PROJECT : 0);
                pOpenDoc->m_bRecurse = GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS);
                pOpenDoc->m_strExt = theApp.m_pGlobalFileFilter->GetFilterNameOrMask();
@@ -1912,7 +2078,7 @@ void CMainFrame::OnSaveProject()
                pOpenDoc->m_strExt = static_cast<FileFilterHelper *>(ctxt.m_piFilterGlobal)->GetFilterNameOrMask();
        }
 
-       CFrameWnd *pOpenFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, NULL);
+       CFrameWnd *pOpenFrame = theApp.m_pOpenTemplate->CreateNewFrame(pOpenDoc, nullptr);
        theApp.m_pOpenTemplate->InitialUpdateFrame(pOpenFrame, pOpenDoc);
 }
 
@@ -1923,7 +2089,7 @@ void CMainFrame::StartFlashing()
 {
        CWnd * activeWindow = GetActiveWindow();
        if (activeWindow != this)
-               FlashWindowEx(FLASHW_ALL | FLASHW_TIMERNOFG, 0, 0);
+               FlashWindowEx(FLASHW_ALL | FLASHW_TIMERNOFG, 3, 0);
 }
 
 #if _MFC_VER > 0x0600
@@ -1938,15 +2104,8 @@ void CMainFrame::OnActivateApp(BOOL bActive, HTASK hTask)
        CMDIFrameWnd::OnActivateApp(bActive, hTask);
 #endif
 
-       CFrameWnd * pFrame = GetActiveFrame();
-       if (pFrame)
-       {
-               IMergeDoc *pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame->GetActiveDocument());
-               if (!pMergeDoc)
-                       pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
-               if (pMergeDoc)
-                       PostMessage(WM_USER+1);
-       }
+       if (IMergeDoc *pMergeDoc = GetActiveIMergeDoc())
+               PostMessage(WM_USER+1);
 }
 
 BOOL CMainFrame::CreateToolbar()
@@ -1957,7 +2116,8 @@ BOOL CMainFrame::CreateToolbar()
                return FALSE;
        }
 
-       if (!m_wndReBar.Create(this))
+       if (!m_wndReBar.Create(this, RBS_BANDBORDERS,
+               WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | CBRS_ALIGN_TOP))
        {
                return FALSE;
        }
@@ -1974,11 +2134,14 @@ BOOL CMainFrame::CreateToolbar()
        LoadToolbarImages();
 
        UINT nID, nStyle;
-       int iImage;
-       int index = m_wndToolBar.GetToolBarCtrl().CommandToIndex(ID_OPTIONS);
-       m_wndToolBar.GetButtonInfo(index, nID, nStyle, iImage);
-       nStyle |= TBSTYLE_DROPDOWN;
-       m_wndToolBar.SetButtonInfo(index, nID, nStyle, iImage);
+       for (auto cmd : { ID_OPTIONS, ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE })
+       {
+               int iImage;
+               int index = m_wndToolBar.GetToolBarCtrl().CommandToIndex(cmd);
+               m_wndToolBar.GetButtonInfo(index, nID, nStyle, iImage);
+               nStyle |= TBSTYLE_DROPDOWN;
+               m_wndToolBar.SetButtonInfo(index, nID, nStyle, iImage);
+       }
 
        if (!GetOptionsMgr()->GetBool(OPT_SHOW_TOOLBAR))
        {
@@ -1991,28 +2154,28 @@ BOOL CMainFrame::CreateToolbar()
 /** @brief Load toolbar images from the resource. */
 void CMainFrame::LoadToolbarImages()
 {
-       const int toolbarSize = 16 << GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE);
+       const int toolbarNewImgSize = MulDiv(16, GetSystemMetrics(SM_CXSMICON), 16) * (1 + GetOptionsMgr()->GetInt(OPT_TOOLBAR_SIZE));
+       const int toolbarOrgImgSize = toolbarNewImgSize <= 20 ? 16 : 32;
        CToolBarCtrl& BarCtrl = m_wndToolBar.GetToolBarCtrl();
 
-       m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].DeleteImageList();
-       m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].DeleteImageList();
+       m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].Detach();
+       m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].Detach();
        CSize sizeButton(0, 0);
 
-       LoadToolbarImageList(toolbarSize,
-               toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
-               toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED_MASK : IDB_TOOLBAR_ENABLED_MASK32,
+       LoadToolbarImageList(toolbarOrgImgSize, toolbarNewImgSize,
+               toolbarOrgImgSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
                false, m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
-       LoadToolbarImageList(toolbarSize,
-               toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
-               toolbarSize <= 16 ? IDB_TOOLBAR_ENABLED_MASK : IDB_TOOLBAR_ENABLED_MASK32,
+       LoadToolbarImageList(toolbarOrgImgSize, toolbarNewImgSize,
+               toolbarOrgImgSize <= 16 ? IDB_TOOLBAR_ENABLED : IDB_TOOLBAR_ENABLED32,
                true, m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
-       sizeButton = CSize(toolbarSize + 8, toolbarSize + 8);
+
+       sizeButton = CSize(toolbarNewImgSize + 8, toolbarNewImgSize + 8);
 
        BarCtrl.SetButtonSize(sizeButton);
-       BarCtrl.SetImageList(&m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]);
-       BarCtrl.SetDisabledImageList(&m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]);
-       m_ToolbarImages[TOOLBAR_IMAGES_ENABLED].Detach();
-       m_ToolbarImages[TOOLBAR_IMAGES_DISABLED].Detach();
+       if (CImageList *pImgList = BarCtrl.SetImageList(&m_ToolbarImages[TOOLBAR_IMAGES_ENABLED]))
+               pImgList->DeleteImageList();
+       if (CImageList *pImgList = BarCtrl.SetDisabledImageList(&m_ToolbarImages[TOOLBAR_IMAGES_DISABLED]))
+               pImgList->DeleteImageList();
 
        // resize the rebar.
        REBARBANDINFO rbbi = { sizeof REBARBANDINFO };
@@ -2023,29 +2186,27 @@ void CMainFrame::LoadToolbarImages()
 
 
 /**
- * @brief Load a transparent 24-bit color image list.
+ * @brief Load a transparent 32-bit color image list.
  */
-static void LoadHiColImageList(UINT nIDResource, UINT nIDResourceMask, int nWidth, int nHeight, int nCount, bool bGrayscale, CImageList& ImgList)
+static void LoadHiColImageList(UINT nIDResource, int nWidth, int nHeight, int nNewWidth, int nNewHeight, int nCount, bool bGrayscale, CImageList& ImgList)
 {
-       CBitmap bm, bmMask;
-       bm.Attach(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResource), IMAGE_BITMAP, nWidth * nCount, nHeight, LR_DEFAULTCOLOR));
-       bmMask.Attach(LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(nIDResourceMask), IMAGE_BITMAP, nWidth * nCount, nHeight, LR_MONOCHROME));
-       if (bGrayscale)
-               GrayScale(&bm);
-       VERIFY(ImgList.Create(nWidth, nHeight, ILC_COLORDDB|ILC_MASK, nCount, 0));
-       int nIndex = ImgList.Add(&bm, &bmMask);
+       CBitmap bm;
+       bm.Attach(LoadBitmapAndConvertTo32bit(AfxGetInstanceHandle(), nIDResource, nNewWidth * nCount, nNewHeight, bGrayscale, RGB(0xff, 0, 0xff)));
+
+       VERIFY(ImgList.Create(nNewWidth, nNewHeight, ILC_COLOR32, nCount, 0));
+       int nIndex = ImgList.Add(&bm, nullptr);
        ASSERT(-1 != nIndex);
 }
 
 /**
  * @brief Load toolbar image list.
  */
-static void LoadToolbarImageList(int imageWidth, UINT nIDResource, UINT nIDResourceMask, bool bGrayscale,
-               CImageList& ImgList)
+static void LoadToolbarImageList(int orgImageWidth, int newImageWidth, UINT nIDResource, bool bGrayscale, CImageList& ImgList)
 {
-       const int ImageCount = 22;
-       const int imageHeight = imageWidth - 1;
-       LoadHiColImageList(nIDResource, nIDResourceMask, imageWidth, imageHeight, ImageCount, bGrayscale, ImgList);
+       const int ImageCount = 26;
+       const int orgImageHeight = orgImageWidth - 1;
+       const int newImageHeight = newImageWidth - 1;
+       LoadHiColImageList(nIDResource, orgImageWidth, orgImageHeight, newImageWidth, newImageHeight, ImageCount, bGrayscale, ImgList);
 }
 
 /**
@@ -2055,7 +2216,7 @@ void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
 {
        CFrameWnd::OnUpdateFrameTitle(bAddToTitle);
        
-       if (m_wndTabBar.m_hWnd)
+       if (m_wndTabBar.m_hWnd != nullptr)
                m_wndTabBar.UpdateTabs();
 }
 
@@ -2115,17 +2276,10 @@ BOOL CMainFrame::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
                // this is the command id, not the button index
                AfxExtractSubString(strTipText, strFullText.c_str(), 1, '\n');
        }
-#ifndef _UNICODE
-       if (pNMHDR->code == TTN_NEEDTEXTA)
-               lstrcpyn(pTTTA->szText, strTipText, countof(pTTTA->szText));
-       else
-               _mbstowcsz(pTTTW->szText, strTipText, countof(pTTTW->szText));
-#else
        if (pNMHDR->code == TTN_NEEDTEXTA)
-               _wcstombsz(pTTTA->szText, strTipText, countof(pTTTA->szText));
+               _wcstombsz(pTTTA->szText, strTipText, static_cast<ULONG>(std::size(pTTTA->szText)));
        else
-               lstrcpyn(pTTTW->szText, strTipText, countof(pTTTW->szText));
-#endif
+               lstrcpyn(pTTTW->szText, strTipText, static_cast<int>(std::size(pTTTW->szText)));
        *pResult = 0;
 
        // bring the tooltip window above other popup windows
@@ -2170,7 +2324,7 @@ bool CMainFrame::AskCloseConfirmation()
 void CMainFrame::OnHelpReleasenotes()
 {
        const String sPath = paths::ConcatPath(env::GetProgPath(), RelNotes);
-       ShellExecute(NULL, _T("open"), sPath.c_str(), NULL, NULL, SW_SHOWNORMAL);
+       shell::Open(sPath.c_str());
 }
 
 /**
@@ -2179,7 +2333,7 @@ void CMainFrame::OnHelpReleasenotes()
  */
 void CMainFrame::OnHelpTranslations()
 {
-       ShellExecute(NULL, _T("open"), TranslationsUrl, NULL, NULL, SW_SHOWNORMAL);
+       shell::Open(TranslationsUrl);
 }
 
 /**
@@ -2273,14 +2427,31 @@ bool CMainFrame::DoOpenConflict(const String& conflictFile, const String strDesc
        return conflictCompared;
 }
 
+bool CMainFrame::DoSelfCompare(UINT nID, const String& file, const String strDesc[] /*= nullptr*/)
+{
+       String ext = paths::FindExtension(file);
+       TempFilePtr wTemp(new TempFile());
+       String copiedFile = wTemp->Create(_T("self-compare_"), ext);
+       m_tempFiles.push_back(wTemp);
+
+       TFile(file).copyTo(copiedFile);
+
+       String strDesc2[2] = { 
+               (strDesc && !strDesc[0].empty()) ? strDesc[0] : _("Original File"),
+               (strDesc && !strDesc[1].empty()) ? strDesc[1] : _("") };
+       DWORD dwFlags[2] = {FFILEOPEN_READONLY | FFILEOPEN_NOMRU, FFILEOPEN_NOMRU};
+       PathContext tmpPathContext(copiedFile, file);
+       return DoFileOpen(nID, &tmpPathContext, dwFlags, strDesc2);
+}
+
 /**
  * @brief Get type of frame (File/Folder compare).
  * @param [in] pFrame Pointer to frame to check.
  * @return FRAMETYPE of the given frame.
 */
-CMainFrame::FRAMETYPE CMainFrame::GetFrameType(const CFrameWnd * pFrame) const
+CMainFrame::FRAMETYPE CMainFrame::GetFrameType(const CFrameWnd * pFrame)
 {
-       bool bMergeFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame));
+       bool bMergeFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CMergeEditFrame));
        bool bHexMergeFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame));
        bool bImgMergeFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame));
        bool bDirFrame = !!pFrame->IsKindOf(RUNTIME_CLASS(CDirFrame));
@@ -2306,12 +2477,28 @@ void CMainFrame::OnPluginsList()
        dlg.DoModal();
 }
 
-void CMainFrame::OnDiffOptionsDropDown(NMHDR* pNMHDR, LRESULT* pResult)
+void CMainFrame::OnToolbarButtonDropDown(NMHDR* pNMHDR, LRESULT* pResult)
 {
        LPNMTOOLBAR pToolBar = reinterpret_cast<LPNMTOOLBAR>(pNMHDR);
        ClientToScreen(&(pToolBar->rcButton));
        BCMenu menu;
-       VERIFY(menu.LoadMenu(IDR_POPUP_DIFF_OPTIONS));
+       int id;
+       switch (pToolBar->iItem)
+       {
+       case ID_FILE_NEW:
+               id = IDR_POPUP_NEW;
+               break;
+       case ID_FILE_OPEN:
+               id = IDR_POPUP_OPEN;
+               break;
+       case ID_FILE_SAVE:
+               id = IDR_POPUP_SAVE;
+               break;
+       default:
+               id = IDR_POPUP_DIFF_OPTIONS;
+               break;
+       }
+       VERIFY(menu.LoadMenu(id));
        theApp.TranslateMenu(menu.m_hMenu);
        CMenu* pPopup = menu.GetSubMenu(0);
        if (pPopup != nullptr)
@@ -2333,15 +2520,15 @@ void CMainFrame::OnUpdateDiffWhitespace(CCmdUI* pCmdUI)
        pCmdUI->Enable();
 }
 
-void CMainFrame::OnDiffCaseSensitive()
+void CMainFrame::OnDiffIgnoreCase()
 {
        GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_CASE, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
        ApplyDiffOptions();
 }
 
-void CMainFrame::OnUpdateDiffCaseSensitive(CCmdUI* pCmdUI)
+void CMainFrame::OnUpdateDiffIgnoreCase(CCmdUI* pCmdUI)
 {
-       pCmdUI->SetCheck(!GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
+       pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CASE));
        pCmdUI->Enable();
 }
 
@@ -2357,6 +2544,18 @@ void CMainFrame::OnUpdateDiffIgnoreEOL(CCmdUI* pCmdUI)
        pCmdUI->Enable();
 }
 
+void CMainFrame::OnDiffIgnoreCP()
+{
+       GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_CODEPAGE, !GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CODEPAGE));
+       ApplyDiffOptions();
+}
+
+void CMainFrame::OnUpdateDiffIgnoreCP(CCmdUI* pCmdUI)
+{
+       pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CODEPAGE));
+       pCmdUI->Enable();
+}
+
 void CMainFrame::OnIncludeSubfolders()
 {
        GetOptionsMgr()->SaveOption(OPT_CMP_INCLUDE_SUBDIRS, !GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS));
@@ -2387,7 +2586,7 @@ void CMainFrame::OnUpdateCompareMethod(CCmdUI* pCmdUI)
 void CMainFrame::OnMRUs(UINT nID)
 {
        std::vector<JumpList::Item> mrus = JumpList::GetRecentDocs(GetOptionsMgr()->GetInt(OPT_MRU_MAX));
-       const size_t idx = nID - ID_MRU_FIRST;
+       const size_t idx = static_cast<size_t>(nID) - ID_MRU_FIRST;
        if (idx < mrus.size())
        {
                MergeCmdLineInfo cmdInfo((_T("\"") + mrus[idx].path + _T("\" ") + mrus[idx].params).c_str());
@@ -2398,9 +2597,10 @@ void CMainFrame::OnMRUs(UINT nID)
 void CMainFrame::OnUpdateNoMRUs(CCmdUI* pCmdUI)
 {
        // append the MRU submenu
-       HMENU hMenu = GetSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu, ID_FILE_NEW, false);
-       if (hMenu == NULL)
+       CMenu *pMenu = pCmdUI->m_pSubMenu ? pCmdUI->m_pSubMenu : pCmdUI->m_pMenu;
+       if (pMenu == nullptr)
                return;
+       HMENU hMenu = pMenu->m_hMenu;
        
        // empty the menu
        size_t i = ::GetMenuItemCount(hMenu);
@@ -2436,6 +2636,94 @@ void CMainFrame::OnUpdatePluginName(CCmdUI* pCmdUI)
        pCmdUI->SetText(_T(""));
 }
 
+/**
+ * @brief Move to next file
+ */
+void CMainFrame::OnNextFile()
+{
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       pDirDoc->MoveToNextFile(pMergeDoc);
+}
+
+/**
+ * @brief Called when Move to next file is updated
+ */
+void CMainFrame::OnUpdateNextFile(CCmdUI* pCmdUI)
+{
+       bool enabled = false;
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       enabled = !pDirDoc->IsLastFile();
+       pCmdUI->Enable(enabled);
+}
+
+/**
+ * @brief Move to previous file
+ */
+void CMainFrame::OnPrevFile()
+{
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       pDirDoc->MoveToPrevFile(pMergeDoc);
+}
+
+/**
+ * @brief Called when Move to previous file is updated
+ */
+void CMainFrame::OnUpdatePrevFile(CCmdUI* pCmdUI)
+{
+       bool enabled = false;
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       enabled = !pDirDoc->IsFirstFile();
+       pCmdUI->Enable(enabled);
+}
+
+/**
+ * @brief Move to first file
+ */
+void CMainFrame::OnFirstFile()
+{
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       pDirDoc->MoveToFirstFile(pMergeDoc);
+}
+
+/**
+ * @brief Called when Move to first file is updated
+ */
+void CMainFrame::OnUpdateFirstFile(CCmdUI* pCmdUI)
+{
+       bool enabled = false;
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       enabled = !pDirDoc->IsFirstFile();
+       pCmdUI->Enable(enabled);
+}
+
+/**
+ * @brief Move to last file
+ */
+void CMainFrame::OnLastFile()
+{
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       pDirDoc->MoveToLastFile(pMergeDoc);
+}
+
+/**
+ * @brief Called when Move to last file item is updated
+ */
+void CMainFrame::OnUpdateLastFile(CCmdUI* pCmdUI)
+{
+       bool enabled = false;
+       if (IMergeDoc* pMergeDoc = GetActiveIMergeDoc())
+               if (CDirDoc* pDirDoc = pMergeDoc->GetDirDoc())
+                       enabled = !pDirDoc->IsLastFile();
+       pCmdUI->Enable(enabled);
+}
+
 void CMainFrame::ReloadMenu()
 {
        // set the menu of the main frame window
@@ -2446,7 +2734,7 @@ void CMainFrame::ReloadMenu()
        HMENU hNewMergeMenu = pMainFrame->NewMergeViewMenu();
        HMENU hNewImgMergeMenu = pMainFrame->NewImgMergeViewMenu();
        HMENU hNewDirMenu = pMainFrame->NewDirViewMenu();
-       if (hNewDefaultMenu && hNewMergeMenu && hNewDirMenu)
+       if (hNewDefaultMenu != nullptr && hNewMergeMenu != nullptr && hNewDirMenu != nullptr)
        {
                // Note : for Windows98 compatibility, use FromHandle and not Attach/Detach
                CMenu * pNewDefaultMenu = CMenu::FromHandle(hNewDefaultMenu);
@@ -2455,10 +2743,10 @@ void CMainFrame::ReloadMenu()
                CMenu * pNewDirMenu = CMenu::FromHandle(hNewDirMenu);
 
                CWnd *pFrame = CWnd::FromHandle(::GetWindow(pMainFrame->m_hWndMDIClient, GW_CHILD));
-               while (pFrame)
+               while (pFrame != nullptr)
                {
-                       if (pFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
-                               static_cast<CChildFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
+                       if (pFrame->IsKindOf(RUNTIME_CLASS(CMergeEditFrame)))
+                               static_cast<CMergeEditFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
                        if (pFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
                                static_cast<CHexMergeFrame *>(pFrame)->SetSharedMenu(hNewMergeMenu);
                        if (pFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
@@ -2471,30 +2759,30 @@ void CMainFrame::ReloadMenu()
                }
 
                CFrameWnd *pActiveFrame = pMainFrame->GetActiveFrame();
-               if (pActiveFrame)
+               if (pActiveFrame != nullptr)
                {
-                       if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CChildFrame)))
-                               pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
+                       if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CMergeEditFrame)))
+                               pMainFrame->MDISetMenu(pNewMergeMenu, nullptr);
                        else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CHexMergeFrame)))
-                               pMainFrame->MDISetMenu(pNewMergeMenu, NULL);
+                               pMainFrame->MDISetMenu(pNewMergeMenu, nullptr);
                        else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CImgMergeFrame)))
-                               pMainFrame->MDISetMenu(pNewImgMergeMenu, NULL);
+                               pMainFrame->MDISetMenu(pNewImgMergeMenu, nullptr);
                        else if (pActiveFrame->IsKindOf(RUNTIME_CLASS(CDirFrame)))
-                               pMainFrame->MDISetMenu(pNewDirMenu, NULL);
+                               pMainFrame->MDISetMenu(pNewDirMenu, nullptr);
                        else
-                               pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
+                               pMainFrame->MDISetMenu(pNewDefaultMenu, nullptr);
                }
                else
-                       pMainFrame->MDISetMenu(pNewDefaultMenu, NULL);
+                       pMainFrame->MDISetMenu(pNewDefaultMenu, nullptr);
 
                // Don't delete the old menu
                // There is a bug in BCMenu or in Windows98 : the new menu does not
                // appear correctly if we destroy the old one
-               //                      if (pOldDefaultMenu)
+               //                      if (pOldDefaultMenu != nullptr)
                //                              pOldDefaultMenu->DestroyMenu();
-               //                      if (pOldMergeMenu)
+               //                      if (pOldMergeMenu != nullptr)
                //                              pOldMergeMenu->DestroyMenu();
-               //                      if (pOldDirMenu)
+               //                      if (pOldDirMenu = nullptr)
                //                              pOldDirMenu->DestroyMenu();
 
                // m_hMenuDefault is used to redraw the main menu when we close a child frame
@@ -2509,6 +2797,17 @@ void CMainFrame::ReloadMenu()
        }
 }
 
+IMergeDoc* CMainFrame::GetActiveIMergeDoc()
+{
+       CFrameWnd* pFrame = GetActiveFrame();
+       if (!pFrame)
+               return nullptr;
+       IMergeDoc* pMergeDoc = dynamic_cast<IMergeDoc*>(pFrame->GetActiveDocument());
+       if (!pMergeDoc)
+               pMergeDoc = dynamic_cast<IMergeDoc *>(pFrame);
+       return pMergeDoc;
+}
+
 void CMainFrame::UpdateDocTitle()
 {
        CDocManager* pDocManager = AfxGetApp()->m_pDocManager;
@@ -2528,3 +2827,72 @@ void CMainFrame::UpdateDocTitle()
                }
        }
 }
+
+void CMainFrame::OnAccelQuit()
+{
+       // TODO: Add your command handler code here
+
+       SendMessage(WM_CLOSE);
+}
+
+LRESULT CMainFrame::OnChildFrameAdded(WPARAM wParam, LPARAM lParam)
+{
+       for (int i = 0; i < m_arrChild.GetSize(); ++i)
+       {
+               if (reinterpret_cast<CMDIChildWnd*>(lParam) == m_arrChild.GetAt(i))
+               {
+                       return 0;
+               }
+       }
+
+       m_arrChild.InsertAt(0, (CMDIChildWnd*)lParam);
+
+       return 1;
+}
+
+LRESULT CMainFrame::OnChildFrameRemoved(WPARAM wParam, LPARAM lParam)
+{
+       for (int i = 0; i < m_arrChild.GetSize(); ++i)
+       {
+               if (reinterpret_cast<CMDIChildWnd*>(lParam) == m_arrChild.GetAt(i))
+               {
+                       m_arrChild.RemoveAt(i);
+                       break;
+               }
+       }
+
+       return 1;
+}
+
+LRESULT CMainFrame::OnChildFrameActivate(WPARAM wParam, LPARAM lParam)
+{
+       for (int i = 0; i < m_arrChild.GetSize(); ++i)
+       {
+               if (reinterpret_cast<CMDIChildWnd*>(lParam) == m_arrChild.GetAt(i))
+               {
+                       CMDIChildWnd* pMDIChild = m_arrChild.GetAt(i);
+                       if (pMDIChild->IsIconic())
+                               pMDIChild->ShowWindow(SW_RESTORE);
+                       MDIActivate(pMDIChild);
+                       break;
+               }
+       }
+
+       return 1;
+}
+// put lParam as index 0 in m_arrChild
+LRESULT CMainFrame::OnChildFrameActivated(WPARAM wParam, LPARAM lParam)
+{
+       for (int i = 0; i < m_arrChild.GetSize(); ++i)
+       {
+               if (reinterpret_cast<CMDIChildWnd*>(lParam) == m_arrChild.GetAt(i))
+               {
+                       m_arrChild.RemoveAt(i);
+                       break;
+               }
+       }
+
+       m_arrChild.InsertAt(0, (CMDIChildWnd*)lParam);
+
+       return 1;
+}