OSDN Git Service

Fix issue #784: Error on try to show differences between two different gif
[winmerge-jp/winmerge-jp.git] / Src / ImgMergeFrm.cpp
index adb075f..3c24d0f 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  ImgMergeFrm.cpp
 #include "FileLocation.h"
 #include "Constants.h"
 #include "DropHandler.h"
+#include "Environment.h"
 #include <cmath>
 
 #ifdef _DEBUG
 #define new DEBUG_NEW
 #endif
 
+/** @brief Location for image compare specific help to open. */
+static const TCHAR ImgMergeFrameHelpLocation[] = _T("::/htmlhelp/Compare_images.html");
+
 /////////////////////////////////////////////////////////////////////////////
 // CImgMergeFrame
 
@@ -59,6 +49,7 @@ BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
        //{{AFX_MSG_MAP(CImgMergeFrame)
        ON_WM_CREATE()
        ON_WM_CLOSE()
+       ON_WM_DESTROY()
        ON_WM_MDIACTIVATE()
        ON_WM_SIZE()
        ON_COMMAND(ID_FILE_SAVE, OnFileSave)
@@ -91,6 +82,13 @@ BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
        ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
        ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
        ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
+       ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
+       ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
+       ON_COMMAND(ID_EDIT_CUT, OnEditCut)
+       ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
+       ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
+       ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
+       ON_COMMAND(ID_EDIT_SELECT_ALL, OnEditSelectAll)
        ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
        ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
        ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
@@ -128,8 +126,8 @@ BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
        ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_ZOOM_25, ID_IMG_ZOOM_800, OnUpdateImgZoom)
        ON_COMMAND_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnImgOverlayMode)
        ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_OVERLAY_NONE, ID_IMG_OVERLAY_ALPHABLEND_ANIM, OnUpdateImgOverlayMode)
-       ON_COMMAND_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_ADJUST_OFFSET, OnImgDraggingMode)
-       ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_ADJUST_OFFSET, OnUpdateImgDraggingMode)
+       ON_COMMAND_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_RECTANGLE_SELECT, OnImgDraggingMode)
+       ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DRAGGINGMODE_NONE, ID_IMG_DRAGGINGMODE_RECTANGLE_SELECT, OnUpdateImgDraggingMode)
        ON_COMMAND_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnImgDiffBlockSize)
        ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_DIFFBLOCKSIZE_1, ID_IMG_DIFFBLOCKSIZE_32, OnUpdateImgDiffBlockSize)
        ON_COMMAND_RANGE(ID_IMG_THRESHOLD_0, ID_IMG_THRESHOLD_64, OnImgThreshold)
@@ -148,9 +146,11 @@ BEGIN_MESSAGE_MAP(CImgMergeFrame, CMergeFrameCommon)
        ON_UPDATE_COMMAND_UI(ID_IMG_USEBACKCOLOR, OnUpdateImgUseBackColor)
        ON_COMMAND_RANGE(ID_IMG_VECTORIMAGESCALING_25, ID_IMG_VECTORIMAGESCALING_800, OnImgVectorImageScaling)
        ON_UPDATE_COMMAND_UI_RANGE(ID_IMG_VECTORIMAGESCALING_25, ID_IMG_VECTORIMAGESCALING_800, OnUpdateImgVectorImageScaling)
+       ON_COMMAND(ID_IMG_COMPARE_EXTRACTED_TEXT, OnImgCompareExtractedText)
        ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
        ON_COMMAND(ID_REFRESH, OnRefresh)
        ON_WM_SETFOCUS ()
+       ON_COMMAND(ID_HELP, OnHelp)
        //}}AFX_MSG_MAP
 END_MESSAGE_MAP()
 
@@ -165,9 +165,10 @@ CImgMergeFrame::CImgMergeFrame()
 , m_bAutoMerged(false)
 , m_pImgMergeWindow(nullptr)
 , m_pImgToolWindow(nullptr)
-, m_nBufferType{BUFFER_NORMAL, BUFFER_NORMAL, BUFFER_NORMAL}
+, m_nBufferType{BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL, BUFFERTYPE::NORMAL}
 , m_bRO{}
 , m_nActivePane(-1)
+, m_unpackerSubcode{}
 {
 }
 
