// 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 MergeDoc.cpp
#include "OptionsDef.h"
#include "DiffFileInfo.h"
#include "SaveClosingDlg.h"
+#include "OpenTableDlg.h"
#include "DiffList.h"
#include "paths.h"
#include "OptionsMgr.h"
#include "MergeLineFlags.h"
#include "FileOrFolderSelect.h"
#include "LineFiltersList.h"
+#include "SubstitutionFiltersList.h"
#include "TempFile.h"
#include "codepage_detect.h"
-#include "SelectUnpackerDlg.h"
+#include "SelectPluginDlg.h"
#include "EncodingErrorBar.h"
#include "MergeCmdLineInfo.h"
#include "TFile.h"
using std::swap;
-/** @brief Max len of path in caption. */
-static const UINT CAPTION_PATH_MAX = 50;
-
int CMergeDoc::m_nBuffersTemp = 2;
-/** @brief EOL types */
-static LPCTSTR crlfs[] =
-{
- _T ("\x0d\x0a"), // DOS/Windows style
- _T ("\x0a"), // UNIX style
- _T ("\x0d") // Macintosh style
-};
-
static void SaveBuffForDiff(CDiffTextBuffer & buf, const String& filepath, int nStartLine = 0, int nLines = -1);
/////////////////////////////////////////////////////////////////////////////
ON_COMMAND(ID_FILE_SAVEAS_MIDDLE, OnFileSaveAsMiddle)
ON_COMMAND(ID_FILE_SAVEAS_RIGHT, OnFileSaveAsRight)
ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
- ON_UPDATE_COMMAND_UI(ID_STATUS_PLUGIN, OnUpdatePluginName)
ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
ON_COMMAND(ID_RESCAN, OnFileReload)
ON_COMMAND(ID_FILE_ENCODING, OnFileEncoding)
- ON_COMMAND_RANGE(ID_VIEW_DIFFCONTEXT_ALL, ID_VIEW_DIFFCONTEXT_TOGGLE, OnDiffContext)
- ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_DIFFCONTEXT_ALL, ID_VIEW_DIFFCONTEXT_TOGGLE, OnUpdateDiffContext)
- ON_COMMAND(ID_POPUP_OPEN_WITH_UNPACKER, OnCtxtOpenWithUnpacker)
+ ON_COMMAND_RANGE(ID_VIEW_DIFFCONTEXT_ALL, ID_VIEW_DIFFCONTEXT_INVERT, OnDiffContext)
+ ON_UPDATE_COMMAND_UI_RANGE(ID_VIEW_DIFFCONTEXT_ALL, ID_VIEW_DIFFCONTEXT_INVERT, OnUpdateDiffContext)
+ ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
+ ON_COMMAND(ID_APPLY_PREDIFFER, OnApplyPrediffer)
ON_BN_CLICKED(IDC_FILEENCODING, OnBnClickedFileEncoding)
ON_BN_CLICKED(IDC_PLUGIN, OnBnClickedPlugin)
ON_BN_CLICKED(IDC_HEXVIEW, OnBnClickedHexView)
ON_COMMAND(IDOK, OnOK)
- ON_COMMAND(ID_MERGE_COMPARE_XML, OnFileRecompareAsXML)
+ ON_COMMAND(ID_MERGE_COMPARE_TEXT, OnFileRecompareAsText)
+ ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_TEXT, OnUpdateFileRecompareAsText)
+ ON_COMMAND(ID_MERGE_COMPARE_TABLE, OnFileRecompareAsTable)
+ ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_TABLE, OnUpdateFileRecompareAsTable)
ON_COMMAND_RANGE(ID_MERGE_COMPARE_HEX, ID_MERGE_COMPARE_IMAGE, OnFileRecompareAs)
+ ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnFileRecompareAs)
+ ON_UPDATE_COMMAND_UI_RANGE(ID_SWAPPANES_SWAP23, ID_SWAPPANES_SWAP13, OnUpdateSwapContext)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
CMergeDoc::CMergeDoc()
: m_bEnableRescan(true)
, m_nCurDiff(-1)
+, m_CurWordDiff{ -1, static_cast<size_t>(-1), -1 }
, m_pDirDoc(nullptr)
, m_bMixedEol(false)
-, m_pInfoUnpacker(new PackingInfo)
, m_pEncodingErrorBar(nullptr)
, m_bHasSyncPoints(false)
, m_bAutoMerged(false)
, m_nGroups(0)
, m_pView{nullptr}
+, m_bAutomaticRescan(false)
{
DIFFOPTIONS options = {0};
m_ptBuf[nBuffer].reset(new CDiffTextBuffer(this, nBuffer));
m_pSaveFileInfo[nBuffer].reset(new DiffFileInfo());
m_pRescanFileInfo[nBuffer].reset(new DiffFileInfo());
- m_nBufferType[nBuffer] = BUFFER_NORMAL;
+ m_nBufferType[nBuffer] = BUFFERTYPE::NORMAL;
m_bEditAfterRescan[nBuffer] = false;
}
- m_nCurDiff=-1;
m_bEnableRescan = true;
+ m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
+
// COleDateTime m_LastRescan
curUndo = undoTgt.begin();
m_nDiffContext = GetOptionsMgr()->GetInt(OPT_DIFF_CONTEXT);
+ m_bInvertDiffContext = GetOptionsMgr()->GetBool(OPT_INVERT_DIFF_CONTEXT);
m_diffWrapper.SetDetectMovedBlocks(GetOptionsMgr()->GetBool(OPT_CMP_MOVED_BLOCKS));
Options::DiffOptions::Load(GetOptionsMgr(), options);
{
if (infoNewHandler != nullptr)
{
- *m_pInfoUnpacker = *infoNewHandler;
+ m_infoUnpacker = *infoNewHandler;
}
}
{
m_diffWrapper.SetPrediffer(infoPrediffer);
}
+
void CMergeDoc::GetPrediffer(PrediffingInfo * infoPrediffer)
{
m_diffWrapper.GetPrediffer(infoPrediffer);
}
+const PrediffingInfo* CMergeDoc::GetPrediffer() const
+{
+ static PrediffingInfo infoPrediffer;
+ m_diffWrapper.GetPrediffer(&infoPrediffer);
+ return &infoPrediffer;
+}
+
/////////////////////////////////////////////////////////////////////////////
// CMergeDoc serialization
static void SaveBuffForDiff(CDiffTextBuffer & buf, const String& filepath, int nStartLine, int nLines)
{
// and we don't repack the file
- PackingInfo * tempPacker = nullptr;
+ PackingInfo tempPacker(false);
// write buffer out to temporary file
String sError;
int retVal = buf.SaveToFile(filepath, true, sError, tempPacker,
- CRLF_STYLE_AUTOMATIC, false, nStartLine, nLines);
+ CRLFSTYLE::AUTOMATIC, false, nStartLine, nLines);
}
/**
DiffFileInfo fileInfo;
bool diffSuccess = false;
int nResult = RESCAN_OK;
- FileChange FileChanged[3] = {FileNoChange, FileNoChange, FileNoChange};
+ FileChange Changed[3] = {FileChange::NoChange, FileChange::NoChange, FileChange::NoChange};
int nBuffer;
if (!bForced)
{
m_diffWrapper.SetFilterList(_T(""));
}
- m_diffWrapper.SetFilterCommentsManager(theApp.m_pFilterCommentsManager.get());
+
+ if (theApp.m_pSubstitutionFiltersList && theApp.m_pSubstitutionFiltersList->GetEnabled())
+ {
+ m_diffWrapper.SetSubstitutionList(theApp.m_pSubstitutionFiltersList->MakeSubstitutionList());
+ }
+ else
+ {
+ m_diffWrapper.SetSubstitutionList(nullptr);
+ }
+
+ if (GetView(0, 0)->m_CurSourceDef->type != 0)
+ m_diffWrapper.SetFilterCommentsSourceDef(GetView(0, 0)->m_CurSourceDef);
+ else
+ m_diffWrapper.SetFilterCommentsSourceDef(GetFileExt(m_filePaths[0].c_str(), m_strDesc[0].c_str()));
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
// Ignore checking in case of scratchpads (empty filenames)
if (!m_filePaths[nBuffer].empty())
{
- FileChanged[nBuffer] = IsFileChangedOnDisk(m_filePaths[nBuffer].c_str(),
+ Changed[nBuffer] = IsFileChangedOnDisk(m_filePaths[nBuffer].c_str(),
fileInfo, false, nBuffer);
}
}
LPCTSTR tnames[] = {_T("t0_wmdoc"), _T("t1_wmdoc"), _T("t2_wmdoc")};
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
- if (FileChanged[nBuffer] == FileRemoved)
+ if (Changed[nBuffer] == FileChange::Removed)
{
String msg = strutils::format_string1(_("The file\n%1\nhas disappeared. Please save a copy of the file to continue."), m_filePaths[nBuffer]);
ShowMessageBox(msg, MB_ICONWARNING);
String temp = m_tempFiles[nBuffer].GetPath();
if (temp.empty())
- {
- temp = m_tempFiles[nBuffer].CreateFromFile(m_filePaths.GetPath(nBuffer),
- tnames[nBuffer]);
- }
+ temp = m_tempFiles[nBuffer].Create(tnames[nBuffer]);
if (temp.empty())
return RESCAN_TEMP_ERR;
}
// Clear diff list
m_diffList.Clear();
m_nCurDiff = -1;
+ m_CurWordDiff = { -1, static_cast<size_t>(-1), -1 };
// Clear moved lines lists
if (m_diffWrapper.GetDetectMovedBlocks())
{
diffSuccess = m_diffWrapper.RunFileDiff();
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
nRealLine[nBuffer] = m_ptBuf[nBuffer]->ComputeRealLine(nStartLine[nBuffer]);
+
+ // Correct the comparison results made by diffutils if the first file separated by the sync point is an empty file.
+ if (i == 0 && templist.GetSize() > 0)
+ for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ if (nStartLine[nBuffer] == 0)
+ {
+ bool isEmptyFile = true;
+ for (int j = 0; j < nLines[nBuffer]; j++)
+ {
+ if (!(m_ptBuf[nBuffer]->GetLineFlags(nStartLine[nBuffer] + j) & LF_GHOST))
+ {
+ isEmptyFile = false;
+ break;
+ }
+ }
+ if (isEmptyFile)
+ {
+ DIFFRANGE di;
+ templist.GetDiff(0, di);
+ if (di.begin[nBuffer] == 0 && di.end[nBuffer] == 0)
+ {
+ di.end[nBuffer] = -1;
+ templist.SetDiff(0, di);
+ }
+ }
+ }
+
m_diffList.AppendDiffList(templist, nRealLine);
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
nStartLine[nBuffer] += nLines[nBuffer];
// Apply flags to lines that are trivial
PrediffingInfo infoPrediffer;
GetPrediffer(&infoPrediffer);
- if (!infoPrediffer.m_PluginName.empty())
+ if (!infoPrediffer.GetPluginPipeline().empty())
FlagTrivialLines();
// Apply flags to lines that moved, to differentiate from appeared/disappeared lines
// Identical files are also updated
if (!m_diffList.HasSignificantDiffs())
- identical = IDENTLEVEL_ALL;
+ identical = IDENTLEVEL::ALL;
ForEachView([](auto& pView) {
// just apply some options to the views
}
if (!GetOptionsMgr()->GetBool(OPT_CMP_IGNORE_CODEPAGE) &&
- identical == IDENTLEVEL_ALL &&
+ identical == IDENTLEVEL::ALL &&
std::any_of(m_ptBuf, m_ptBuf + m_nBuffers,
[&](std::unique_ptr<CDiffTextBuffer>& buf) { return buf->getEncoding() != m_ptBuf[0]->getEncoding(); }))
- identical = IDENTLEVEL_NONE;
+ identical = IDENTLEVEL::NONE;
- GetParentFrame()->SetLastCompareResult(identical != IDENTLEVEL_ALL ? 1 : 0);
+ GetParentFrame()->SetLastCompareResult(identical != IDENTLEVEL::ALL ? 1 : 0);
return nResult;
}
m_pRescanFileInfo[nBuffer]->Update((LPCTSTR)m_filePaths[nBuffer].c_str());
}
+ bool bDoReload = false;
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
- if (FileChange[nBuffer] == FileChanged)
+ if (FileChange[nBuffer] == 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[nBuffer]);
if (ShowMessageBox(msg, MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_FILECHANGED_RESCAN) == IDYES)
+ bDoReload = true;
+ break;
+ }
+ }
+ if (bDoReload)
+ {
+ for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ {
+ if (FileChange[nBuffer] == FileChange::Changed)
{
- OnFileReload();
+ CPoint pt = GetView(0, nBuffer)->GetCursorPos();
+ ChangeFile(nBuffer, m_filePaths[nBuffer], pt.y);
}
- break;
}
}
}
// Make the call to stringdiffs, which does all the hard & tedious computations
std::vector<strdiff::wdiff> worddiffs = strdiff::ComputeWordDiffs(m_nBuffers, str,
!diffOptions.bIgnoreCase,
+ !diffOptions.bIgnoreEol,
diffOptions.nIgnoreWhitespace,
GetBreakType(), // whitespace only or include punctuation
GetByteColoringOption());
pMovedLines = m_diffWrapper.GetMovedLines(0);
for (i = 0; i < m_ptBuf[0]->GetLineCount(); ++i)
{
- int j = pMovedLines->LineInBlock(i, MovedLines::SIDE_RIGHT);
+ int j = pMovedLines->LineInBlock(i, MovedLines::SIDE::RIGHT);
if (j != -1)
{
TRACE(_T("%d->%d\n"), i, j);
if (m_ptBuf[0]->FlagIsSet(apparent, LF_DIFF))
{
m_ptBuf[0]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ if (m_ptBuf[1]->FlagIsSet(apparent, LF_GHOST))
+ {
+ int apparentJ = m_ptBuf[1]->ComputeApparentLine(j);
+ if (m_ptBuf[0]->FlagIsSet(apparentJ, LF_GHOST))
+ m_ptBuf[1]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ }
}
}
}
pMovedLines = m_diffWrapper.GetMovedLines(1);
for (i=0; i<m_ptBuf[1]->GetLineCount(); ++i)
{
- int j = pMovedLines->LineInBlock(i, MovedLines::SIDE_LEFT);
+ int j = pMovedLines->LineInBlock(i, MovedLines::SIDE::LEFT);
if (j != -1)
{
TRACE(_T("%d->%d\n"), i, j);
if (m_ptBuf[1]->FlagIsSet(apparent, LF_DIFF))
{
m_ptBuf[1]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ if (m_ptBuf[0]->FlagIsSet(apparent, LF_GHOST))
+ {
+ int apparentJ = m_ptBuf[0]->ComputeApparentLine(j);
+ if (m_ptBuf[1]->FlagIsSet(apparentJ, LF_GHOST))
+ m_ptBuf[0]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ }
}
}
}
pMovedLines = m_diffWrapper.GetMovedLines(1);
for (i=0; i<m_ptBuf[1]->GetLineCount(); ++i)
{
- int j = pMovedLines->LineInBlock(i, MovedLines::SIDE_RIGHT);
+ int j = pMovedLines->LineInBlock(i, MovedLines::SIDE::RIGHT);
if (j != -1)
{
TRACE(_T("%d->%d\n"), i, j);
if (m_ptBuf[1]->FlagIsSet(apparent, LF_DIFF))
{
m_ptBuf[1]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ if (m_ptBuf[2]->FlagIsSet(apparent, LF_GHOST))
+ {
+ int apparentJ = m_ptBuf[2]->ComputeApparentLine(j);
+ if (m_ptBuf[1]->FlagIsSet(apparentJ, LF_GHOST))
+ m_ptBuf[2]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ }
}
}
}
pMovedLines = m_diffWrapper.GetMovedLines(2);
for (i=0; i<m_ptBuf[2]->GetLineCount(); ++i)
{
- int j = pMovedLines->LineInBlock(i, MovedLines::SIDE_LEFT);
+ int j = pMovedLines->LineInBlock(i, MovedLines::SIDE::LEFT);
if (j != -1)
{
TRACE(_T("%d->%d\n"), i, j);
if (m_ptBuf[2]->FlagIsSet(apparent, LF_DIFF))
{
m_ptBuf[2]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ if (m_ptBuf[1]->FlagIsSet(apparent, LF_GHOST))
+ {
+ int apparentJ = m_ptBuf[1]->ComputeApparentLine(j);
+ if (m_ptBuf[2]->FlagIsSet(apparentJ, LF_GHOST))
+ m_ptBuf[1]->SetLineFlag(apparent, LF_MOVED, true, false, false);
+ }
}
}
}
}
// Files are not binaries, but they are identical
- if (identical != IDENTLEVEL_NONE)
+ if (identical != IDENTLEVEL::NONE)
{
- if (!m_filePaths.GetLeft().empty() && !m_filePaths.GetMiddle().empty() && !m_filePaths.GetRight().empty() &&
- m_filePaths.GetLeft() == m_filePaths.GetRight() && m_filePaths.GetMiddle() == m_filePaths.GetRight())
- {
- // compare file to itself, a custom message so user may hide the message in this case only
- s = _("The same file is opened in both panels.");
- ShowMessageBox(s, MB_ICONINFORMATION | MB_DONT_DISPLAY_AGAIN, IDS_FILE_TO_ITSELF);
- }
- else if (identical == IDENTLEVEL_ALL)
- {
- UINT nFlags = MB_ICONINFORMATION | MB_DONT_DISPLAY_AGAIN;
-
- if (theApp.m_bExitIfNoDiff == MergeCmdLineInfo::Exit)
- {
- // Show the "files are identical" for basic "exit no diff" flag
- // If user don't want to see the message one uses the quiet version
- // of the "exit no diff".
- nFlags &= ~MB_DONT_DISPLAY_AGAIN;
- }
-
- if (theApp.m_bExitIfNoDiff != MergeCmdLineInfo::ExitQuiet)
- {
- s = _("The selected files are identical.");
- ShowMessageBox(s, nFlags, IDS_FILESSAME);
- }
-
- // Exit application if files are identical.
- if (theApp.m_bExitIfNoDiff == MergeCmdLineInfo::Exit ||
- theApp.m_bExitIfNoDiff == MergeCmdLineInfo::ExitQuiet)
- {
- AfxGetMainWnd()->PostMessage(WM_COMMAND, ID_APP_EXIT);
- }
- }
+ CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, identical == IDENTLEVEL::ALL,
+ [this](LPCTSTR msg, UINT flags, UINT id) -> int { return ShowMessageBox(msg, flags, id); });
}
}
}
else
{
- if (!WordListCopy(srcPane, dstPane, lastDiff, firstWordDiff, lastWordDiff, nullptr, bGroupWithPrevious, true))
+ if (!WordListCopy(srcPane, dstPane, lastDiff,
+ (firstDiff == lastDiff) ? firstWordDiff : 0, lastWordDiff, nullptr, bGroupWithPrevious, true))
return; // sync failure
}
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>
DoMergeValue(m_ptBuf[0]->getEncoding(), m_ptBuf[1]->getEncoding(), m_ptBuf[2]->getEncoding(), dstPane);
if (mergedEncoding.first == Merged)
{
- ShowMessageBox(_("The change of codepage has been merged"), MB_ICONINFORMATION);
+ ShowMessageBox(_("The change of codepage has been merged."), MB_ICONINFORMATION);
m_ptBuf[dstPane]->setEncoding(mergedEncoding.second);
}
else if (mergedEncoding.first == Conflict)
- ShowMessageBox(_("The changes of codepage are conflicting"), MB_ICONINFORMATION);
+ ShowMessageBox(_("The changes of codepage are conflicting."), MB_ICONINFORMATION);
std::pair<MergeResult, CRLFSTYLE> mergedEOLStyle =
DoMergeValue(m_ptBuf[0]->GetCRLFMode(), m_ptBuf[1]->GetCRLFMode(), m_ptBuf[2]->GetCRLFMode(), dstPane);
if (mergedEOLStyle.first == Merged)
{
- ShowMessageBox(_("The change of EOL has been merged"), MB_ICONINFORMATION);
+ ShowMessageBox(_("The change of EOL has been merged."), MB_ICONINFORMATION);
m_ptBuf[dstPane]->SetCRLFMode(mergedEOLStyle.second);
}
else if (mergedEOLStyle.first == Conflict)
- ShowMessageBox(_("The changes of EOL are conflicting"), MB_ICONINFORMATION);
+ ShowMessageBox(_("The changes of EOL are conflicting."), MB_ICONINFORMATION);
RescanSuppress suppressRescan(*this);
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);
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*/)
{
* @sa CMergeDoc::CDiffTextBuffer::SaveToFile()
*/
bool CMergeDoc::TrySaveAs(String &strPath, int &nSaveResult, String & sError,
- int nBuffer, PackingInfo * pInfoTempUnpacker)
+ int nBuffer, PackingInfo& infoTempUnpacker)
{
String s;
String str;
// Select message based on reason function called
if (nSaveResult == SAVE_PACK_FAILED)
{
- if (m_nBuffers == 3)
- {
- str = strutils::format_string2(
- nBuffer == 0 ?
- _("Plugin '%2' cannot pack your changes to the left file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?")
- : (nBuffer == 1 ?
- _("Plugin '%2' cannot pack your changes to the middle file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?"):
- _("Plugin '%2' cannot pack your changes to the right file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?")),
- strPath, pInfoTempUnpacker->m_PluginName);
- }
- else
- {
- str = strutils::format_string2(nBuffer == 0 ? _("Plugin '%2' cannot pack your changes to the left file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?") :
- _("Plugin '%2' cannot pack your changes to the right file back into '%1'.\n\nThe original file will not be changed.\n\nDo you want to save the unpacked version to another file?"),
- strPath, pInfoTempUnpacker->m_PluginName);
- }
+ str = CMergeApp::GetPackingErrorMessage(nBuffer, m_nBuffers, strPath, infoTempUnpacker);
// replace the unpacker with a "do nothing" unpacker
- pInfoTempUnpacker->Initialize(PLUGIN_MANUAL);
+ infoTempUnpacker.Initialize(false);
}
else
{
- 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, sError);
+ 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, sError);
}
// SAVE_NO_FILENAME is temporarily used for scratchpad.
CDiffTextBuffer *pBuffer = m_ptBuf[nBuffer].get();
strSavePath = s;
nSaveResult = pBuffer->SaveToFile(strSavePath, false, sError,
- pInfoTempUnpacker);
+ infoTempUnpacker);
if (nSaveResult == SAVE_DONE)
{
// We are saving scratchpad (unnamed file)
if (strPath.empty())
{
- m_nBufferType[nBuffer] = BUFFER_UNNAMED_SAVED;
+ m_nBufferType[nBuffer] = BUFFERTYPE::UNNAMED_SAVED;
m_strDesc[nBuffer].erase();
}
int nRetVal = -1;
fileChanged = IsFileChangedOnDisk(szPath, fileInfo, true, nBuffer);
- if (fileChanged == FileChanged)
+ if (fileChanged == FileChange::Changed)
{
String msg = strutils::format_string1(_("Another application has updated file\n%1\nsince WinMerge loaded it.\n\nOverwrite changed file?"), szPath);
if (ShowMessageBox(msg, MB_ICONWARNING | MB_YESNO) == IDNO)
}
// use a temp packer
- // first copy the m_pInfoUnpacker
+ // first copy the m_infoUnpacker
// if an error arises during packing, change and take a "do nothing" packer
- PackingInfo infoTempUnpacker = *m_pInfoUnpacker;
+ PackingInfo infoTempUnpacker = m_infoUnpacker;
bSaveSuccess = false;
strSavePath = theApp.m_strSaveAsPath;
}
- nRetVal = theApp.HandleReadonlySave(strSavePath, false, bApplyToAll);
+ nRetVal = CMergeApp::HandleReadonlySave(strSavePath, false, bApplyToAll);
if (nRetVal == IDCANCEL)
return false;
- if (!theApp.CreateBackup(false, strSavePath))
+ if (!CMergeApp::CreateBackup(false, strSavePath))
return false;
// false as long as the user is not satisfied
nSaveErrorCode = SAVE_NO_FILENAME;
// Handle unnamed buffers
- if (m_nBufferType[nBuffer] == BUFFER_UNNAMED)
+ if (m_nBufferType[nBuffer] == BUFFERTYPE::UNNAMED)
nSaveErrorCode = SAVE_NO_FILENAME;
String sError;
if (nSaveErrorCode == SAVE_DONE)
// We have a filename, just try to save
- nSaveErrorCode = pBuffer->SaveToFile(strSavePath, false, sError, &infoTempUnpacker);
+ nSaveErrorCode = pBuffer->SaveToFile(strSavePath, false, sError, infoTempUnpacker);
if (nSaveErrorCode != SAVE_DONE)
{
// Saving failed, user may save to another location if wants to
do
- result = TrySaveAs(strSavePath, nSaveErrorCode, sError, nBuffer, &infoTempUnpacker);
+ result = TrySaveAs(strSavePath, nSaveErrorCode, sError, nBuffer, infoTempUnpacker);
while (!result);
}
String strSavePath(szPath);
// use a temp packer
- // first copy the m_pInfoUnpacker
+ // first copy the m_infoUnpacker
// if an error arises during packing, change and take a "do nothing" packer
- PackingInfo infoTempUnpacker = *m_pInfoUnpacker;
+ PackingInfo infoTempUnpacker = m_infoUnpacker;
bSaveSuccess = false;
// false as long as the user is not satisfied
// Loop until user succeeds saving or cancels
String sError;
do
- result = TrySaveAs(strSavePath, nSaveErrorCode, sError, nBuffer, &infoTempUnpacker);
+ result = TrySaveAs(strSavePath, nSaveErrorCode, sError, nBuffer, infoTempUnpacker);
while (!result);
// Saving succeeded with given/selected filename
if (m_diffWrapper.GetDetectMovedBlocks())
{
realRightLine = m_diffWrapper.GetMovedLines(nBuffer)->LineInBlock(realLeftLine,
- MovedLines::SIDE_RIGHT);
+ MovedLines::SIDE::RIGHT);
}
if (realRightLine != -1)
return m_ptBuf[nBuffer + 1]->ComputeApparentLine(realRightLine);
if (m_diffWrapper.GetDetectMovedBlocks())
{
realLeftLine = m_diffWrapper.GetMovedLines(nBuffer)->LineInBlock(realRightLine,
- MovedLines::SIDE_LEFT);
+ MovedLines::SIDE::LEFT);
}
if (realLeftLine != -1)
return m_ptBuf[nBuffer - 1]->ComputeApparentLine(realLeftLine);
pActiveView->HideCursor();
bool bBinary = false;
- IDENTLEVEL identical = IDENTLEVEL_NONE;
+ IDENTLEVEL identical = IDENTLEVEL::NONE;
int nRescanResult = Rescan(bBinary, identical, bForced);
// restore cursors and caret
// because of ghostlines, m_nTopLine may differ just after Rescan
// scroll both views to the same top line
pView->UpdateSiblingScrollPos(false);
-
- // make sure we see the cursor from the curent view
- pView->EnsureVisible(pView->GetCursorPos());
});
+ // make sure we see the cursor from the curent view
+ pActiveView->EnsureVisible(pActiveView->GetCursorPos());
// Refresh display
UpdateAllViews(nullptr);
}
/**
- * @brief Update plugin name
- * @param [in] pCmdUI UI component to update.
- */
-void CMergeDoc::OnUpdatePluginName(CCmdUI* pCmdUI)
-{
- String pluginNames;
- if (m_pInfoUnpacker && !m_pInfoUnpacker->m_PluginName.empty())
- pluginNames += m_pInfoUnpacker->m_PluginName + _T("&");
- PrediffingInfo prediffer;
- GetPrediffer(&prediffer);
- if (!prediffer.m_PluginName.empty())
- pluginNames += prediffer.m_PluginName + _T("&");
- pCmdUI->SetText(pluginNames.substr(0, pluginNames.length() - 1).c_str());
-}
-
-/**
* @brief Change number of diff context lines
*/
void CMergeDoc::OnDiffContext(UINT nID)
if (m_nDiffContext >= 0)
m_nDiffContext = -m_nDiffContext - 1;
break;
+ case ID_VIEW_DIFFCONTEXT_INVERT:
+ m_bInvertDiffContext = !m_bInvertDiffContext;
+ break;
}
GetOptionsMgr()->SaveOption(OPT_DIFF_CONTEXT, m_nDiffContext);
+ GetOptionsMgr()->SaveOption(OPT_INVERT_DIFF_CONTEXT, m_bInvertDiffContext);
FlushAndRescan(true);
}
/**
+ * @brief Swap context enable for 3 file compares
+ */
+void CMergeDoc::OnUpdateSwapContext(CCmdUI* pCmdUI)
+{
+ if (m_nBuffers > 2)
+ {
+ pCmdUI->Enable(true);
+ }
+ else
+ {
+ pCmdUI->Enable(false);
+ }
+}
+
+/**
* @brief Update number of diff context lines
*/
void CMergeDoc::OnUpdateDiffContext(CCmdUI* pCmdUI)
bCheck = (m_nDiffContext == 9); break;
case ID_VIEW_DIFFCONTEXT_TOGGLE:
bCheck = false; break;
+ case ID_VIEW_DIFFCONTEXT_INVERT:
+ bCheck = m_bInvertDiffContext; break;
default:
bCheck = (m_nDiffContext < 0); break;
}
pCmdUI->SetCheck(bCheck);
- pCmdUI->Enable(true);
+ pCmdUI->Enable(!(pCmdUI->m_nID == ID_VIEW_DIFFCONTEXT_INVERT && (m_nDiffContext < 0)));
}
/**
{
case OP_TRIVIAL:
++m_nTrivialDiffs;
- // fall through and handle as diff
+ [[fallthrough]];
case OP_DIFF:
case OP_1STONLY:
case OP_2NDONLY:
// We assume file existed, so disappearing means removal
if (!dfi.Update(szPath))
- return FileRemoved;
+ return FileChange::Removed;
int64_t timeDiff = dfi.mtime - fileInfo->mtime;
if (timeDiff < 0) timeDiff = -timeDiff;
}
if (bFileChanged)
- return FileChanged;
+ return FileChange::Changed;
else
- return FileNoChange;
+ return FileChange::NoChange;
}
void CMergeDoc::HideLines()
for (nLine = 0; nLine < nLineCount;)
{
- if (!(m_ptBuf[0]->GetLineFlags(nLine) & (LF_DIFF | LF_GHOST)))
+ bool diff = !!(m_ptBuf[0]->GetLineFlags(nLine) & (LF_DIFF | LF_GHOST));
+ if ((!m_bInvertDiffContext && !diff) || (m_bInvertDiffContext && diff))
{
for (file = 0; file < m_nBuffers; file++)
m_ptBuf[file]->SetLineFlag(nLine, LF_INVISIBLE, true, false, false);
for (; nLine < nLineCount; nLine++)
{
- if (!(m_ptBuf[0]->GetLineFlags(nLine) & (LF_DIFF | LF_GHOST)))
+ diff = !!(m_ptBuf[0]->GetLineFlags(nLine) & (LF_DIFF | LF_GHOST));
+ if ((!m_bInvertDiffContext && !diff) || (m_bInvertDiffContext && diff))
break;
for (file = 0; file < m_nBuffers; file++)
m_ptBuf[file]->SetLineFlag(nLine, LF_INVISIBLE, false, false, false);
{
for (file = 0; file < m_nBuffers; file++)
m_ptBuf[file]->SetLineFlag(nLine, LF_INVISIBLE, false, false, false);
- if (m_ptBuf[0]->GetLineFlags(nLine) & (LF_DIFF | LF_GHOST))
+ diff = !!(m_ptBuf[0]->GetLineFlags(nLine) & (LF_DIFF | LF_GHOST));
+ if ((!m_bInvertDiffContext && diff) || (m_bInvertDiffContext && !diff))
nLineEnd2 = (nLine + 1 + m_nDiffContext >= nLineCount) ? nLineCount-1 : (nLine + 1 + m_nDiffContext);
}
}
if (!PromptAndSaveIfNeeded(true))
return false;
- GetParentFrame()->CloseNow();
+ GetParentFrame()->DestroyWindow();
return true;
}
CDiffTextBuffer *pBuf = m_ptBuf[nBuffer].get();
m_filePaths[nBuffer] = sFileName;
- CRLFSTYLE nCrlfStyle = CRLF_STYLE_AUTOMATIC;
+ CRLFSTYLE nCrlfStyle = CRLFSTYLE::AUTOMATIC;
CString sOpenError;
- retVal = pBuf->LoadFromFile(sFileName, m_pInfoUnpacker.get(),
+ retVal = pBuf->LoadFromFile(sFileName, m_infoUnpacker,
m_strBothFilenames.c_str(), readOnly, nCrlfStyle, encoding, sOpenError);
// if CMergeDoc::CDiffTextBuffer::LoadFromFile failed,
if (!filename.empty())
{
if (strDesc.empty())
- m_nBufferType[index] = BUFFER_NORMAL;
+ m_nBufferType[index] = BUFFERTYPE::NORMAL;
else
- m_nBufferType[index] = BUFFER_NORMAL_NAMED;
+ m_nBufferType[index] = BUFFERTYPE::NORMAL_NAMED;
m_pSaveFileInfo[index]->Update(filename);
m_pRescanFileInfo[index]->Update(filename);
loadSuccess = LoadFile(filename.c_str(), index, readOnly, encoding);
if (FileLoadResult::IsLossy(loadSuccess))
{
- m_ptBuf[index]->FreeAll();
- loadSuccess = LoadFile(filename.c_str(), index, readOnly,
- GuessCodepageEncoding(filename, GetOptionsMgr()->GetInt(OPT_CP_DETECT), -1));
+ // Determine the file encoding by looking at all the contents of the file, not just part of it
+ FileTextEncoding encodingNew = codepage_detect::Guess(filename, GetOptionsMgr()->GetInt(OPT_CP_DETECT), -1);
+ if (encoding != encodingNew)
+ {
+ m_ptBuf[index]->FreeAll();
+ loadSuccess = LoadFile(filename.c_str(), index, readOnly, encodingNew);
+ }
}
}
else
{
- m_nBufferType[index] = BUFFER_UNNAMED;
+ m_nBufferType[index] = BUFFERTYPE::UNNAMED;
m_ptBuf[index]->InitNew();
m_ptBuf[index]->m_encoding = encoding;
m_ptBuf[index]->FinishLoading(); // should clear GGhostTextBuffer::m_RealityBlock when reloading unnamed buffer
return loadSuccess;
}
+void CMergeDoc::SetTableProperties()
+{
+ struct TableProps { bool istable; TCHAR delimiter; TCHAR quote; bool allowNewlinesInQuotes; };
+ auto getTablePropsByFileName = [](const String& path, const std::optional<bool>& enableTableEditing)-> TableProps
+ {
+ const TCHAR quote = GetOptionsMgr()->GetString(OPT_CMP_TBL_QUOTE_CHAR).c_str()[0];
+ FileFilterHelper filterCSV, filterTSV, filterDSV;
+ bool allowNewlineIQuotes = GetOptionsMgr()->GetBool(OPT_CMP_TBL_ALLOW_NEWLINES_IN_QUOTES);
+ const String csvFilePattern = GetOptionsMgr()->GetString(OPT_CMP_CSV_FILEPATTERNS);
+ if (!csvFilePattern.empty())
+ {
+ filterCSV.UseMask(true);
+ filterCSV.SetMask(csvFilePattern);
+ if (filterCSV.includeFile(path))
+ return { true, ',', quote, allowNewlineIQuotes };
+ }
+ const String tsvFilePattern = GetOptionsMgr()->GetString(OPT_CMP_TSV_FILEPATTERNS);
+ if (!tsvFilePattern.empty())
+ {
+ filterTSV.UseMask(true);
+ filterTSV.SetMask(tsvFilePattern);
+ if (filterTSV.includeFile(path))
+ return { true, '\t', quote, allowNewlineIQuotes };
+ }
+ const String dsvFilePattern = GetOptionsMgr()->GetString(OPT_CMP_DSV_FILEPATTERNS);
+ if (!dsvFilePattern.empty())
+ {
+ filterDSV.UseMask(true);
+ filterDSV.SetMask(dsvFilePattern);
+ if (filterDSV.includeFile(path))
+ return { true, GetOptionsMgr()->GetString(OPT_CMP_DSV_DELIM_CHAR).c_str()[0], quote };
+ }
+ if (enableTableEditing.value_or(false))
+ {
+ COpenTableDlg dlg;
+ if (dlg.DoModal() == IDOK)
+ return { true, dlg.m_sDelimiterChar.c_str()[0], dlg.m_sQuoteChar.c_str()[0], dlg.m_bAllowNewlinesInQuotes };
+ }
+ return { false, 0, 0, false };
+ };
+
+ TableProps tableProps[3] = {};
+ int nTableFileIndex = -1;
+ for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ {
+ if (nBuffer == 0 ||
+ paths::FindExtension(m_ptBuf[nBuffer - 1]->GetTempFileName()) != paths::FindExtension(m_ptBuf[nBuffer]->GetTempFileName()))
+ tableProps[nBuffer] = getTablePropsByFileName(m_ptBuf[nBuffer]->GetTempFileName(), m_bEnableTableEditing);
+ else
+ tableProps[nBuffer] = tableProps[nBuffer - 1];
+ if (tableProps[nBuffer].istable)
+ nTableFileIndex = nBuffer;
+ }
+ for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ {
+ if (m_bEnableTableEditing.value_or(true) && nTableFileIndex >= 0)
+ {
+ int i = tableProps[nBuffer].istable ? nBuffer : nTableFileIndex;
+ m_ptBuf[nBuffer]->SetTableEditing(true);
+ m_ptBuf[nBuffer]->ShareColumnWidths(*m_ptBuf[0]);
+ m_ptBuf[nBuffer]->SetAllowNewlinesInQuotes(tableProps[i].allowNewlinesInQuotes);
+ m_ptBuf[nBuffer]->SetFieldDelimiter(tableProps[i].delimiter);
+ m_ptBuf[nBuffer]->SetFieldEnclosure(tableProps[i].quote);
+ m_ptBuf[nBuffer]->JoinLinesForTableEditingMode();
+ }
+ else
+ {
+ m_ptBuf[nBuffer]->SetTableEditing(false);
+ }
+ }
+}
+
/**
* @brief Loads files and does initial rescan.
* @param fileloc [in] File to open to left/middle/right side (path & encoding info)
* @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[])
{
- IDENTLEVEL identical = IDENTLEVEL_NONE;
+ IDENTLEVEL identical = IDENTLEVEL::NONE;
int nRescanResult = RESCAN_OK;
int nBuffer;
FileLocation fileloc[3];
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();
+
const bool bFiltersEnabled = GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED);
// scratchpad : we don't call LoadFile, so
// we need to initialize the unpacker as a "do nothing" one
if (bFiltersEnabled)
{
- if (std::count(m_nBufferType, m_nBufferType + m_nBuffers, BUFFER_UNNAMED) == m_nBuffers)
- {
- m_pInfoUnpacker->Initialize(PLUGIN_MANUAL);
- }
- }
-
- // 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)
+ if (std::count(m_nBufferType, m_nBufferType + m_nBuffers, BUFFERTYPE::UNNAMED) == m_nBuffers)
{
- // Use verify macro to trap possible error in debug.
- VERIFY(pFrame->DestroyWindow());
+ m_infoUnpacker.Initialize(false);
}
- return false;
}
// Warn user if file load was lossy (bad encoding)
// All lines will differ, that is not very interesting and probably not wanted.
// Propose to turn off the option 'sensitive to EOL'
String s = theApp.LoadString(IDS_SUGGEST_IGNOREEOL);
- if (ShowMessageBox(s, MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN | MB_IGNORE_IF_SILENCED, IDS_SUGGEST_IGNOREEOL) == IDYES)
+ if (ShowMessageBox(s, MB_YESNO | MB_ICONWARNING | MB_DONT_ASK_AGAIN, IDS_SUGGEST_IGNOREEOL) == IDYES)
{
diffOptions.bIgnoreEol = true;
m_diffWrapper.SetOptions(&diffOptions);
+
+ CMessageBoxDialog dlg(nullptr, s.c_str(), _T(""), 0, IDS_SUGGEST_IGNOREEOL);
+ const int nFormerResult = dlg.GetFormerResult();
+ if (nFormerResult != -1)
+ {
+ // "Don't ask this question again" checkbox is checked
+ GetOptionsMgr()->SaveOption(OPT_CMP_IGNORE_EOL, true);
+ }
}
}
}
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
- if (bFiltersEnabled && m_pInfoUnpacker->m_textType.length())
- sext[nBuffer] = m_pInfoUnpacker->m_textType;
- else
- sext[nBuffer] = GetFileExt(fileloc[nBuffer].filepath.c_str(), m_strDesc[nBuffer].c_str());
+ sext[nBuffer] = GetFileExt(m_ptBuf[nBuffer]->GetTempFileName().c_str(), m_strDesc[nBuffer].c_str());
ForEachView(nBuffer, [&](auto& pView) {
bTyped[nBuffer] = pView->SetTextType(sext[nBuffer].c_str());
if (bTyped[nBuffer])
if (syntaxHLEnabled)
{
- CCrystalTextView::TextDefinition *enuType = GetView(0, paneTyped)->GetTextType(sext[paneTyped].c_str());
+ CrystalLineParser::TextDefinition *enuType = CrystalLineParser::GetTextType(sext[paneTyped].c_str());
ForEachView([&bTyped, enuType](auto& pView) {
if (!bTyped[pView->m_nThisPane])
pView->SetTextType(enuType);
ForEachView(nBuffer, [](auto& pView) { pView->DocumentsLoaded(); });
- if ((m_nBufferType[nBuffer] == BUFFER_NORMAL) ||
- (m_nBufferType[nBuffer] == BUFFER_NORMAL_NAMED))
+ if ((m_nBufferType[nBuffer] == BUFFERTYPE::NORMAL) ||
+ (m_nBufferType[nBuffer] == BUFFERTYPE::NORMAL_NAMED))
{
nNormalBuffer++;
}
// Inform user that files are identical
// Don't show message if new buffers created
- if (identical == IDENTLEVEL_ALL && nNormalBuffer > 0)
+ if (identical == IDENTLEVEL::ALL && nNormalBuffer > 0)
{
ShowRescanError(nRescanResult, identical);
}
m_diffList.HasSignificantDiffs())
{
int nDiff = m_diffList.FirstSignificantDiff();
- m_pView[0][nPane]->SelectDiff(nDiff, true, false);
- nLineIndex = m_pView[0][nPane]->GetCursorPos().y;
- }
- else
- {
- nLineIndex = 0;
+ if (nDiff != -1)
+ m_pView[0][nPane]->SelectDiff(nDiff, true, false);
+ m_pView[0][nPane]->SetActivePane();
+ return;
}
}
- m_pView[0][nPane]->GotoLine(nLineIndex, false, nPane);
+ m_pView[0][nPane]->GotoLine(nLineIndex < 0 ? 0 : nLineIndex, false, nPane);
}
-void CMergeDoc::ChangeFile(int nBuffer, const String& path)
+void CMergeDoc::ChangeFile(int nBuffer, const String& path, int nLineIndex)
{
if (!PromptAndSaveIfNeeded(true))
return;
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));
- OpenDocs(m_nBuffers, fileloc, bRO, strDesc);
- MoveOnLoad(nBuffer, 0);
+ if (OpenDocs(m_nBuffers, fileloc, bRO, strDesc))
+ MoveOnLoad(nBuffer, nLineIndex);
}
/**
{
DIFFOPTIONS options = {0};
+ m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
+
m_diffWrapper.SetDetectMovedBlocks(GetOptionsMgr()->GetBool(OPT_CMP_MOVED_BLOCKS));
Options::DiffOptions::Load(GetOptionsMgr(), options);
String sText;
bool bChanges = false;
- 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];
}
m_bEditAfterRescan[nBuffer] = true;
}
+bool CMergeDoc::IsEditedAfterRescan(int nBuffer) const
+{
+ if (nBuffer >= 0 && nBuffer < m_nBuffers)
+ return m_bEditAfterRescan[nBuffer];
+
+ for (nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
+ {
+ if (m_bEditAfterRescan[nBuffer])
+ return true;
+ }
+
+ return false;
+}
+
/**
* @brief Update document filenames to title
*/
void CMergeDoc::SetTitle(LPCTSTR lpszTitle)
{
- String sTitle;
- String sFileName[3];
-
- if (lpszTitle != nullptr)
- sTitle = lpszTitle;
- else
- {
- for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
- sFileName[nBuffer] = !m_strDesc[nBuffer].empty() ? m_strDesc[nBuffer] : paths::FindFileName(m_filePaths[nBuffer]);
- if (std::count(&sFileName[0], &sFileName[0] + m_nBuffers, sFileName[0]) == m_nBuffers)
- sTitle = sFileName[0] + strutils::format(_T(" x %d"), m_nBuffers);
- else
- sTitle = strutils::join(&sFileName[0], &sFileName[0] + m_nBuffers, _T(" - "));
- }
+ PrediffingInfo infoPrediffer;
+ GetPrediffer(&infoPrediffer);
+ String sTitle = (lpszTitle != nullptr) ? lpszTitle : CMergeFrameCommon::GetTitleString(m_filePaths, m_strDesc, &m_infoUnpacker, &infoPrediffer);
CDocument::SetTitle(sTitle.c_str());
}
*/
void CMergeDoc::UpdateResources()
{
- CString str;
- int nBuffer;
-
- m_strDesc[0] = _("Untitled left");
- m_strDesc[m_nBuffers - 1] = _("Untitled right");
- if (m_nBuffers == 3)
+ if (m_nBufferType[0] == BUFFERTYPE::UNNAMED)
+ m_strDesc[0] = _("Untitled left");
+ if (m_nBufferType[m_nBuffers - 1] == BUFFERTYPE::UNNAMED)
+ m_strDesc[m_nBuffers - 1] = _("Untitled right");
+ if (m_nBuffers == 3 && m_nBufferType[1] == BUFFERTYPE::UNNAMED)
m_strDesc[1] = _("Untitled middle");
- for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
UpdateHeaderPath(nBuffer);
GetParentFrame()->UpdateResources();
}
/// Swap files and update views
-void CMergeDoc::SwapFiles()
+void CMergeDoc::SwapFiles(int nFromIndex, int nToIndex)
{
- // Swap views
- for (int nGroup = 0; nGroup < m_nGroups; ++nGroup)
+ if ((nFromIndex >= 0 && nFromIndex < m_nBuffers) && (nToIndex >= 0 && nToIndex < m_nBuffers))
{
- int nLeftViewId = m_pView[nGroup][0]->GetDlgCtrlID();
- int nRightViewId = m_pView[nGroup][m_nBuffers - 1]->GetDlgCtrlID();
- m_pView[nGroup][0]->SetDlgCtrlID(nRightViewId);
- m_pView[nGroup][m_nBuffers - 1]->SetDlgCtrlID(nLeftViewId);
- }
-
-
- // Swap buffers and so on
- std::swap(m_ptBuf[0], m_ptBuf[m_nBuffers - 1]);
- for (int nGroup = 0; nGroup < m_nGroups; ++nGroup)
- std::swap(m_pView[nGroup][0], m_pView[nGroup][m_nBuffers - 1]);
- std::swap(m_pSaveFileInfo[0], m_pSaveFileInfo[m_nBuffers - 1]);
- std::swap(m_pRescanFileInfo[0], m_pRescanFileInfo[m_nBuffers - 1]);
- std::swap(m_nBufferType[0], m_nBufferType[m_nBuffers - 1]);
- std::swap(m_bEditAfterRescan[0], m_bEditAfterRescan[m_nBuffers - 1]);
- std::swap(m_strDesc[0], m_strDesc[m_nBuffers - 1]);
+ // Swap views
+ for (int nGroup = 0; nGroup < m_nGroups; ++nGroup)
+ {
+ int nLeftViewId = m_pView[nGroup][nFromIndex]->GetDlgCtrlID();
+ int nRightViewId = m_pView[nGroup][nToIndex]->GetDlgCtrlID();
+ m_pView[nGroup][nFromIndex]->SetDlgCtrlID(nRightViewId);
+ m_pView[nGroup][nToIndex]->SetDlgCtrlID(nLeftViewId);
+ }
- m_filePaths.Swap();
- m_diffList.Swap(0, m_nBuffers - 1);
- for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
- swap(m_pView[nGroup][0]->m_piMergeEditStatus, m_pView[nGroup][m_nBuffers - 1]->m_piMergeEditStatus);
- ClearWordDiffCache();
+ // Swap buffers and so on
+ std::swap(m_ptBuf[nFromIndex], m_ptBuf[nToIndex]);
+ for (int nGroup = 0; nGroup < m_nGroups; ++nGroup)
+ std::swap(m_pView[nGroup][nFromIndex], m_pView[nGroup][nToIndex]);
+ std::swap(m_pSaveFileInfo[nFromIndex], m_pSaveFileInfo[nToIndex]);
+ std::swap(m_pRescanFileInfo[nFromIndex], m_pRescanFileInfo[nToIndex]);
+ std::swap(m_nBufferType[nFromIndex], m_nBufferType[nToIndex]);
+ std::swap(m_bEditAfterRescan[nFromIndex], m_bEditAfterRescan[nToIndex]);
+ std::swap(m_strDesc[nFromIndex], m_strDesc[nToIndex]);
- for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
- {
- m_ptBuf[nBuffer]->m_nThisPane = nBuffer;
+ m_filePaths.Swap(nFromIndex, nToIndex);
+ m_diffList.Swap(nFromIndex, nToIndex);
for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
- m_pView[nGroup][nBuffer]->m_nThisPane = nBuffer;
-
- // Update views
- UpdateHeaderPath(nBuffer);
- }
- GetParentFrame()->UpdateSplitter();
- ForEachView([](auto& pView) { pView->UpdateStatusbar(); });
-
- UpdateAllViews(nullptr);
-}
+ swap(m_pView[nGroup][nFromIndex]->m_piMergeEditStatus, m_pView[nGroup][nToIndex]->m_piMergeEditStatus);
-/**
- * @brief Display unpacker dialog to user & handle user's choices
- */
-bool CMergeDoc::OpenWithUnpackerDialog()
-{
- // let the user choose a handler
- CSelectUnpackerDlg dlg(m_filePaths[0], nullptr);
- // create now a new infoUnpacker to initialize the manual/automatic flag
- PackingInfo infoUnpacker(PLUGIN_AUTO);
- dlg.SetInitialInfoHandler(&infoUnpacker);
+ ClearWordDiffCache();
- if (dlg.DoModal() == IDOK)
- {
- infoUnpacker = dlg.GetInfoHandler();
- Merge7zFormatMergePluginScope scope(&infoUnpacker);
- if (HasZipSupport() && std::count_if(m_filePaths.begin(), m_filePaths.end(), ArchiveGuessFormat) == m_nBuffers)
- {
- DWORD dwFlags[3] = {FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU};
- GetMainFrame()->DoFileOpen(&m_filePaths, dwFlags, m_strDesc, _T(""),
- GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, _T(""), &infoUnpacker);
- CloseNow();
- }
- else
+ for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
- SetUnpacker(&infoUnpacker);
- OnFileReload();
+ m_ptBuf[nBuffer]->m_nThisPane = nBuffer;
+ for (int nGroup = 0; nGroup < m_nGroups; nGroup++)
+ m_pView[nGroup][nBuffer]->m_nThisPane = nBuffer;
+
+ // Update views
+ UpdateHeaderPath(nBuffer);
}
- return true;
- }
- else
- {
- return false;
+ GetParentFrame()->UpdateSplitter();
+ ForEachView([](auto& pView) { pView->UpdateStatusbar(); });
+
+ UpdateAllViews(nullptr);
}
}
fileloc[pane].setPath(m_filePaths[pane]);
}
CPoint pt = GetActiveMergeView()->GetCursorPos();
- OpenDocs(m_nBuffers, fileloc, bRO, m_strDesc);
- MoveOnLoad(GetActiveMergeView()->m_nThisPane, pt.y);
+ if (OpenDocs(m_nBuffers, fileloc, bRO, m_strDesc))
+ MoveOnLoad(GetActiveMergeView()->m_nThisPane, pt.y);
}
/**
DoFileEncodingDialog();
}
-void CMergeDoc::OnCtxtOpenWithUnpacker()
+void CMergeDoc::OnOpenWithUnpacker()
+{
+ CSelectPluginDlg dlg(m_infoUnpacker.GetPluginPipeline(),
+ strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")), true, false);
+ if (dlg.DoModal() != IDOK)
+ return;
+
+ if (!PromptAndSaveIfNeeded(true))
+ return;
+
+ PackingInfo infoUnpacker(dlg.GetPluginPipeline());
+ PathContext paths = m_filePaths;
+ DWORD dwFlags[3] = { FFILEOPEN_NOMRU, FFILEOPEN_NOMRU, FFILEOPEN_NOMRU };
+ String strDesc[3] = { m_strDesc[0], m_strDesc[1], m_strDesc[2] };
+ int nID = m_ptBuf[0]->GetTableEditing() ? ID_MERGE_COMPARE_TABLE : ID_MERGE_COMPARE_TEXT;
+ nID = GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? nID : -1;
+
+ if (GetMainFrame()->DoFileOpen(nID, &paths, dwFlags, strDesc, _T(""), &infoUnpacker))
+ GetParentFrame()->DestroyWindow();
+}
+
+void CMergeDoc::OnApplyPrediffer()
{
- OpenWithUnpackerDialog();
+ PrediffingInfo prediffer;
+ GetPrediffer(&prediffer);
+ // let the user choose a handler
+ CSelectPluginDlg dlg(prediffer.GetPluginPipeline(),
+ strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")), false, false);
+ if (dlg.DoModal() != IDOK)
+ return;
+ prediffer.SetPluginPipeline(dlg.GetPluginPipeline());
+ SetPrediffer(&prediffer);
+ FlushAndRescan(true);
+ SetTitle(nullptr);
}
void CMergeDoc::OnBnClickedFileEncoding()
{
+ if (m_pEncodingErrorBar == nullptr || m_pView[0][0] == nullptr)
+ return;
m_pView[0][0]->GetParentFrame()->ShowControlBar(m_pEncodingErrorBar.get(), FALSE, FALSE);
DoFileEncodingDialog();
}
void CMergeDoc::OnBnClickedPlugin()
{
+ if (m_pEncodingErrorBar == nullptr || m_pView[0][0] == nullptr)
+ return;
m_pView[0][0]->GetParentFrame()->ShowControlBar(m_pEncodingErrorBar.get(), FALSE, FALSE);
- OpenWithUnpackerDialog();
+ OnOpenWithUnpacker();
}
void CMergeDoc::OnBnClickedHexView()
void CMergeDoc::OnOK()
{
+ if (m_pEncodingErrorBar == nullptr || m_pView[0][0] == nullptr)
+ return;
m_pView[0][0]->GetParentFrame()->ShowControlBar(m_pEncodingErrorBar.get(), FALSE, FALSE);
}
-void CMergeDoc::OnFileRecompareAsXML()
+void CMergeDoc::OnFileRecompareAsText()
+{
+ m_bEnableTableEditing = false;
+ OnFileReload();
+}
+
+void CMergeDoc::OnUpdateFileRecompareAsText(CCmdUI *pCmdUI)
+{
+ pCmdUI->Enable(m_ptBuf[0]->GetTableEditing());
+}
+
+void CMergeDoc::OnFileRecompareAsTable()
{
- PackingInfo infoUnpacker(PLUGIN_BUILTIN_XML);
- SetUnpacker(&infoUnpacker);
+ m_bEnableTableEditing = true;
OnFileReload();
}
+void CMergeDoc::OnUpdateFileRecompareAsTable(CCmdUI *pCmdUI)
+{
+ pCmdUI->Enable(!m_ptBuf[0]->GetTableEditing());
+}
+
void CMergeDoc::OnFileRecompareAs(UINT nID)
{
+ if (!PromptAndSaveIfNeeded(true))
+ return;
+
DWORD dwFlags[3] = { 0 };
FileLocation fileloc[3];
+ String strDesc[3];
+ int nBuffers = m_nBuffers;
+ CDirDoc *pDirDoc = m_pDirDoc->GetMainView() ? m_pDirDoc :
+ static_cast<CDirDoc*>(theApp.m_pDirTemplate->CreateNewDocument());
+ PackingInfo infoUnpacker(m_infoUnpacker.GetPluginPipeline());
+
for (int pane = 0; pane < m_nBuffers; pane++)
{
fileloc[pane].setPath(m_filePaths[pane]);
dwFlags[pane] |= FFILEOPEN_NOMRU | (m_ptBuf[pane]->GetReadOnly() ? FFILEOPEN_READONLY : 0);
+ strDesc[pane] = m_strDesc[pane];
}
- 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);
- GetParentFrame()->ShowWindow(SW_RESTORE);
- GetParentFrame()->DestroyWindow();
+ if (ID_UNPACKERS_FIRST <= nID && nID <= ID_UNPACKERS_LAST)
+ {
+ infoUnpacker.SetPluginPipeline(CMainFrame::GetPluginPipelineByMenuId(nID, FileTransform::UnpackerEventNames, ID_UNPACKERS_FIRST));
+ nID = m_ptBuf[0]->GetTableEditing() ? ID_MERGE_COMPARE_TABLE : ID_MERGE_COMPARE_TEXT;
+ nID = GetOptionsMgr()->GetBool(OPT_PLUGINS_OPEN_IN_SAME_FRAME_TYPE) ? nID : -1;
+ }
+
+ if (GetMainFrame()->ShowMergeDoc(nID, pDirDoc, nBuffers, fileloc, dwFlags, strDesc, _T(""), &infoUnpacker))
+ GetParentFrame()->DestroyWindow();
}
// Return file extension either from file name
_T("<title>WinMerge File Compare Report</title>\n")
_T("<style type=\"text/css\">\n")
_T("<!--\n")
- _T("td,th {word-break: break-all; font-size: %dpt;}\n")
+ _T("table {margin: 0; border: 1px solid #a0a0a0; box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.15);}\n")
+ _T("td,th {word-break: break-all; font-size: %dpt;padding: 0 3px;}\n")
_T("tr { vertical-align: top; }\n")
- _T(".ln {text-align: right; word-break: normal; background-color: lightgrey; box-shadow: inset 1px 0px 0px rgba(0, 0, 0, 0.10);}\n")
_T(".title {color: white; background-color: blue; vertical-align: top; padding: 4px 4px; background: linear-gradient(mediumblue, darkblue);}\n")
_T("%s")
_T("-->\n")
_T("</style>\n")
_T("</head>\n")
_T("<body>\n")
- _T("<table cellspacing=\"0\" cellpadding=\"0\" style=\"width: 100%%; margin: 0; border: none;\">\n")
+ _T("<table cellspacing=\"0\" cellpadding=\"0\" style=\"width:100%%;\">\n")
_T("<thead>\n")
_T("<tr>\n");
String header =
}
}
- // left and right title
+ // titles
int nBuffer;
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
- int nLineNumberColumnWidth = 1;
- String data = strutils::format(_T("<th class=\"title\" style=\"width:%d%%\"></th>"),
- nLineNumberColumnWidth);
- file.WriteString(data);
- data = strutils::format(_T("<th class=\"title\" style=\"width:%f%%\">"),
- (double)(100 - nLineNumberColumnWidth * m_nBuffers) / m_nBuffers);
+ String data = strutils::format(_T("<th colspan=\"2\" class=\"title\" style=\"width:%f%%\">"),
+ (double)100 / m_nBuffers);
file.WriteString(data);
file.WriteString(ucr::toTString(CMarkdown::Entities(ucr::toUTF8(paths[nBuffer]))));
file.WriteString(_T("</th>\n"));
if (idx[nBuffer] < nLineCount[nBuffer])
{
// line number
+ int iVisibleLineNumber = 0;
String tdtag = _T("<td class=\"ln\">");
DWORD dwFlags = m_ptBuf[nBuffer]->GetLineFlags(idx[nBuffer]);
- if (nBuffer == 0 &&
- (dwFlags & (LF_DIFF | LF_GHOST))!=0 && (idx[nBuffer] == 0 ||
- (m_ptBuf[nBuffer]->GetLineFlags(idx[nBuffer] - 1) & (LF_DIFF | LF_GHOST))==0 ))
+ if ((dwFlags & LF_GHOST) == 0 && m_pView[0][nBuffer]->GetViewLineNumbers())
+ {
+ iVisibleLineNumber = m_ptBuf[nBuffer]->ComputeRealLine(idx[nBuffer]) + 1;
+ }
+ if (nBuffer == 0 &&
+ (dwFlags & (LF_DIFF | LF_GHOST)) != 0 && (idx[nBuffer] == 0 ||
+ (m_ptBuf[nBuffer]->GetLineFlags(idx[nBuffer] - 1) & (LF_DIFF | LF_GHOST)) == 0))
{
++nDiff;
- tdtag += strutils::format(_T("<a name=\"d%d\" href=\"#d%d\">.</a>"), nDiff, nDiff);
+ if (iVisibleLineNumber > 0)
+ {
+ tdtag += strutils::format(_T("<a name=\"d%d\" href=\"#d%d\">%d</a>"), nDiff, nDiff, iVisibleLineNumber);
+ iVisibleLineNumber = 0;
+ }
+ else
+ tdtag += strutils::format(_T("<a name=\"d%d\" href=\"#d%d\">.</a>"), nDiff, nDiff);
}
- if ((dwFlags & LF_GHOST)==0 && m_pView[0][nBuffer]->GetViewLineNumbers())
- tdtag += strutils::format(_T("%d</td>"), m_ptBuf[nBuffer]->ComputeRealLine(idx[nBuffer]) + 1);
+ if (iVisibleLineNumber > 0)
+ tdtag += strutils::format(_T("%d</td>"), iVisibleLineNumber);
else
tdtag += _T("</td>");
file.WriteString(tdtag);
- // write a line on left/right side
+ // line content
file.WriteString((LPCTSTR)m_pView[0][nBuffer]->GetHTMLLine(idx[nBuffer], _T("td")));
idx[nBuffer]++;
}
if (!SelectFile(AfxGetMainWnd()->GetSafeHwnd(), s, false, folder, _T(""), _("HTML Files (*.htm,*.html)|*.htm;*.html|All Files (*.*)|*.*||"), _T("htm")))
return;
- GenerateReport(s.c_str());
-
- LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
+ if (GenerateReport(s.c_str()))
+ LangMessageBox(IDS_REPORT_SUCCESS, MB_OK | MB_ICONINFORMATION);
}
/**
int nLine[3];
for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
{
- int tmp = m_pView[0][nBuffer]->GetCursorPos().y;
- nLine[nBuffer] = m_ptBuf[nBuffer]->ComputeApparentLine(m_ptBuf[nBuffer]->ComputeRealLine(tmp));
+ int tmp = m_pView[0][nBuffer]->GetCursorPos().y;
+ nLine[nBuffer] = m_ptBuf[nBuffer]->ComputeApparentLine(m_ptBuf[nBuffer]->ComputeRealLine(tmp));
+ }
+
+ // If adding a sync point by selecting a ghost line that is after the last block, Cancel the process adding a sync point.
+ for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
+ if (nLine[nBuffer] >= m_ptBuf[nBuffer]->GetLineCount())
+ {
+ LangMessageBox(IDS_SYNCPOINT_LASTBLOCK, MB_ICONSTOP);
+ return;
+ }
+ for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
if (m_ptBuf[nBuffer]->GetLineFlags(nLine[nBuffer]) & LF_INVALID_BREAKPOINT)
DeleteSyncPoint(nBuffer, nLine[nBuffer], false);
- }
-
+
for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
m_ptBuf[nBuffer]->SetLineFlag(nLine[nBuffer], LF_INVALID_BREAKPOINT, true, false);
FlushAndRescan(true);
}
-/**
- * @brief return true if there are synchronization points
- */
-bool CMergeDoc::HasSyncPoints()
-{
- return m_bHasSyncPoints;
-}
-
std::vector<std::vector<int> > CMergeDoc::GetSyncPointList()
{
std::vector<std::vector<int> > list;