OSDN Git Service

MergeDoc.cpp: Avoid unpacking the file on the right if the Unpacker plugin fails...
[winmerge-jp/winmerge-jp.git] / Src / MergeDoc.cpp
index d7948ab..b8928cc 100644 (file)
@@ -39,7 +39,7 @@
 #include "MergeLineFlags.h"
 #include "FileOrFolderSelect.h"
 #include "LineFiltersList.h"
-#include "TokenPairList.h"
+#include "SubstitutionFiltersList.h"
 #include "TempFile.h"
 #include "codepage_detect.h"
 #include "SelectUnpackerDlg.h"
@@ -121,6 +121,7 @@ CMergeDoc::CMergeDoc()
 , m_bAutoMerged(false)
 , m_nGroups(0)
 , m_pView{nullptr}
+, m_bAutomaticRescan(false)
 {
        DIFFOPTIONS options = {0};
 
@@ -137,6 +138,8 @@ CMergeDoc::CMergeDoc()
        }
 
        m_bEnableRescan = true;
+       m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
+
        // COleDateTime m_LastRescan
        curUndo = undoTgt.begin();
        m_nDiffContext = GetOptionsMgr()->GetInt(OPT_DIFF_CONTEXT);
@@ -303,13 +306,13 @@ int CMergeDoc::Rescan(bool &bBinary, IDENTLEVEL &identical,
                m_diffWrapper.SetFilterList(_T(""));
        }
 
-       if (GetOptionsMgr()->GetBool(OPT_IGNORED_SUBSTITUTIONS_ARE_ENABLED) && theApp.m_pTokensForIs)
+       if (theApp.m_pSubstitutionFiltersList && theApp.m_pSubstitutionFiltersList->GetEnabled())
        {
-               m_diffWrapper.SetIgnoredSubstitutionsList(theApp.m_pTokensForIs.get());
+               m_diffWrapper.SetSubstitutionList(theApp.m_pSubstitutionFiltersList->MakeSubstitutionList());
        }
        else
        {
-               m_diffWrapper.SetIgnoredSubstitutionsList(nullptr);
+               m_diffWrapper.SetSubstitutionList(nullptr);
        }
 
        if (GetView(0, 0)->m_CurSourceDef->type != 0)
@@ -917,6 +920,84 @@ void CMergeDoc::CopyMultipleList(int srcPane, int dstPane, int firstDiff, int la
        FlushAndRescan();
 }
 