@@ -189,11 +190,7 @@ CImgMergeFrame::~CImgMergeFrame()
                if (pfnWinIMerge_DestroyWindow != nullptr && pfnWinIMerge_DestroyToolWindow != nullptr)
                {
                        if (m_pImgMergeWindow != nullptr)
-                       {
-                               for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
-                                       RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
                                pfnWinIMerge_DestroyWindow(m_pImgMergeWindow);
-                       }
                        if (m_pImgToolWindow != nullptr)
                                pfnWinIMerge_DestroyToolWindow(m_pImgToolWindow);
                        m_pImgMergeWindow = nullptr;
@@ -204,13 +201,19 @@ CImgMergeFrame::~CImgMergeFrame()
 
 bool CImgMergeFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bool bRO[], const String strDesc[], CMDIFrameWnd *pParent)
 {
-
+       int nNormalBuffer = 0;
        for (int pane = 0; pane < nFiles; ++pane)
        {
                m_filePaths.SetPath(pane, fileloc[pane].filepath);
                m_bRO[pane] = bRO[pane];
                m_strDesc[pane] = strDesc ? strDesc[pane] : _T("");
-               m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFER_NORMAL : BUFFER_NORMAL_NAMED;
+               if (fileloc[pane].filepath.empty())
+                       m_nBufferType[pane] = BUFFERTYPE::UNNAMED;
+               else
+               {
+                       m_nBufferType[pane] = (!strDesc || strDesc[pane].empty()) ? BUFFERTYPE::NORMAL : BUFFERTYPE::NORMAL_NAMED;
+                       ++nNormalBuffer;
+               }
        }
        SetTitle(nullptr);
 
@@ -226,7 +229,12 @@ bool CImgMergeFrame::OpenDocs(int nFiles, const FileLocation fileloc[], const bo
        ShowWindow(nCmdShow);
        BringToTop(nCmdShow);
 
-       OnRefresh();
+       GetParent()->ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_DRAWFRAME);
+
+       if (nNormalBuffer > 0)
+               OnRefresh();
+       else
+               UpdateDiffItem(m_pDirDoc);
 
        if (GetOptionsMgr()->GetBool(OPT_SCROLL_TO_FIRST))
                m_pImgMergeWindow->FirstDiff();
@@ -248,26 +256,20 @@ void CImgMergeFrame::MoveOnLoad(int nPane, int)
 
 void CImgMergeFrame::ChangeFile(int nBuffer, const String& path)
 {
+       if (!PromptAndSaveIfNeeded(true))
+               return;
+
        for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
                RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
 
        m_filePaths[nBuffer] = path;
-       m_nBufferType[nBuffer] = BUFFER_NORMAL;
+       m_nBufferType[nBuffer] = BUFFERTYPE::NORMAL;
        m_strDesc[nBuffer] = _T("");
 
-       if (m_filePaths.GetSize() == 2)
-               m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
-       else
-               m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str(), ucr::toUTF16(m_filePaths[2]).c_str());
-
+       OpenImages();
        for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
-       {
                m_fileInfo[pane].Update(m_filePaths[pane]);
 
-               RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
-                       new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
-       }
-
        UpdateHeaderPath(nBuffer);
        UpdateLastCompareResult();
 }
@@ -291,7 +293,7 @@ void CImgMergeFrame::DoAutoMerge(int dstPane)
 
        AfxMessageBox(
                strutils::format_string2(
-                       _T("The number of automatically merged changes: %1\nThe number of unresolved conflicts: %2"), 
+                       _("The number of automatically merged changes: %1\nThe number of unresolved conflicts: %2"), 
                        strutils::format(_T("%d"), autoMergedCount),
                        strutils::format(_T("%d"), m_pImgMergeWindow->GetConflictCount())).c_str(),
                MB_ICONINFORMATION);
