#include "DirDoc.h"
#include "files.h"
#include "FileTransform.h"
+#include "Plugins.h"
#include "unicoder.h"
#include "UniFile.h"
#include "OptionsDef.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"
BEGIN_MESSAGE_MAP(CMergeDoc, CDocument)
//{{AFX_MSG_MAP(CMergeDoc)
+ // [File] menu
ON_COMMAND(ID_FILE_SAVE, OnFileSave)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
ON_COMMAND(ID_FILE_SAVE_LEFT, OnFileSaveLeft)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
ON_COMMAND(ID_FILE_SAVE_MIDDLE, OnFileSaveMiddle)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
ON_COMMAND(ID_FILE_SAVE_RIGHT, OnFileSaveRight)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
ON_COMMAND(ID_FILE_SAVEAS_LEFT, OnFileSaveAsLeft)
- ON_UPDATE_COMMAND_UI(ID_FILE_SAVEAS_MIDDLE, OnUpdateFileSaveAsMiddle)
ON_COMMAND(ID_FILE_SAVEAS_MIDDLE, OnFileSaveAsMiddle)
+ ON_UPDATE_COMMAND_UI(ID_FILE_SAVEAS_MIDDLE, OnUpdateFileSaveAsMiddle)
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_FILE_LEFT_READONLY, OnFileReadOnlyLeft)
+ ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateFileReadOnlyLeft)
+ ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnFileReadOnlyMiddle)
+ ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateFileReadOnlyMiddle)
+ ON_COMMAND(ID_FILE_RIGHT_READONLY, OnFileReadOnlyRight)
+ ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateFileReadOnlyRight)
ON_COMMAND(ID_RESCAN, OnFileReload)
ON_COMMAND(ID_FILE_ENCODING, OnFileEncoding)
- 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_POPUP_OPEN_WITH_UNPACKER, OnCtxtOpenWithUnpacker)
- 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_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(ID_MERGE_COMPARE_XML, OnFileRecompareAsXML)
- ON_UPDATE_COMMAND_UI(ID_MERGE_COMPARE_XML, OnUpdateFileRecompareAsXML)
- ON_COMMAND_RANGE(ID_MERGE_COMPARE_HEX, ID_MERGE_COMPARE_IMAGE, OnFileRecompareAs)
+ ON_COMMAND_RANGE(ID_MERGE_COMPARE_HEX, ID_MERGE_COMPARE_WEBPAGE, OnFileRecompareAs)
+ ON_COMMAND_RANGE(ID_UNPACKERS_FIRST, ID_UNPACKERS_LAST, OnFileRecompareAs)
+ // [View] menu
+ 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_SWAPPANES_SWAP12, (OnViewSwapPanes<0, 1>))
+ ON_COMMAND(ID_SWAPPANES_SWAP23, (OnViewSwapPanes<1, 2>))
+ ON_COMMAND(ID_SWAPPANES_SWAP13, (OnViewSwapPanes<0, 2>))
ON_UPDATE_COMMAND_UI_RANGE(ID_SWAPPANES_SWAP23, ID_SWAPPANES_SWAP13, OnUpdateSwapContext)
+ ON_COMMAND(ID_REFRESH, OnRefresh)
+ // [Tools] menu
+ ON_COMMAND(ID_TOOLS_GENERATEREPORT, OnToolsGenerateReport)
+ ON_COMMAND(ID_TOOLS_GENERATEPATCH, OnToolsGeneratePatch)
+ // [Plugins] menu
+ ON_COMMAND(ID_OPEN_WITH_UNPACKER, OnOpenWithUnpacker)
+ ON_COMMAND(ID_APPLY_PREDIFFER, OnApplyPrediffer)
+ ON_COMMAND_RANGE(ID_NO_PREDIFFER, ID_NO_PREDIFFER, OnPrediffer)
+ ON_COMMAND_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnPrediffer)
+ ON_UPDATE_COMMAND_UI(ID_NO_PREDIFFER, OnUpdatePrediffer)
+ ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnUpdatePrediffer)
+ // Encoding Error dialog
+ ON_BN_CLICKED(IDC_FILEENCODING, OnBnClickedFileEncoding)
+ ON_BN_CLICKED(IDC_PLUGIN, OnBnClickedPlugin)
+ ON_BN_CLICKED(IDC_HEXVIEW, OnBnClickedHexView)
+ ON_COMMAND(IDOK, OnOK)
+ // Status bar
+ ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_RO, OnUpdateStatusRO)
+ ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_RO, OnUpdateStatusRO)
+ ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_RO, OnUpdateStatusRO)
+ ON_UPDATE_COMMAND_UI(ID_STATUS_DIFFNUM, OnUpdateStatusNum)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
, 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)
+, m_CurrentPredifferID(0)
+, m_bChangedSchemeManually(false)
{
DIFFOPTIONS options = {0};
}
m_bEnableRescan = true;
+ m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
+
// COleDateTime m_LastRescan
curUndo = undoTgt.begin();
m_nDiffContext = GetOptionsMgr()->GetInt(OPT_DIFF_CONTEXT);
*/
CMergeDoc::~CMergeDoc()
{
+ GetMainFrame()->UnwatchDocuments(this);
+
if (m_pDirDoc != nullptr)
{
m_pDirDoc->MergeDocClosing(this);
{
if (infoNewHandler != nullptr)
{
- *m_pInfoUnpacker = *infoNewHandler;
+ m_infoUnpacker = *infoNewHandler;
}
}
{
m_diffWrapper.SetPrediffer(infoPrediffer);
}
-void CMergeDoc::GetPrediffer(PrediffingInfo * infoPrediffer)
+
+void CMergeDoc::GetPrediffer(PrediffingInfo * infoPrediffer) const
{
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;
{
m_diffWrapper.SetFilterList(_T(""));
}
+
+ 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()));
+ m_diffWrapper.SetFilterCommentsSourceDef(GetFileExt(m_ptBuf[0]->m_strTempFileName.c_str(), m_strDesc[0].c_str()));
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
// Check if files have been modified since last rescan
// Ignore checking in case of scratchpads (empty filenames)
- if (!m_filePaths[nBuffer].empty())
+ if (!m_filePaths[nBuffer].empty() && !paths::IsURL(m_filePaths[nBuffer]))
{
Changed[nBuffer] = IsFileChangedOnDisk(m_filePaths[nBuffer].c_str(),
fileInfo, false, nBuffer);
else
{
const std::vector<std::vector<int> > syncpoints = GetSyncPointList();
- int nStartLine[3] = {0};
- int nLines[3], nRealLine[3];
+ int nStartLine[3]{};
+ int nLines[3]{}, nRealLine[3]{};
for (size_t i = 0; i <= syncpoints.size(); ++i)
{
// Save text buffer to file
// 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;
}
}
}
+ }
+ }
m_diffList.AppendDiffList(templist, nRealLine);
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
m_ptBuf[nBuffer]->prepareForRescan();
// Divide diff blocks to match lines.
- if (GetOptionsMgr()->GetBool(OPT_CMP_MATCH_SIMILAR_LINES) && m_nBuffers < 3)
- AdjustDiffBlocks();
+ if (GetOptionsMgr()->GetBool(OPT_CMP_MATCH_SIMILAR_LINES))
+ {
+ if (m_nBuffers < 3)
+ AdjustDiffBlocks();
+ else
+ AdjustDiffBlocks3way();
+ }
// Analyse diff-list (updating real line-numbers)
// this operation does not change the modified flag
// 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
{
int nBuffer;
DiffFileInfo fileInfo;
- FileChange FileChange[3];
+ FileChange FileChange[3]{};
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
for (int file = 0; file < m_nBuffers; ++file)
{
const TCHAR *p = m_ptBuf[file]->GetLineChars(i);
- str[file] = p ? p : _T("");
+ str[file] = p ? String(p, m_ptBuf[file]->GetFullLineLength(i)) : _T("");
}
if (std::count(str + 1, str + m_nBuffers, str[0]) != m_nBuffers - 1)
m_diffWrapper.GetOptions(&diffOptions);
// Make the call to stringdiffs, which does all the hard & tedious computations
- std::vector<strdiff::wdiff> worddiffs = strdiff::ComputeWordDiffs(m_nBuffers, str,
+ int result = strdiff::Compare(str[0], str[1],
!diffOptions.bIgnoreCase,
!diffOptions.bIgnoreEol,
diffOptions.nIgnoreWhitespace,
- GetBreakType(), // whitespace only or include punctuation
- GetByteColoringOption());
- if (!worddiffs.empty())
+ diffOptions.bIgnoreNumbers);
+ if (m_nBuffers >= 2 && result == 0)
+ {
+ result = strdiff::Compare(str[1], str[2],
+ !diffOptions.bIgnoreCase,
+ !diffOptions.bIgnoreEol,
+ diffOptions.nIgnoreWhitespace,
+ diffOptions.bIgnoreNumbers);
+ }
+ if (result != 0)
{
for (int file = 0; file < m_nBuffers; ++file)
m_ptBuf[file]->SetLineFlag(i, LF_TRIVIAL, true, false, false);
+ ++m_nTrivialDiffs;
}
}
}
// Files are not binaries, but they are identical
if (identical != IDENTLEVEL::NONE)
{
- static_cast<CMergeFrameCommon*>(GetParentFrame())->ShowIdenticalMessage(m_filePaths, identical == IDENTLEVEL::ALL,
+ CMergeFrameCommon::ShowIdenticalMessage(m_filePaths, identical == IDENTLEVEL::ALL,
[this](LPCTSTR msg, UINT flags, UINT id) -> int { return ShowMessageBox(msg, flags, id); });
}
}
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>
*/
void CMergeDoc::DoAutoMerge(int dstPane)
{
+ if (m_nBuffers < 3)
+ return;
const int lastDiff = m_diffList.GetSize() - 1;
const int firstDiff = 0;
bool bGroupWithPrevious = false;
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);
* @param [in] dr Difference to check.
* @return true if difference lines match, false otherwise.
*/
-bool CMergeDoc::SanityCheckDiff(DIFFRANGE dr) const
+bool CMergeDoc::SanityCheckDiff(const DIFFRANGE& dr) const
{
const int cd_dbegin = dr.dbegin;
const int cd_dend = dr.dend;
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 *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*/)
{
int nGroup = GetActiveMergeView()->m_nThisGroup;
- CMergeEditView *pViewSrc = m_pView[nGroup][srcPane];
CMergeEditView *pViewDst = m_pView[nGroup][dstPane];
CCrystalTextView *pSource = bUpdateView ? pViewDst : nullptr;
ForEachView(dstPane, [currentPos](auto& pView) { pView->SetCursorPos(currentPos); });
}
- // if the current diff contains missing lines, remove them from both sides
- int limit = cd_dend;
-
// curView is the view which is changed, so the opposite of the source view
dbuf.BeginUndoGroup(bGroupWithPrevious);
* @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_MODE::PLUGIN_MANUAL);
+ infoTempUnpacker.Initialize(false);
}
else
{
if (SelectFile(GetActiveMergeView()->GetSafeHwnd(), s, false, strPath.c_str(), title))
{
CDiffTextBuffer *pBuffer = m_ptBuf[nBuffer].get();
- strSavePath = s;
+ strSavePath = std::move(s);
nSaveResult = pBuffer->SaveToFile(strSavePath, false, sError,
- pInfoTempUnpacker);
+ infoTempUnpacker);
if (nSaveResult == SAVE_DONE)
{
}
// 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
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);
}
}
m_ptBuf[nBuffer]->SetModified(false);
- m_pSaveFileInfo[nBuffer]->Update(strSavePath.c_str());
- m_filePaths[nBuffer] = strSavePath;
- m_pRescanFileInfo[nBuffer]->Update(m_filePaths[nBuffer].c_str());
+ m_pSaveFileInfo[nBuffer]->Update(strSavePath);
+ m_filePaths[nBuffer] = std::move(strSavePath);
+ m_pRescanFileInfo[nBuffer]->Update(m_filePaths[nBuffer]);
UpdateHeaderPath(nBuffer);
bSaveSuccess = true;
result = true;
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 (nSaveErrorCode == SAVE_DONE)
{
m_pSaveFileInfo[nBuffer]->Update(strSavePath);
- m_filePaths[nBuffer] = strSavePath;
+ m_filePaths[nBuffer] = std::move(strSavePath);
m_pRescanFileInfo[nBuffer]->Update(m_filePaths[nBuffer]);
UpdateHeaderPath(nBuffer);
bSaveSuccess = true;
if (nRescanResult != RESCAN_SUPPRESSED)
ShowRescanError(nRescanResult, identical);
m_LastRescan = COleDateTime::GetCurrentTime();
+ SetTitle(nullptr);
}
/**
}
/**
+ * @brief Called when "Save" item is updated
+ */
+void CMergeDoc::OnUpdateFileSave(CCmdUI* pCmdUI)
+{
+ bool bModified = false;
+ for (int nPane = 0; nPane < m_nBuffers; nPane++)
+ {
+ if (m_ptBuf[nPane]->IsModified())
+ bModified = true;
+ }
+ pCmdUI->Enable(bModified);
+}
+
+/**
+ * @brief Called when "Save left (as...)" item is updated
+ */
+void CMergeDoc::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(m_ptBuf[0]->IsModified());
+}
+
+/**
+ * @brief Called when "Save middle (as...)" item is updated
+ */
+void CMergeDoc::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(m_nBuffers == 3 && m_ptBuf[1]->IsModified());
+}
+
+/**
+ * @brief Called when "Save right (as...)" item is updated
+ */
+void CMergeDoc::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(m_ptBuf[m_nBuffers - 1]->IsModified());
+}
+
+/**
* @brief Saves left-side file with name asked
*/
void CMergeDoc::OnFileSaveAsLeft()
}
/**
+ * @brief Enable/disable left buffer read-only
+ */
+void CMergeDoc::OnFileReadOnlyLeft()
+{
+ bool bReadOnly = m_ptBuf[0]->GetReadOnly();
+ m_ptBuf[0]->SetReadOnly(!bReadOnly);
+}
+
+/**
+ * @brief Called when "Left read-only" item is updated
+ */
+void CMergeDoc::OnUpdateFileReadOnlyLeft(CCmdUI* pCmdUI)
+{
+ bool bReadOnly = m_ptBuf[0]->GetReadOnly();
+ pCmdUI->Enable(true);
+ pCmdUI->SetCheck(bReadOnly);
+}
+
+/**
+ * @brief Enable/disable middle buffer read-only
+ */
+void CMergeDoc::OnFileReadOnlyMiddle()
+{
+ if (m_nBuffers == 3)
+ {
+ bool bReadOnly = m_ptBuf[1]->GetReadOnly();
+ m_ptBuf[1]->SetReadOnly(!bReadOnly);
+ }
+}
+
+/**
+ * @brief Called when "Middle read-only" item is updated
+ */
+void CMergeDoc::OnUpdateFileReadOnlyMiddle(CCmdUI* pCmdUI)
+{
+ if (m_nBuffers < 3)
+ {
+ pCmdUI->Enable(false);
+ }
+ else
+ {
+ bool bReadOnly = m_ptBuf[1]->GetReadOnly();
+ pCmdUI->Enable(true);
+ pCmdUI->SetCheck(bReadOnly);
+ }
+}
+
+/**
+ * @brief Enable/disable right buffer read-only
+ */
+void CMergeDoc::OnFileReadOnlyRight()
+{
+ bool bReadOnly = m_ptBuf[m_nBuffers - 1]->GetReadOnly();
+ m_ptBuf[m_nBuffers - 1]->SetReadOnly(!bReadOnly);
+}
+
+/**
+ * @brief Called when "Left read-only" item is updated
+ */
+void CMergeDoc::OnUpdateFileReadOnlyRight(CCmdUI* pCmdUI)
+{
+ bool bReadOnly = m_ptBuf[m_nBuffers - 1]->GetReadOnly();
+ pCmdUI->Enable(true);
+ pCmdUI->SetCheck(bReadOnly);
+}
+
+/**
+ * @brief Update readonly statusbaritem
+ */
+void CMergeDoc::OnUpdateStatusRO(CCmdUI* pCmdUI)
+{
+ bool bRO = m_ptBuf[pCmdUI->m_nID - ID_STATUS_PANE0FILE_RO]->GetReadOnly();
+ pCmdUI->Enable(bRO);
+}
+
+/**
* @brief Update diff-number pane text in file compare.
* The diff number pane shows selected difference/amount of differences when
* there is difference selected. If there is no difference selected, then
*/
void CMergeDoc::OnUpdateStatusNum(CCmdUI* pCmdUI)
{
- TCHAR sIdx[32] = { 0 };
TCHAR sCnt[32] = { 0 };
String s;
const int nDiffs = m_diffList.GetSignificantDiffs();
// - show diff number and amount of diffs
else
{
+ TCHAR sIdx[32] = { 0 };
s = _("Difference %1 of %2");
const int signInd = m_diffList.GetSignificantIndex(GetCurrentDiff());
_itot_s(signInd + 1, sIdx, 10);
}
/**
- * @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)
GetOptionsMgr()->SaveOption(OPT_DIFF_CONTEXT, m_nDiffContext);
GetOptionsMgr()->SaveOption(OPT_INVERT_DIFF_CONTEXT, m_bInvertDiffContext);
FlushAndRescan(true);
+ ForEachView([](auto& pView) { if (pView->m_bDetailView) pView->EnsureVisible(pView->GetCursorPos()); });
+}
+
+/**
+ * @brief Swap the positions of the two panes
+ */
+template<int srcPane, int dstPane>
+void CMergeDoc::OnViewSwapPanes()
+{
+ SwapFiles(srcPane, dstPane);
}
/**
*/
void CMergeDoc::OnUpdateSwapContext(CCmdUI* pCmdUI)
{
- if (m_nBuffers > 2)
- {
- pCmdUI->Enable(true);
- }
- else
- {
- pCmdUI->Enable(false);
- }
+ pCmdUI->Enable(m_nBuffers > 2);
}
/**
}
/**
+ * @brief Refresh display using text-buffers
+ * @note This DOES NOT reload files!
+ */
+void CMergeDoc::OnRefresh()
+{
+ FlushAndRescan(true);
+}
+
+/**
* @brief Build the diff array and prepare buffers accordingly (insert ghost lines, set WinMerge flags)
*
* @note After PrimeTextBuffers(), all buffers should have the same length.
ForEachView([](auto& pView) { pView->SetEnableHideLines(true); });
}
+void CMergeDoc::AddToLineFilters(const String& text)
+{
+ theApp.m_pLineFilters->AddFilter(strutils::to_regex(text), true);
+ theApp.m_pLineFilters->SaveFilters();
+}
+
/**
* @brief Asks and then saves modified files.
*
* @brief We have two child views (left & right), so we keep pointers directly
* at them (the MFC view list doesn't have them both)
*/
-void CMergeDoc::AddMergeViews(CMergeEditView *pView[3])
+void CMergeDoc::AddMergeViews(CMergeEditSplitterView* pMergeEditSplitterView, CMergeEditView *pView[3])
{
-
+ m_pMergeEditSplitterView[m_nGroups] = pMergeEditSplitterView;
for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
m_pView[m_nGroups][nBuffer] = pView[nBuffer];
++m_nGroups;
}
-void CMergeDoc::RemoveMergeViews(int nGroup)
+void CMergeDoc::RemoveMergeViews(CMergeEditSplitterView* pMergeEditSplitterView)
{
-
+ ASSERT(m_nGroups > 0);
+ int nGroup;
+ for (nGroup = 0; nGroup < m_nGroups; ++nGroup)
+ {
+ if (m_pMergeEditSplitterView[nGroup] == pMergeEditSplitterView)
+ break;
+ }
+ ASSERT(nGroup < m_nGroups);
for (; nGroup < m_nGroups - 1; nGroup++)
{
for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
m_pView[nGroup][nBuffer] = m_pView[nGroup + 1][nBuffer];
m_pView[nGroup][nBuffer]->m_nThisGroup = nGroup;
}
+ m_pMergeEditSplitterView[nGroup] = m_pMergeEditSplitterView[nGroup + 1];
}
+ for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ m_pView[m_nGroups - 1][nBuffer] = nullptr;
+ m_pMergeEditSplitterView[m_nGroups - 1] = nullptr;
--m_nGroups;
}
if (!PromptAndSaveIfNeeded(true))
return false;
- GetParentFrame()->CloseNow();
+ GetParentFrame()->DestroyWindow();
return true;
}
/**
+ * @brief A string to display as a tooltip for MDITabbar
+ */
+CString CMergeDoc::GetTooltipString() const
+{
+ PrediffingInfo infoPrediffer;
+ GetPrediffer(&infoPrediffer);
+ return CMergeFrameCommon::GetTooltipString(m_filePaths, m_strDesc, &m_infoUnpacker, &infoPrediffer, m_nTrivialDiffs > 0).c_str();
+}
+
+/**
* @brief Loads file to buffer and shows load-errors
* @param [in] sFileName File to open
* @param [in] nBuffer Index (0-based) of buffer to load
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,
* @param [in] encoding File's encoding.
* @return One of FileLoadResult values.
*/
-DWORD CMergeDoc::LoadOneFile(int index, String filename, bool readOnly, const String& strDesc,
+DWORD CMergeDoc::LoadOneFile(int index, const String& filename, bool readOnly, const String& strDesc,
const FileTextEncoding & encoding)
{
DWORD loadSuccess = FileLoadResult::FRESULT_ERROR;;
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
return loadSuccess;
}
-void CMergeDoc::SetTableProperties()
+CMergeDoc::TableProps CMergeDoc::MakeTablePropertiesByFileName(const String& path, const std::optional<bool>& enableTableEditing, bool showDialog)
{
- struct TableProps { bool istable; TCHAR delimiter; TCHAR quote; bool allowNewlinesInQuotes; };
- auto getTablePropsByFileName = [](const String& path, const std::optional<bool>& enableTableEditing)-> TableProps
+ const TCHAR quote = strutils::from_charstr(GetOptionsMgr()->GetString(OPT_CMP_TBL_QUOTE_CHAR));
+ 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())
{
- 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))
+ 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, strutils::from_charstr(GetOptionsMgr()->GetString(OPT_CMP_DSV_DELIM_CHAR)), quote };
+ }
+ if (enableTableEditing.value_or(false))
+ {
+ if (showDialog)
{
COpenTableDlg dlg;
if (dlg.DoModal() == IDOK)
- return { true, dlg.m_sDelimiterChar.c_str()[0], dlg.m_sQuoteChar.c_str()[0], dlg.m_bAllowNewlinesInQuotes };
+ return { true, strutils::from_charstr(dlg.m_sDelimiterChar), strutils::from_charstr(dlg.m_sQuoteChar), dlg.m_bAllowNewlinesInQuotes };
+ }
+ else
+ {
+ return { true, strutils::from_charstr(GetOptionsMgr()->GetString(OPT_CMP_DSV_DELIM_CHAR)), quote };
}
- return { false, 0, 0, false };
- };
+ }
+ return { false, 0, 0, false };
+};
- TableProps tableProps[3] = {};
+void CMergeDoc::SetTableProperties()
+{
+ TableProps tableProps[3] = { };
int nTableFileIndex = -1;
- for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ if (m_pTablePropsPrepared)
{
- 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;
+ nTableFileIndex = 0;
+ tableProps[0] = *m_pTablePropsPrepared;
+ }
+ else
+ {
+ 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] = MakeTablePropertiesByFileName(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++)
{
}
}
+void CMergeDoc::SetTextType(int textType)
+{
+ ForEachView([textType, this](auto& pView) {
+ pView->SetTextType(CrystalLineParser::TextType(textType));
+ pView->SetDisableBSAtSOL(false);
+ m_bChangedSchemeManually = true;
+ });
+}
+
+void CMergeDoc::SetTextType(const String& ext)
+{
+ String ext2 = ext;
+ strutils::replace(ext2, _T("."), _T(""));
+ ForEachView([&ext2, this](auto& pView) {
+ pView->SetTextType(ext2.c_str());
+ pView->SetDisableBSAtSOL(false);
+ m_bChangedSchemeManually = true;
+ });
+}
+
/**
* @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[])
{
+ CWaitCursor waitstatus;
IDENTLEVEL identical = IDENTLEVEL::NONE;
int nRescanResult = RESCAN_OK;
int nBuffer;
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();
{
if (std::count(m_nBufferType, m_nBufferType + m_nBuffers, BUFFERTYPE::UNNAMED) == m_nBuffers)
{
- m_pInfoUnpacker->Initialize(PLUGIN_MODE::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)
- {
- // 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)
// Note: If option enabled, and another side type is not recognized,
// we use recognized type for unrecognized side too.
String sext[3];
- bool bTyped[3];
+ bool bTyped[3]{};
int paneTyped = 0;
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 (m_pView[0][0] != nullptr)
m_pView[0][0]->RepaintLocationPane();
+ GetMainFrame()->WatchDocuments(this);
+
return true;
}
-void CMergeDoc::MoveOnLoad(int nPane, int nLineIndex)
+void CMergeDoc::MoveOnLoad(int nPane, int nLineIndex, bool bRealLine, int nCharIndex)
{
if (nPane < 0)
{
- nPane = GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE);
+ nPane = (m_nBufferType[0] != BUFFERTYPE::UNNAMED) ? GetOptionsMgr()->GetInt(OPT_ACTIVE_PANE) : 0;
if (nPane < 0 || nPane >= m_nBuffers)
nPane = 0;
}
return;
}
}
- m_pView[0][nPane]->GotoLine(nLineIndex < 0 ? 0 : nLineIndex, false, nPane);
+ m_pView[0][nPane]->GotoLine(nLineIndex < 0 ? 0 : nLineIndex, bRealLine, nPane, true, nCharIndex);
}
void CMergeDoc::ChangeFile(int nBuffer, const String& path, int nLineIndex)
FileLocation fileloc[3];
String strDesc[3];
- bool bRO[3];
+ bool bRO[3]{};
for (int pane = 0; pane < m_nBuffers; pane++)
{
bRO[pane] = m_ptBuf[pane]->GetReadOnly();
}
std::copy_n(m_strDesc, m_nBuffers, strDesc);
- strDesc[nBuffer] = _T("");
+ strDesc[nBuffer].clear();
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));
+
+ bool filenameChanged = path != m_filePaths[nBuffer];
+ auto columnWidths = m_ptBuf[nBuffer]->GetColumnWidths();
+ int nActivePane = GetActiveMergeView()->m_nThisPane;
if (OpenDocs(m_nBuffers, fileloc, bRO, strDesc))
- MoveOnLoad(nBuffer, nLineIndex);
+ {
+ // Restore column widths and active pane changed by OpenDocs to their previous state
+ if (!filenameChanged)
+ {
+ m_ptBuf[nBuffer]->SetColumnWidths(columnWidths);
+ }
+ MoveOnLoad(nActivePane, 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);
if (bChanges)
sText.insert(0, _T("* "));
+ if (m_sCurrentHeaderTitle[pane] == sText)
+ return;
+
+ m_sCurrentHeaderTitle[pane] = sText;
+
pf->GetHeaderInterface()->SetText(pane, sText);
SetTitle(nullptr);
*/
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, m_nTrivialDiffs > 0);
CDocument::SetTitle(sTitle.c_str());
}
}
/**
- * @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_MODE::PLUGIN_AUTO);
- dlg.SetInitialInfoHandler(&infoUnpacker);
-
- 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
- {
- SetUnpacker(&infoUnpacker);
- OnFileReload();
- }
- return true;
- }
- else
- {
- return false;
- }
-}
-
-/**
* @brief Reloads the opened files
*/
void CMergeDoc::OnFileReload()
return;
FileLocation fileloc[3];
- bool bRO[3];
+ bool bRO[3]{};
for (int pane = 0; pane < m_nBuffers; pane++)
{
bRO[pane] = m_ptBuf[pane]->GetReadOnly();
fileloc[pane].encoding.m_codepage = m_ptBuf[pane]->getCodepage();
fileloc[pane].setPath(m_filePaths[pane]);
}
+ int nActivePane = GetActiveMergeView()->m_nThisPane;
CPoint pt = GetActiveMergeView()->GetCursorPos();
+ auto columnWidths = m_ptBuf[0]->GetColumnWidths();
if (OpenDocs(m_nBuffers, fileloc, bRO, m_strDesc))
- MoveOnLoad(GetActiveMergeView()->m_nThisPane, pt.y);
+ {
+ // Restore column widths and active pane changed by OpenDocs to their previous state
+ m_ptBuf[0]->SetColumnWidths(columnWidths);
+ MoveOnLoad(nActivePane, pt.y);
+ }
}
/**
DoFileEncodingDialog();
}
-void CMergeDoc::OnCtxtOpenWithUnpacker()
+void CMergeDoc::OnOpenWithUnpacker()
+{
+ CSelectPluginDlg dlg(m_infoUnpacker.GetPluginPipeline(),
+ strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")),
+ CSelectPluginDlg::PluginType::Unpacker, 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 : -nID;
+
+ if (GetMainFrame()->DoFileOrFolderOpen(&paths, dwFlags, strDesc, _T(""),
+ GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID))
+ GetParentFrame()->DestroyWindow();
+}
+
+void CMergeDoc::OnApplyPrediffer()
+{
+ PrediffingInfo prediffer;
+ GetPrediffer(&prediffer);
+ // let the user choose a handler
+ CSelectPluginDlg dlg(prediffer.GetPluginPipeline(),
+ strutils::join(m_filePaths.begin(), m_filePaths.end(), _T("|")),
+ CSelectPluginDlg::PluginType::Prediffer, false);
+ if (dlg.DoModal() != IDOK)
+ return;
+ prediffer.SetPluginPipeline(dlg.GetPluginPipeline());
+ SetPrediffer(&prediffer);
+ m_CurrentPredifferID = -1;
+ FlushAndRescan(true);
+}
+
+/**
+ * @brief Create the dynamic submenu for prediffers
+ *
+ * @note The plugins are grouped in (suggested) and (not suggested)
+ * The IDs follow the order of GetAvailableScripts
+ * For example :
+ * suggested 0 ID_1ST + 0
+ * suggested 1 ID_1ST + 2
+ * suggested 2 ID_1ST + 5
+ * not suggested 0 ID_1ST + 1
+ * not suggested 1 ID_1ST + 3
+ * not suggested 2 ID_1ST + 4
+ */
+HMENU CMergeDoc::createPrediffersSubmenu(HMENU hMenu)
+{
+ // empty the menu
+ int j = GetMenuItemCount(hMenu);
+ while (j --)
+ DeleteMenu(hMenu, 0, MF_BYPOSITION);
+
+ // title
+ AppendMenu(hMenu, MF_STRING, ID_NO_PREDIFFER, _("No prediffer (normal)").c_str());
+
+ if (!GetOptionsMgr()->GetBool(OPT_PLUGINS_ENABLED))
+ return hMenu;
+
+ m_CurrentPredifferID = -1;
+
+ // compute the m_CurrentPredifferID (to set the radio button)
+ PrediffingInfo prediffer;
+ GetPrediffer(&prediffer);
+ if (prediffer.GetPluginPipeline().empty())
+ m_CurrentPredifferID = ID_NO_PREDIFFER;
+
+ // get the scriptlet files
+ const auto& [ suggestedPlugins, allPlugins ]= FileTransform::CreatePluginMenuInfos(
+ m_strBothFilenames, FileTransform::PredifferEventNames, ID_PREDIFFERS_FIRST);
+
+ // build the menu : first part, suggested plugins
+ // title
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ AppendMenu(hMenu, MF_STRING, ID_SUGGESTED_PLUGINS, _("Suggested plugins").c_str());
+
+ for (const auto& [caption, name, id, plugin ] : suggestedPlugins)
+ AppendMenu(hMenu, MF_STRING, id, caption.c_str());
+
+ // build the menu : second part, others plugins
+ // title
+ AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
+ AppendMenu(hMenu, MF_STRING, ID_NOT_SUGGESTED_PLUGINS, _("All plugins").c_str());
+
+ String lastPluginName;
+ String errorMessage;
+ auto result = prediffer.ParsePluginPipeline(errorMessage);
+ if (result.size() > 0)
+ lastPluginName = result.back().name;
+
+ for (const auto& [processType, pluginAry] : allPlugins)
+ {
+ for (const auto& [caption, name, id, plugin] : pluginAry)
+ {
+ if (!name.empty())
+ {
+ AppendMenu(hMenu, MF_STRING, id, caption.c_str());
+ if (lastPluginName == plugin->m_name)
+ m_CurrentPredifferID = id;
+ }
+ }
+ }
+
+ return hMenu;
+}
+
+/**
+ * @brief Called when an editor script item is updated
+ */
+void CMergeDoc::OnUpdatePrediffer(CCmdUI* pCmdUI)
+{
+ pCmdUI->Enable(true);
+
+ PrediffingInfo prediffer;
+ GetPrediffer(&prediffer);
+
+ if (prediffer.GetPluginPipeline().find(_T("<Automatic>")) != String::npos)
+ {
+ pCmdUI->SetRadio(false);
+ return;
+ }
+
+ // Detect when CDiffWrapper::RunFileDiff has canceled a buggy prediffer
+ if (prediffer.GetPluginPipeline().empty())
+ m_CurrentPredifferID = ID_NO_PREDIFFER;
+
+ pCmdUI->SetRadio(pCmdUI->m_nID == static_cast<UINT>(m_CurrentPredifferID));
+}
+
+/**
+ * @brief Handler for all prediffer choices, including ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, ID_NO_PREDIFFER, & specific prediffers
+ */
+void CMergeDoc::OnPrediffer(UINT nID )
+{
+ SetPredifferByMenu(nID);
+ FlushAndRescan(true);
+}
+
+/**
+ * @brief Handler for all prediffer choices.
+ * Prediffer choises include ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO,
+ * ID_NO_PREDIFFER, & specific prediffers.
+ */
+void CMergeDoc::SetPredifferByMenu(UINT nID)
{
- OpenWithUnpackerDialog();
+ // update data for the radio button
+ m_CurrentPredifferID = nID;
+
+ if (nID == ID_NO_PREDIFFER)
+ {
+ // All flags are set correctly during the construction
+ PrediffingInfo infoPrediffer(false);
+ SetPrediffer(&infoPrediffer);
+ return;
+ }
+
+ String pluginName = CMainFrame::GetPluginPipelineByMenuId(nID, FileTransform::PredifferEventNames, ID_PREDIFFERS_FIRST);
+
+ // build a PrediffingInfo structure fom the ID
+ PrediffingInfo prediffer(pluginName);
+
+ // update the prediffer and rescan
+ SetPrediffer(&prediffer);
}
void CMergeDoc::OnBnClickedFileEncoding()
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::OnFileRecompareAsText()
{
m_bEnableTableEditing = false;
- PackingInfo infoUnpacker;
- SetUnpacker(&infoUnpacker);
OnFileReload();
}
void CMergeDoc::OnUpdateFileRecompareAsText(CCmdUI *pCmdUI)
{
- pCmdUI->Enable(m_pInfoUnpacker->m_PluginOrPredifferMode == PLUGIN_MODE::PLUGIN_BUILTIN_XML ||
- m_ptBuf[0]->GetTableEditing());
+ pCmdUI->Enable(m_ptBuf[0]->GetTableEditing());
}
void CMergeDoc::OnFileRecompareAsTable()
{
+ if (m_ptBuf[0]->GetTableEditing())
+ {
+ COpenTableDlg dlg;
+ if (dlg.DoModal() != IDOK)
+ return;
+ m_pTablePropsPrepared.reset(new TableProps{ true, strutils::from_charstr(dlg.m_sDelimiterChar), strutils::from_charstr(dlg.m_sQuoteChar), dlg.m_bAllowNewlinesInQuotes });
+ }
m_bEnableTableEditing = true;
- PackingInfo infoUnpacker;
- SetUnpacker(&infoUnpacker);
OnFileReload();
}
void CMergeDoc::OnUpdateFileRecompareAsTable(CCmdUI *pCmdUI)
{
- pCmdUI->Enable(!m_ptBuf[0]->GetTableEditing());
-}
-
-void CMergeDoc::OnFileRecompareAsXML()
-{
- m_bEnableTableEditing = false;
- PackingInfo infoUnpacker(PLUGIN_MODE::PLUGIN_BUILTIN_XML);
- SetUnpacker(&infoUnpacker);
- OnFileReload();
-}
-
-void CMergeDoc::OnUpdateFileRecompareAsXML(CCmdUI *pCmdUI)
-{
- pCmdUI->Enable(m_pInfoUnpacker->m_PluginOrPredifferMode != PLUGIN_MODE::PLUGIN_BUILTIN_XML);
+ pCmdUI->Enable(true);
}
void CMergeDoc::OnFileRecompareAs(UINT nID)
{
+ if (!PromptAndSaveIfNeeded(true))
+ return;
+
DWORD dwFlags[3] = { 0 };
- FileLocation fileloc[3];
+ PathContext paths = m_filePaths;
+ String strDesc[3];
+ int nBuffers = m_nBuffers;
+ 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 : -static_cast<int>(nID);
+ }
+
+ if (GetMainFrame()->DoFileOrFolderOpen(&m_filePaths, dwFlags, strDesc, _T(""),
+ GetOptionsMgr()->GetBool(OPT_CMP_INCLUDE_SUBDIRS), nullptr, &infoUnpacker, nullptr, nID))
+ GetParentFrame()->DestroyWindow();
}
// Return file extension either from file name
}
file.SetCodepage(ucr::CP_UTF_8);
-
CString headerText =
_T("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n")
_T("\t\"http://www.w3.org/TR/html4/loose.dtd\">\n")
_T("</style>\n")
_T("</head>\n")
_T("<body>\n")
- _T("<table cellspacing=\"0\" cellpadding=\"0\" style=\"width:100%%;\">\n")
- _T("<thead>\n")
- _T("<tr>\n");
+ _T("<table cellspacing=\"0\" cellpadding=\"0\" style=\"width:100%%;\">\n");
String header =
strutils::format((LPCTSTR)headerText, nFontSize, (LPCTSTR)m_pView[0][0]->GetHTMLStyles());
file.WriteString(header);
+ file.WriteString(_T("<colgroup>\n"));
+ double marginWidth = m_pView[0][0]->GetViewLineNumbers() ?
+ strutils::to_str(m_pView[0][0]->GetLineCount()).length() / 1.5 + 0.5 : 0.5;
+ for (int nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
+ {
+ String data = strutils::format(
+ _T("<col style=\"width: %.1fem;\" />\n")
+ _T("<col style=\"width: calc(100vw / %d - %.1fem);\" />\n"),
+ marginWidth, m_nBuffers, marginWidth);
+ file.WriteString(data);
+ }
+ file.WriteString(
+ _T("</colgroup>\n")
+ _T("<thead>\n")
+ _T("<tr>\n"));
+
// Get paths
// If archive, use archive path + folder + filename inside archive
// If desc text given, use it
if (m_pDirDoc != nullptr && m_pDirDoc->IsArchiveFolders())
{
for (int i = 0; i < paths.GetSize(); i++)
- m_pDirDoc->ApplyDisplayRoot(i, paths[i]);
+ {
+ if (!m_strDesc[i].empty())
+ paths[i] = m_strDesc[i];
+ else
+ m_pDirDoc->ApplyDisplayRoot(i, paths[i]);
+ }
}
else
{
int nBuffer;
for (nBuffer = 0; nBuffer < m_nBuffers; nBuffer++)
{
- String data = strutils::format(_T("<th colspan=\"2\" class=\"title\" style=\"width:%f%%\">"),
- (double)100 / m_nBuffers);
+ String data = _T("<th colspan=\"2\" class=\"title\">");
file.WriteString(data);
file.WriteString(ucr::toTString(CMarkdown::Entities(ucr::toUTF8(paths[nBuffer]))));
file.WriteString(_T("</th>\n"));
*/
void CMergeDoc::AddSyncPoint()
{
- int nLine[3];
+ int nLine[3]{};
for (int nBuffer = 0; nBuffer < m_nBuffers; ++nBuffer)
{
int tmp = m_pView[0][nBuffer]->GetCursorPos().y;