+void CMergeDoc::CopyMultiplePartialList(int srcPane, int dstPane, int firstDiff, int lastDiff,
+       int firstLineDiff, int lastLineDiff)
+{
+       lastDiff = min(m_diffList.GetSize() - 1, lastDiff);
+       firstDiff = max(0, firstDiff);
+       if (firstDiff > lastDiff)
+               return;
+       
+       RescanSuppress suppressRescan(*this);
+
+       bool bGroupWithPrevious = false;
+       if (firstLineDiff <= 0 && lastLineDiff == -1)
+       {
+               if (!ListCopy(srcPane, dstPane, -1, bGroupWithPrevious, true))
+                       return; // sync failure
+       }
+       else
+       {
+               if (!PartialListCopy(srcPane, dstPane, lastDiff, 
+                       (firstDiff == lastDiff) ? firstLineDiff : 0, lastLineDiff, bGroupWithPrevious, true))
+                       return; // sync failure
+       }
+
+
+       SetEditedAfterRescan(dstPane);
+
+       int nGroup = GetActiveMergeView()->m_nThisGroup;
+       CMergeEditView *pViewSrc = m_pView[nGroup][srcPane];
+       CMergeEditView *pViewDst = m_pView[nGroup][dstPane];
+       CPoint currentPosSrc = pViewSrc->GetCursorPos();
+       currentPosSrc.x = 0;
+       CPoint currentPosDst = pViewDst->GetCursorPos();
+       currentPosDst.x = 0;
+
+       CPoint pt(0, 0);
+       pViewDst->SetCursorPos(pt);
+       pViewDst->SetNewSelection(pt, pt, false);
+       pViewDst->SetNewAnchor(pt);
+
+       // copy from bottom up is more efficient
+       for (int i = lastDiff - 1; i >= firstDiff; --i)
+       {
+               if (m_diffList.IsDiffSignificant(i))
+               {
+                       SetCurrentDiff(i);
+                       const DIFFRANGE *pdi = m_diffList.DiffRangeAt(i);
+                       if (currentPosDst.y > pdi->dend)
+                       {
+                               if (pdi->blank[dstPane] >= 0)
+                                       currentPosDst.y -= pdi->dend - pdi->blank[dstPane] + 1;
+                               else if (pdi->blank[srcPane] >= 0)
+                                       currentPosDst.y -= pdi->dend - pdi->blank[srcPane] + 1;
+                       }                       
+                       // Group merge with previous (merge undo data to one action)
+                       bGroupWithPrevious = true;
+                       if (i > firstDiff || firstLineDiff <= 0)
+                       {
+                               if (!ListCopy(srcPane, dstPane, -1, bGroupWithPrevious, false))
+                                       break; // sync failure
+                       }
+                       else
+                       {
+                               if (!PartialListCopy(srcPane, dstPane, firstDiff, firstLineDiff, -1, bGroupWithPrevious, false))
+                                       break; // sync failure
+                       }
+               }
+       }
+
+       ForEachView(dstPane, [currentPosDst](auto& pView) {
+               pView->SetCursorPos(currentPosDst);
+               pView->SetNewSelection(currentPosDst, currentPosDst, false);
+               pView->SetNewAnchor(currentPosDst);
+       });
+
+       suppressRescan.Clear(); // done suppress Rescan
+       FlushAndRescan();
+}
+
 enum MergeResult { NoMergeNeeded, Merged, Conflict };
 
 template<class Type>
@@ -1045,7 +1126,7 @@ void CMergeDoc::DoAutoMerge(int dstPane)
 
        ShowMessageBox(
                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"), unresolvedConflictCount)),
                MB_ICONINFORMATION);
@@ -1212,6 +1293,99 @@ bool CMergeDoc::ListCopy(int srcPane, int dstPane, int nDiff /* = -1*/,
        return true;
 }
 