@@ -310,22 +312,22 @@ IMergeDoc::FileChange CImgMergeFrame::IsFileChangedOnDisk(int pane) const
 {
        DiffFileInfo dfi;
        if (!dfi.Update(m_filePaths[pane]))
-               return FileRemoved;
+               return FileChange::Removed;
        int tolerance = 0;
        if (GetOptionsMgr()->GetBool(OPT_IGNORE_SMALL_FILETIME))
                tolerance = SmallTimeDiff; // From MainFrm.h
        int64_t timeDiff = dfi.mtime - m_fileInfo[pane].mtime;
        if (timeDiff < 0) timeDiff = -timeDiff;
        if ((timeDiff > tolerance * Poco::Timestamp::resolution()) || (dfi.size != m_fileInfo[pane].size))
-               return FileChanged;
-       return FileNoChange;
+               return FileChange::Changed;
+       return FileChange::NoChange;
 }
 
 void CImgMergeFrame::CheckFileChanged(void)
 {
        for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
        {
-               if (IsFileChangedOnDisk(pane) == FileChanged)
+               if (IsFileChangedOnDisk(pane) == FileChange::Changed)
                {
                        String msg = strutils::format_string1(_("Another application has updated file\n%1\nsince WinMerge scanned it last time.\n\nDo you want to reload the file?"), m_filePaths[pane]);
                        if (AfxMessageBox(msg.c_str(), MB_YESNO | MB_ICONWARNING) == IDYES)
@@ -364,10 +366,10 @@ void CImgMergeFrame::OnChildPaneEvent(const IImgMergeWindow::Event& evt)
                case VK_RIGHT:
                case VK_UP:
                case VK_DOWN:
-                       if (GetAsyncKeyState(VK_SHIFT))
+                       if (::GetAsyncKeyState(VK_SHIFT) & 0x8000)
                        {
                                int nActivePane = pFrame->m_pImgMergeWindow->GetActivePane();
-                               int m = GetAsyncKeyState(VK_CONTROL) ? 8 : 1;
+                               int m = (::GetAsyncKeyState(VK_CONTROL) & 0x8000) ? 8 : 1;
                                int dx = (-(evt.keycode == VK_LEFT) + (evt.keycode == VK_RIGHT)) * m;
                                int dy = (-(evt.keycode == VK_UP) + (evt.keycode == VK_DOWN)) * m;
                                pFrame->m_pImgMergeWindow->AddImageOffset(nActivePane, dx, dy);
@@ -406,7 +408,7 @@ bool CImgMergeFrame::IsLoadable()
 /**
  * @brief Create the splitter, the filename bar, the status bar, and the two views
  */
-BOOL CImgMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
+BOOL CImgMergeFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
        CCreateContext* pContext)
 {
        if (!IsLoadable())
@@ -416,9 +418,9 @@ BOOL CImgMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
        if (hModule == nullptr)
                return FALSE;
 
-       IImgMergeWindow * (*pfnWinIMerge_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) = 
-                       (IImgMergeWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinIMerge_CreateWindow");
-       if (pfnWinIMerge_CreateWindow == nullptr || 
+       IImgMergeWindow * (*pfnWinIMerge_CreateWindow)(HINSTANCE hInstance, HWND hWndParent, int nID) =
+               (IImgMergeWindow * (*)(HINSTANCE hInstance, HWND hWndParent, int nID))GetProcAddress(hModule, "WinIMerge_CreateWindow");
+       if (pfnWinIMerge_CreateWindow == nullptr ||
                (m_pImgMergeWindow = pfnWinIMerge_CreateWindow(hModule, m_hWnd, AFX_IDW_PANE_FIRST)) == nullptr)
        {
                FreeLibrary(hModule);
@@ -433,18 +435,22 @@ BOOL CImgMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
        m_pImgMergeWindow->SetSelDiffDeletedColor(colors.clrSelDiffDeleted);
        m_pImgMergeWindow->AddEventListener(OnChildPaneEvent, this);
        LoadOptions();
-       
+
        bool bResult;
-       if (m_filePaths.GetSize() == 2)
-               bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str());
+       if (std::count(m_nBufferType, m_nBufferType + m_filePaths.GetSize(), BUFFERTYPE::UNNAMED) == m_filePaths.GetSize())
+       {
+               bResult = m_pImgMergeWindow->NewImages(m_filePaths.GetSize(), 1, 256, 256);
+       }
        else
-               bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(m_filePaths[0]).c_str(), ucr::toUTF16(m_filePaths[1]).c_str(), ucr::toUTF16(m_filePaths[2]).c_str());
+       {
+               bResult = OpenImages();
+       }
 
        for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
        {
                m_fileInfo[pane].Update(m_filePaths[pane]);
-               
-               RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane), 
+
+               RegisterDragDrop(m_pImgMergeWindow->GetPaneHWND(pane),
                        new DropHandler(std::bind(&CImgMergeFrame::OnDropFiles, this, pane, std::placeholders::_1)));
        }
 
@@ -458,30 +464,38 @@ BOOL CImgMergeFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
                return FALSE;
        }
 
-       IImgToolWindow * (*pfnWinIMerge_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *) = 
-                       (IImgToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *pImgMergeWindow))GetProcAddress(hModule, "WinIMerge_CreateToolWindow");
+       IImgToolWindow * (*pfnWinIMerge_CreateToolWindow)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *) =
+               (IImgToolWindow * (*)(HINSTANCE hInstance, HWND hWndParent, IImgMergeWindow *pImgMergeWindow))GetProcAddress(hModule, "WinIMerge_CreateToolWindow");
        if (pfnWinIMerge_CreateToolWindow == nullptr ||
                (m_pImgToolWindow = pfnWinIMerge_CreateToolWindow(hModule, m_wndLocationBar.m_hWnd, m_pImgMergeWindow)) == nullptr)
        {
                return FALSE;
        }
 
+       m_pImgToolWindow->Translate(TranslateLocationPane);
+
        m_wndLocationBar.SetFrameHwnd(GetSafeHwnd());
 
        return TRUE;
 }
 
+void CImgMergeFrame::TranslateLocationPane(int id, const wchar_t *org, size_t dstbufsize, wchar_t *dst)
+{
+       swprintf_s(dst, dstbufsize, L"%s", tr("ImgMergeFrame|LocationPane", ucr::toUTF8(org)).c_str());
+}
+
 /////////////////////////////////////////////////////////////////////////////
 // CImgMergeFrame message handlers
 
 int CImgMergeFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 {
-
        if (CMergeFrameCommon::OnCreate(lpCreateStruct) == -1)
                return -1;
 
        EnableDocking(CBRS_ALIGN_TOP | CBRS_ALIGN_BOTTOM | CBRS_ALIGN_LEFT | CBRS_ALIGN_RIGHT);
 
+       CMergeFrameCommon::RemoveBarBorder();
+
        // Merge frame has a header bar at top
        if (!m_wndFilePathBar.Create(this))
        {
@@ -511,6 +525,13 @@ int CImgMergeFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
        CSize size = m_wndStatusBar[0].CalcFixedLayout(TRUE, TRUE);
        m_rectBorder.bottom = size.cy;
 
+       CDockState pDockState;
+       pDockState.LoadState(_T("Settings-ImgMergeFrame"));
+       if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
+               SetDockState(pDockState);
+       // for the dimensions of the diff and location pane, use the CSizingControlBar loader
+       m_wndLocationBar.LoadState(_T("Settings-ImgMergeFrame"));
+
        return 0;
 }
 
@@ -619,16 +640,6 @@ void CImgMergeFrame::SaveActivePane()
 
 void CImgMergeFrame::OnMDIActivate(BOOL bActivate, CWnd* pActivateWnd, CWnd* pDeactivateWnd)
 {
-       if (bActivate)
-       {
-               CDockState pDockState;
-               pDockState.LoadState(_T("Settings-ImgMergeFrame"));
-               if (EnsureValidDockState(pDockState)) // checks for valid so won't ASSERT
-                       SetDockState(pDockState);
-               // for the dimensions of the diff and location pane, use the CSizingControlBar loader
-               m_wndLocationBar.LoadState(_T("Settings-ImgMergeFrame"));
-       }
-
        CMergeFrameCommon::OnMDIActivate(bActivate, pActivateWnd, pDeactivateWnd);
 
        if (bActivate)
@@ -649,27 +660,50 @@ void CImgMergeFrame::OnClose()
        GetMainFrame()->ClearStatusbarItemCount();
 }
 
+void CImgMergeFrame::OnDestroy()
+{
+       if (!m_pImgMergeWindow)
+               return;
+       for (int pane = 0; pane < m_pImgMergeWindow->GetPaneCount(); ++pane)
+               RevokeDragDrop(m_pImgMergeWindow->GetPaneHWND(pane));
+}
+
 bool CImgMergeFrame::DoFileSave(int pane)
 {
        if (m_pImgMergeWindow->IsModified(pane))
        {
-               if (m_nBufferType[pane] == BUFFER_UNNAMED)
+               if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED)
                        DoFileSaveAs(pane);
                else
                {
                        String filename = ucr::toTString(m_pImgMergeWindow->GetFileName(pane));
                        bool bApplyToAll = false;
-                       if (theApp.HandleReadonlySave(filename, false, bApplyToAll) == IDCANCEL)
+                       if (CMergeApp::HandleReadonlySave(m_filePaths[pane], false, bApplyToAll) == IDCANCEL)
                                return false;
-                       theApp.CreateBackup(false, filename);
+                       CMergeApp::CreateBackup(false, m_filePaths[pane]);
+                       int savepoint = m_pImgMergeWindow->GetSavePoint(pane);
                        if (!m_pImgMergeWindow->SaveImage(pane))
                        {
-                               String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t-use a different filename (Press Ok)\n\t-abort the current operation (Press Cancel)?"), filename, GetSysError());
+                               String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t- use a different filename (Press OK)\n\t- abort the current operation (Press Cancel)?"), filename, GetSysError());
                                int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
                                if (answer == IDOK)
                                        return DoFileSaveAs(pane);
                                return false;
                        }
+                       if (filename != m_filePaths[pane])
+                       {
+                               if (!FileTransform::Packing(filename, m_filePaths[pane], m_infoUnpacker, m_unpackerSubcode[pane]))
+                               {
+                                       // Restore save point
+                                       m_pImgMergeWindow->SetSavePoint(pane, savepoint);
+
+                                       String str = CMergeApp::GetPackingErrorMessage(pane, m_pImgMergeWindow->GetPaneCount(), m_filePaths[pane], m_infoUnpacker.m_PluginName);
+                                       int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
+                                       if (answer == IDOK)
+                                               return DoFileSaveAs(pane, false);
+                                       return false;
+                               }
+                       }
                }
                UpdateDiffItem(m_pDirDoc);
                m_fileInfo[pane].Update(m_filePaths[pane]);
@@ -677,7 +711,7 @@ bool CImgMergeFrame::DoFileSave(int pane)
        return true;
 }
 
-bool CImgMergeFrame::DoFileSaveAs(int pane)
+bool CImgMergeFrame::DoFileSaveAs(int pane, bool packing)
 {
        const String &path = m_filePaths.GetPath(pane);
        String strPath;
@@ -692,18 +726,39 @@ RETRY:
        if (SelectFile(AfxGetMainWnd()->GetSafeHwnd(), strPath, false, path.c_str(), title))
        {
                std::wstring filename = ucr::toUTF16(strPath);
+               if (packing && !m_infoUnpacker.m_PluginName.empty())
+               {
+                       String tempPath = env::GetTemporaryPath();
+                       filename = ucr::toUTF16(env::GetTemporaryFileName(tempPath, _T("MRG_"), 0)
+                               + paths::FindExtension(m_pImgMergeWindow->GetFileName(pane)));
+               }
+               int savepoint = m_pImgMergeWindow->GetSavePoint(pane);
                if (!m_pImgMergeWindow->SaveImageAs(pane, filename.c_str()))
                {
-                       String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t-use a different filename (Press Ok)\n\t-abort the current operation (Press Cancel)?"), strPath, GetSysError());
+                       String str = strutils::format_string2(_("Saving file failed.\n%1\n%2\nDo you want to:\n\t- use a different filename (Press OK)\n\t- abort the current operation (Press Cancel)?"), strPath, GetSysError());
                        int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
                        if (answer == IDOK)
                                goto RETRY;
                        return false;
                }
+               if (filename != strPath)
+               {
+                       if (!FileTransform::Packing(filename, strPath, m_infoUnpacker, m_unpackerSubcode[pane]))
+                       {
+                               // Restore save point
+                               m_pImgMergeWindow->SetSavePoint(pane, savepoint);
+
+                               String str = CMergeApp::GetPackingErrorMessage(pane, m_pImgMergeWindow->GetPaneCount(), strPath, m_infoUnpacker.m_PluginName);
+                               int answer = AfxMessageBox(str.c_str(), MB_OKCANCEL | MB_ICONWARNING);
+                               if (answer == IDOK)
+                                       return DoFileSaveAs(pane, false);
+                               return false;
+                       }
+               }
                if (path.empty())
                {
                        // We are saving scratchpad (unnamed file)
-                       m_nBufferType[pane] = BUFFER_UNNAMED_SAVED;
+                       m_nBufferType[pane] = BUFFERTYPE::UNNAMED_SAVED;
                        m_strDesc[pane].erase();
                }
 
@@ -803,7 +858,7 @@ void CImgMergeFrame::OnFileReload()
 {
        if (!PromptAndSaveIfNeeded(true))
                return;
-       m_pImgMergeWindow->ReloadImages();
+       OpenImages();
        for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
                m_fileInfo[pane].Update(m_filePaths[pane]);
 }
@@ -893,12 +948,7 @@ void CImgMergeFrame::OnFileRecompareAs(UINT nId)
                strDesc[nBuffer] = m_strDesc[nBuffer];
        }
        CloseNow();
-       if (nId == ID_MERGE_COMPARE_TEXT)
-               GetMainFrame()->ShowMergeDoc(pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
-       else if (nId == ID_MERGE_COMPARE_HEX)
-               GetMainFrame()->ShowHexMergeDoc(pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
-       else
-               GetMainFrame()->ShowImgMergeDoc(pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
+       GetMainFrame()->ShowMergeDoc(nId, pDirDoc, nBuffers, fileloc, dwFlags, strDesc);
 }
 
 void CImgMergeFrame::OnUpdateFileRecompareAs(CCmdUI* pCmdUI)
@@ -919,8 +969,8 @@ void CImgMergeFrame::UpdateHeaderPath(int pane)
 {
        String sText;
 
-       if (m_nBufferType[pane] == BUFFER_UNNAMED ||
-               m_nBufferType[pane] == BUFFER_NORMAL_NAMED)
+       if (m_nBufferType[pane] == BUFFERTYPE::UNNAMED ||
+               m_nBufferType[pane] == BUFFERTYPE::NORMAL_NAMED)
        {
                sText = m_strDesc[pane];
        }
@@ -941,7 +991,7 @@ void CImgMergeFrame::UpdateHeaderPath(int pane)
 /// update splitting position for panels 1/2 and headerbar and statusbar 
 void CImgMergeFrame::UpdateHeaderSizes()
 {
-       if (IsWindowVisible() && m_pImgMergeWindow != nullptr)
+       if (m_pImgMergeWindow != nullptr)
        {
                int w[3];
                CRect rc, rcMergeWindow;
@@ -961,7 +1011,7 @@ void CImgMergeFrame::UpdateHeaderSizes()
                else
                {
                        for (int pane = 0; pane < nPaneCount; pane++)
-                               w[pane] = rc.Width() / nPaneCount - 4;
+                               w[pane] = rcMergeWindow.Width() / nPaneCount - 4;
                }
 
                if (!std::equal(m_nLastSplitPos, m_nLastSplitPos + nPaneCount - 1, w))
@@ -989,36 +1039,7 @@ void CImgMergeFrame::UpdateHeaderSizes()
  */
 void CImgMergeFrame::SetTitle(LPCTSTR lpszTitle)
 {
-       String sTitle;
-       String sFileName[3];
-
-       if (lpszTitle != nullptr)
-               sTitle = lpszTitle;
-       else
-       {
-               for (int nBuffer = 0; nBuffer < m_filePaths.GetSize(); nBuffer++)
-               {
-                       if (!m_strDesc[nBuffer].empty())
-                               sFileName[nBuffer] = m_strDesc[nBuffer];
-                       else
-                       {
-                               String file;
-                               String ext;
-                               paths::SplitFilename(m_filePaths[nBuffer], nullptr, &file, &ext);
-                               sFileName[nBuffer] += file;
-                               if (!ext.empty())
-                               {
-                                       sFileName[nBuffer] += _T(".");
-                                       sFileName[nBuffer] += ext;
-                               }
-                       }
-               }
-               const int nBuffers = m_filePaths.GetSize();
-               if (std::count(&sFileName[0], &sFileName[0] + nBuffers, sFileName[0]) == nBuffers)
-                       sTitle = sFileName[0] + strutils::format(_T(" x %d"), nBuffers);
-               else
-                       sTitle = strutils::join(&sFileName[0], &sFileName[0] + nBuffers, _T(" - "));
-       }
+       String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc);
        CMergeFrameCommon::SetTitle(sTitle.c_str());
        if (m_hWnd != nullptr)
                SetWindowText(sTitle.c_str());
@@ -1037,6 +1058,24 @@ void CImgMergeFrame::UpdateSplitter()
 {
 }
 
+bool CImgMergeFrame::OpenImages()
+{
+       bool bResult;
+       String filteredFilenames = strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|"));
+       String strTempFileName[3];
+       for (int pane = 0; pane < m_filePaths.GetSize(); ++pane)
+       {
+               strTempFileName[pane] = m_filePaths[pane];
+               if (!FileTransform::Unpacking(&m_infoUnpacker, &m_unpackerSubcode[pane], strTempFileName[pane], filteredFilenames))
+                       return false;
+       }
+       if (m_filePaths.GetSize() == 2)
+               bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str());
+       else
+               bResult = m_pImgMergeWindow->OpenImages(ucr::toUTF16(strTempFileName[0]).c_str(), ucr::toUTF16(strTempFileName[1]).c_str(), ucr::toUTF16(strTempFileName[2]).c_str());
+       return bResult;
+}
+
 /**
  * @brief Update associated diff item
  */
@@ -1195,6 +1234,7 @@ bool CImgMergeFrame::CloseNow()
  */
 void CImgMergeFrame::UpdateResources()
 {
+       m_pImgToolWindow->Translate(TranslateLocationPane);
 }
 
 /**
@@ -1313,15 +1353,19 @@ void CImgMergeFrame::OnIdleUpdateCmdUI()
                        String text;
                        if (m_pImgMergeWindow->ConvertToRealPos(pane, pt, ptReal))
                        {
-                               text += strutils::format(_T("Pt:(%d,%d) RGBA:(%d,%d,%d,%d) "), ptReal.x, ptReal.y,
+                               text += strutils::format(_("Pt: (%d, %d)  RGBA: (%d, %d, %d, %d)  "), ptReal.x, ptReal.y,
                                        color[pane].rgbRed, color[pane].rgbGreen, color[pane].rgbBlue, color[pane].rgbReserved);
                                if (pane == 1 && m_pImgMergeWindow->GetPaneCount() == 3)
-                                       text += strutils::format(_T("Dist:%g,%g "), colorDistance01, colorDistance12);
+                                       text += strutils::format(_("Dist: %g, %g  "), colorDistance01, colorDistance12);
                                else
-                                       text += strutils::format(_T("Dist:%g "), colorDistance01);
+                                       text += strutils::format(_("Dist: %g  "), colorDistance01);
                        }
-
-                       text += strutils::format(_T("Page:%d/%d Zoom:%d%% %dx%dpx %dbpp"), 
+                       if (m_pImgMergeWindow->IsRectangleSelectionVisible(pane))
+                       {
+                               RECT rc = m_pImgMergeWindow->GetRectangleSelection(pane);
+                               text += strutils::format(_("Rc: (%d, %d)  "), rc.right - rc.left, rc.bottom - rc.top);
+                       }
+                       text += strutils::format(_("Page: %d/%d  Zoom: %d%%  %dx%dpx  %dbpp"), 
                                        m_pImgMergeWindow->GetCurrentPage(pane) + 1,
                                        m_pImgMergeWindow->GetPageCount(pane),
                                        static_cast<int>(m_pImgMergeWindow->GetZoom() * 100),
@@ -1331,6 +1375,7 @@ void CImgMergeFrame::OnIdleUpdateCmdUI()
                                        );
                        m_wndStatusBar[pane].SetPaneText(0, text.c_str());
                }
+               UpdateLastCompareResult();
        }
        CMergeFrameCommon::OnIdleUpdateCmdUI();
 }
@@ -1415,6 +1460,62 @@ void CImgMergeFrame::OnUpdateEditRedo(CCmdUI* pCmdUI)
 }
 
 /**
+ * @brief Copy current selection to clipboard
+ */
+void CImgMergeFrame::OnEditCopy()
+{
+       m_pImgMergeWindow->Copy();
+}
+
+/**
+ * @brief Called when "Copy" item is updated
+ */
+void CImgMergeFrame::OnUpdateEditCopy(CCmdUI* pCmdUI)
+{
+       pCmdUI->Enable(m_pImgMergeWindow->IsCopyable());
+}
+
+/**
+ * @brief Cut current selection to clipboard
+ */
+void CImgMergeFrame::OnEditCut()
+{
+       m_pImgMergeWindow->Cut();
+}
+
+/**
+ * @brief Called when "Cut" item is updated
+ */
+void CImgMergeFrame::OnUpdateEditCut(CCmdUI* pCmdUI)
+{
+       pCmdUI->Enable(m_pImgMergeWindow->IsCuttable());
+}
+
+/**
+ * @brief Paste image from clipboard
+ */
+void CImgMergeFrame::OnEditPaste()
+{
+       m_pImgMergeWindow->Paste();
+}
+
+/**
+ * @brief Called when "Paste" item is updated
+ */
+void CImgMergeFrame::OnUpdateEditPaste(CCmdUI* pCmdUI)
+{
+       pCmdUI->Enable(m_pImgMergeWindow->IsPastable());
+}
+
+/**
+ * @brief Select entire image
+ */
+void CImgMergeFrame::OnEditSelectAll()
+{
+       m_pImgMergeWindow->SelectAll();
+}
+
+/**
  * @brief Called when user selects View/Zoom In from menu.
  */
 void CImgMergeFrame::OnViewZoomIn()
@@ -1974,7 +2075,8 @@ void CImgMergeFrame::OnUpdateImgUseBackColor(CCmdUI* pCmdUI)
 
 void CImgMergeFrame::OnImgVectorImageScaling(UINT nId)
 {
-       m_pImgMergeWindow->SetVectorImageZoomRatio(pow(2.0f, int(nId - ID_IMG_VECTORIMAGESCALING_100)));
+       m_pImgMergeWindow->SetVectorImageZoomRatio(
+               static_cast<float>(pow(2.0f, int(nId - ID_IMG_VECTORIMAGESCALING_100))));
        SaveOptions();
 }
 
@@ -1983,6 +2085,26 @@ void CImgMergeFrame::OnUpdateImgVectorImageScaling(CCmdUI* pCmdUI)
        pCmdUI->SetRadio(pow(2.0, int(pCmdUI->m_nID - ID_IMG_VECTORIMAGESCALING_100)) == m_pImgMergeWindow->GetVectorImageZoomRatio());
 }
 
+void CImgMergeFrame::OnImgCompareExtractedText()
+{
+       String text[3];
+       String desc[3];
+       for (int nBuffer = 0; nBuffer < m_filePaths.GetSize(); ++nBuffer)
+       {
+               BSTR bstr = m_pImgMergeWindow->ExtractTextFromImage(nBuffer,
+                       m_pImgMergeWindow->GetCurrentPage(nBuffer),
+                       static_cast<IImgMergeWindow::OCR_RESULT_TYPE>(GetOptionsMgr()->GetInt(OPT_CMP_IMG_OCR_RESULT_TYPE)));
+               if (bstr)
+               {
+                       text[nBuffer].assign(bstr, SysStringLen(bstr));
+                       desc[nBuffer] = m_strDesc[nBuffer].empty() ?
+                               paths::FindFileName(m_filePaths[nBuffer]) : m_strDesc[nBuffer];
+                       SysFreeString(bstr);
+               }
+       }
+       GetMainFrame()->ShowTextMergeDoc(m_pDirDoc, m_filePaths.GetSize(), text, desc, _T(".yaml"));
+}
+
 /**
  * @brief Generate report from file compare results.
  */
@@ -2056,15 +2178,17 @@ void CImgMergeFrame::OnToolsGenerateReport()
        if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
                return;
 
-       GenerateReport(s);
-
-       LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
+       if (GenerateReport(s))
+               LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
 }
 
 void CImgMergeFrame::OnRefresh()
 {
        if (UpdateDiffItem(m_pDirDoc) == 0)
-               LangMessageBox(IDS_FILESSAME, MB_ICONINFORMATION | MB_DONT_DISPLAY_AGAIN);
+       {
+               CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, true,
+                       [](LPCTSTR msg, UINT flags, UINT id) -> int { return AfxMessageBox(msg, flags, id); });
+       }
 }
 
 void CImgMergeFrame::OnDropFiles(int pane, const std::vector<String>& files)
@@ -2083,3 +2207,12 @@ void CImgMergeFrame::OnSetFocus(CWnd* pNewWnd)
        if (m_nActivePane != -1)
                m_pImgMergeWindow->SetActivePane(m_nActivePane);
 }
+
+
+/**
+ * @brief Open help from mainframe when user presses F1
+ */
+void CImgMergeFrame::OnHelp()
+{
+       theApp.ShowHelp(ImgMergeFrameHelpLocation);
+}