+bool CMergeDoc::PartialListCopy(int srcPane, int dstPane, int nDiff, int firstLine, int lastLine /*= -1*/,
+       bool bGroupWithPrevious /*= false*/, bool bUpdateView /*= true*/)
+{
+       int nGroup = GetActiveMergeView()->m_nThisGroup;
+       CMergeEditView *pViewSrc = m_pView[nGroup][srcPane];
+       CMergeEditView *pViewDst = m_pView[nGroup][dstPane];
+       CCrystalTextView *pSource = bUpdateView ? pViewDst : nullptr;
+
+       // suppress Rescan during this method
+       // (Not only do we not want to rescan a lot of times, but
+       // it will wreck the line status array to rescan as we merge)
+       RescanSuppress suppressRescan(*this);
+
+       DIFFRANGE cd;
+       VERIFY(m_diffList.GetDiff(nDiff, cd));
+       CDiffTextBuffer& sbuf = *m_ptBuf[srcPane];
+       CDiffTextBuffer& dbuf = *m_ptBuf[dstPane];
+       bool bSrcWasMod = sbuf.IsModified();
+       const int cd_dbegin = (firstLine > cd.dbegin) ? firstLine : cd.dbegin;
+       const int cd_dend = cd.dend;
+       const int cd_blank = cd.blank[srcPane];
+       bool bInSync = SanityCheckDiff(cd);
+
+       if (!bInSync)
+       {
+               LangMessageBox(IDS_VIEWS_OUTOFSYNC, MB_ICONSTOP);
+               return false; // abort copying
+       }
+
+       // If we remove whole diff from current view, we must fix cursor
+       // position first. Normally we would move to end of previous line,
+       // but we want to move to begin of that line for usability.
+       if (bUpdateView)
+       {
+               CPoint currentPos = pViewDst->GetCursorPos();
+               currentPos.x = 0;
+               if (currentPos.y > cd_dend)
+               {
+                       if (cd.blank[dstPane] >= 0)
+                               currentPos.y -= cd_dend - cd.blank[dstPane] + 1;
+                       else if (cd.blank[srcPane] >= 0)
+                               currentPos.y -= cd_dend - cd.blank[srcPane] + 1;
+               }
+               ForEachView(dstPane, [currentPos](auto& pView) { pView->SetCursorPos(currentPos); });
+       }
+
+       // if the current diff contains missing lines, remove them from both sides
+       int limit = ((lastLine < 0) || (lastLine > cd_dend)) ? cd_dend : lastLine;
+
+       // curView is the view which is changed, so the opposite of the source view
+       dbuf.BeginUndoGroup(bGroupWithPrevious);
+       if ((cd_blank >= 0) && (cd_dbegin >= cd_blank))
+       {
+               // text was missing, so delete rest of lines on both sides
+               // delete only on destination side since rescan will clear the other side
+               if (limit+1 < dbuf.GetLineCount())
+               {
+                       dbuf.DeleteText(pSource, cd_dbegin, 0, limit+1, 0, CE_ACTION_MERGE);
+               }
+               else
+               {
+                       // To removing EOL chars of last line, deletes from the end of the line (cd_blank - 1).
+                       ASSERT(cd_dbegin > 0);
+                       dbuf.DeleteText(pSource, cd_dbegin-1, dbuf.GetLineLength(cd_dbegin-1), limit, dbuf.GetLineLength(limit), CE_ACTION_MERGE);
+               }
+
+               limit = cd_dbegin-1;
+               dbuf.FlushUndoGroup(pSource);
+               dbuf.BeginUndoGroup(true);
+       }
+
+       // copy the selected text over
+       if (cd_dbegin <= limit)
+       {
+               // text exists on left side, so just replace
+               dbuf.ReplaceFullLines(dbuf, sbuf, pSource, cd_dbegin, limit, CE_ACTION_MERGE);
+               dbuf.FlushUndoGroup(pSource);
+               dbuf.BeginUndoGroup(true);
+       }
+       dbuf.FlushUndoGroup(pSource);
+
+       // remove the diff
+       SetCurrentDiff(-1);
+
+       // reset the mod status of the source view because we do make some
+       // changes, but none that concern the source text
+       sbuf.SetModified(bSrcWasMod);
+
+       suppressRescan.Clear(); // done suppress Rescan
+       FlushAndRescan();
+       return true;
+}
+
 bool CMergeDoc::WordListCopy(int srcPane, int dstPane, int nDiff, int firstWordDiff, int lastWordDiff,
                const std::vector<int> *pWordDiffIndice, bool bGroupWithPrevious /*= false*/, bool bUpdateView /*= true*/)
 {
@@ -2603,7 +2777,7 @@ DWORD CMergeDoc::LoadOneFile(int index, String filename, bool readOnly, const St
                {
                        m_ptBuf[index]->FreeAll();
                        loadSuccess = LoadFile(filename.c_str(), index, readOnly,
-                               GuessCodepageEncoding(filename, GetOptionsMgr()->GetInt(OPT_CP_DETECT), -1));
+                               codepage_detect::Guess(filename, GetOptionsMgr()->GetInt(OPT_CP_DETECT), -1));
                }
        }
        else
@@ -2695,7 +2869,7 @@ void CMergeDoc::SetTableProperties()
  * @param bRO [in] Is left/middle/right file read-only
  * @return Success/Failure/Binary (failure) per typedef enum OpenDocsResult_TYPE
  * @todo Options are still read from CMainFrame, this will change
- * @sa CMainFrame::ShowMergeDoc()
+ * @sa CMainFrame::ShowTextMergeDoc()
  */
 bool CMergeDoc::OpenDocs(int nFiles, const FileLocation ifileloc[],
                const bool bRO[], const String strDesc[])
@@ -2735,11 +2909,21 @@ bool CMergeDoc::OpenDocs(int nFiles, const FileLocation ifileloc[],
        m_strBothFilenames.erase(m_strBothFilenames.length() - 1);
 
        // Load files
-       DWORD nSuccess[3];
+       DWORD nSuccess[3] = { FileLoadResult::FRESULT_ERROR,  FileLoadResult::FRESULT_ERROR,  FileLoadResult::FRESULT_ERROR };
        for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
        {
                nSuccess[nBuffer] = LoadOneFile(nBuffer, fileloc[nBuffer].filepath, bRO[nBuffer], strDesc ? strDesc[nBuffer] : _T(""),
                        fileloc[nBuffer].encoding);
+               if (!FileLoadResult::IsOk(nSuccess[nBuffer]))
+               {
+                       CMergeEditFrame* pFrame = GetParentFrame();
+                       if (pFrame != nullptr)
+                       {
+                               // Use verify macro to trap possible error in debug.
+                               VERIFY(pFrame->DestroyWindow());
+                       }
+                       return false;
+               }
        }
 
        SetTableProperties();
@@ -2756,18 +2940,6 @@ bool CMergeDoc::OpenDocs(int nFiles, const FileLocation ifileloc[],
                }
        }
 
-       // Bail out if either side failed
-       if (std::find_if(nSuccess, nSuccess + m_nBuffers, [](DWORD d){return !FileLoadResult::IsOk(d);} ) != nSuccess + m_nBuffers)
-       {
-               CMergeEditFrame *pFrame = GetParentFrame();
-               if (pFrame != nullptr)
-               {
-                       // Use verify macro to trap possible error in debug.
-                       VERIFY(pFrame->DestroyWindow());
-               }
-               return false;
-       }
-
        // Warn user if file load was lossy (bad encoding)
        int idres=0;
        int nLossyBuffers = 0;
@@ -2998,7 +3170,7 @@ void CMergeDoc::ChangeFile(int nBuffer, const String& path, int nLineIndex)
 
        strDesc[nBuffer] = _T("");
        fileloc[nBuffer].setPath(path);
-       fileloc[nBuffer].encoding = GuessCodepageEncoding(path, GetOptionsMgr()->GetInt(OPT_CP_DETECT));
+       fileloc[nBuffer].encoding = codepage_detect::Guess(path, GetOptionsMgr()->GetInt(OPT_CP_DETECT));
        
        if (OpenDocs(m_nBuffers, fileloc, bRO, strDesc))
                MoveOnLoad(nBuffer, nLineIndex);
@@ -3015,6 +3187,8 @@ void CMergeDoc::RefreshOptions()
 {
        DIFFOPTIONS options = {0};
        
+       m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
+
        m_diffWrapper.SetDetectMovedBlocks(GetOptionsMgr()->GetBool(OPT_CMP_MOVED_BLOCKS));
        Options::DiffOptions::Load(GetOptionsMgr(), options);
 
@@ -3350,10 +3524,7 @@ void CMergeDoc::OnFileRecompareAs(UINT nID)
        }
        if (m_pEncodingErrorBar!=nullptr && m_pEncodingErrorBar->IsWindowVisible())
                m_pView[0][0]->GetParentFrame()->ShowControlBar(m_pEncodingErrorBar.get(), FALSE, FALSE);
-       if (nID == ID_MERGE_COMPARE_HEX)
-               GetMainFrame()->ShowHexMergeDoc(m_pDirDoc, m_nBuffers, fileloc, dwFlags, m_strDesc);
-       else
-               GetMainFrame()->ShowImgMergeDoc(m_pDirDoc, m_nBuffers, fileloc, dwFlags, m_strDesc);
+       GetMainFrame()->ShowMergeDoc(nID, m_pDirDoc, m_nBuffers, fileloc, dwFlags, m_strDesc);
        GetParentFrame()->ShowWindow(SW_RESTORE);
        GetParentFrame()->DestroyWindow();
 }