1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
8 * @file MergeEditView.cpp
10 * @brief Implementation of the CMergeEditView class
14 #include "MergeEditView.h"
18 #include "LocationView.h"
21 #include "OptionsMgr.h"
22 #include "OptionsDiffColors.h"
23 #include "FileTransform.h"
25 #include "WMGotoDlg.h"
26 #include "OptionsDef.h"
27 #include "SyntaxColors.h"
28 #include "MergeEditFrm.h"
29 #include "MergeLineFlags.h"
31 #include "DropHandler.h"
33 #include "ShellContextMenu.h"
40 using CrystalLineParser::TEXTBLOCK;
42 /** @brief Timer ID for delayed rescan. */
43 const UINT IDT_RESCAN = 2;
44 /** @brief Timer timeout for delayed rescan. */
45 const UINT RESCAN_TIMEOUT = 1000;
47 /** @brief Location for file compare specific help to open. */
48 static TCHAR MergeViewHelpLocation[] = _T("::/htmlhelp/Compare_files.html");
50 /////////////////////////////////////////////////////////////////////////////
53 IMPLEMENT_DYNCREATE(CMergeEditView, CCrystalEditViewEx)
55 CMergeEditView::CMergeEditView()
56 : m_bCurrentLineIsDiff(false)
59 , m_bDetailView(false)
60 , m_piMergeEditStatus(nullptr)
61 , m_bAutomaticRescan(false)
62 , fTimerWaitingForIdle(0)
65 , m_CurrentPredifferID(0)
67 SetParser(&m_xParser);
69 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
72 CMergeEditView::~CMergeEditView()
77 BEGIN_MESSAGE_MAP(CMergeEditView, CCrystalEditViewEx)
78 //{{AFX_MSG_MAP(CMergeEditView)
79 ON_COMMAND(ID_CURDIFF, OnCurdiff)
80 ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
81 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
82 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
83 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
84 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
85 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
86 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
87 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
88 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
89 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
90 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
91 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
92 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
93 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
94 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
95 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
96 ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
97 ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
98 ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
99 ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
100 ON_COMMAND(ID_NEXTDIFFLM, OnNextdiffLM)
101 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLM, OnUpdateNextdiffLM)
102 ON_COMMAND(ID_PREVDIFFLM, OnPrevdiffLM)
103 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLM, OnUpdatePrevdiffLM)
104 ON_COMMAND(ID_NEXTDIFFLR, OnNextdiffLR)
105 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLR, OnUpdateNextdiffLR)
106 ON_COMMAND(ID_PREVDIFFLR, OnPrevdiffLR)
107 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLR, OnUpdatePrevdiffLR)
108 ON_COMMAND(ID_NEXTDIFFMR, OnNextdiffMR)
109 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMR, OnUpdateNextdiffMR)
110 ON_COMMAND(ID_PREVDIFFMR, OnPrevdiffMR)
111 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMR, OnUpdatePrevdiffMR)
112 ON_COMMAND(ID_NEXTDIFFLO, OnNextdiffLO)
113 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLO, OnUpdateNextdiffLO)
114 ON_COMMAND(ID_PREVDIFFLO, OnPrevdiffLO)
115 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLO, OnUpdatePrevdiffLO)
116 ON_COMMAND(ID_NEXTDIFFMO, OnNextdiffMO)
117 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMO, OnUpdateNextdiffMO)
118 ON_COMMAND(ID_PREVDIFFMO, OnPrevdiffMO)
119 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMO, OnUpdatePrevdiffMO)
120 ON_COMMAND(ID_NEXTDIFFRO, OnNextdiffRO)
121 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFRO, OnUpdateNextdiffRO)
122 ON_COMMAND(ID_PREVDIFFRO, OnPrevdiffRO)
123 ON_UPDATE_COMMAND_UI(ID_PREVDIFFRO, OnUpdatePrevdiffRO)
124 ON_WM_LBUTTONDBLCLK()
127 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
128 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
129 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
130 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
131 ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
132 ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
133 ON_COMMAND(ID_L2R, OnL2r)
134 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
135 ON_COMMAND(ID_R2L, OnR2l)
136 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
137 ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
138 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
139 ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
140 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
141 ON_COMMAND(ID_ADD_SYNCPOINT, OnAddSyncPoint)
142 ON_COMMAND(ID_CLEAR_SYNCPOINTS, OnClearSyncPoints)
143 ON_UPDATE_COMMAND_UI(ID_CLEAR_SYNCPOINTS, OnUpdateClearSyncPoints)
144 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
145 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
146 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
148 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
149 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
150 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
151 ON_COMMAND(ID_REFRESH, OnRefresh)
152 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
153 ON_COMMAND(ID_SELECTLINEDIFF, OnSelectLineDiff<false>)
154 ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
155 ON_COMMAND(ID_SELECTPREVLINEDIFF, OnSelectLineDiff<true>)
156 ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
158 ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateEditReplace)
159 ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
160 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
161 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
162 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
163 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
164 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
165 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_RO, OnUpdateStatusRO)
166 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_RO, OnUpdateStatusRO)
167 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_RO, OnUpdateStatusRO)
168 ON_COMMAND_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnConvertEolTo)
169 ON_UPDATE_COMMAND_UI_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnUpdateConvertEolTo)
170 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_EOL, OnUpdateStatusEOL)
171 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_EOL, OnUpdateStatusEOL)
172 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_EOL, OnUpdateStatusEOL)
173 ON_COMMAND(ID_L2RNEXT, OnL2RNext)
174 ON_UPDATE_COMMAND_UI(ID_L2RNEXT, OnUpdateL2RNext)
175 ON_COMMAND(ID_R2LNEXT, OnR2LNext)
176 ON_UPDATE_COMMAND_UI(ID_R2LNEXT, OnUpdateR2LNext)
177 ON_COMMAND(ID_WINDOW_CHANGE_PANE, OnChangePane)
178 ON_COMMAND(ID_NEXT_PANE, OnChangePane)
179 ON_COMMAND(ID_EDIT_WMGOTO, OnWMGoto)
180 ON_COMMAND(ID_FILE_SHELLMENU, OnShellMenu)
181 ON_UPDATE_COMMAND_UI(ID_FILE_SHELLMENU, OnUpdateShellMenu)
182 ON_COMMAND_RANGE(ID_SCRIPT_FIRST, ID_SCRIPT_LAST, OnScripts)
183 ON_COMMAND(ID_NO_PREDIFFER, OnNoPrediffer)
184 ON_UPDATE_COMMAND_UI(ID_NO_PREDIFFER, OnUpdateNoPrediffer)
185 ON_COMMAND_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnPrediffer)
186 ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnUpdatePrediffer)
189 ON_COMMAND(ID_EDIT_COPY_LINENUMBERS, OnEditCopyLineNumbers)
190 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY_LINENUMBERS, OnUpdateEditCopyLinenumbers)
191 ON_COMMAND(ID_VIEW_LINEDIFFS, OnViewLineDiffs)
192 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewLineDiffs)
193 ON_COMMAND(ID_VIEW_WORDWRAP, OnViewWordWrap)
194 ON_UPDATE_COMMAND_UI(ID_VIEW_WORDWRAP, OnUpdateViewWordWrap)
195 ON_COMMAND(ID_VIEW_LINENUMBERS, OnViewLineNumbers)
196 ON_UPDATE_COMMAND_UI(ID_VIEW_LINENUMBERS, OnUpdateViewLineNumbers)
197 ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace)
198 ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace)
199 ON_COMMAND(ID_FILE_OPEN_REGISTERED, OnOpenFile)
200 ON_COMMAND(ID_FILE_OPEN_WITHEDITOR, OnOpenFileWithEditor)
201 ON_COMMAND(ID_FILE_OPEN_WITH, OnOpenFileWith)
202 ON_COMMAND(ID_VIEW_SWAPPANES, OnViewSwapPanes)
203 ON_UPDATE_COMMAND_UI(ID_NO_EDIT_SCRIPTS, OnUpdateNoEditScripts)
206 ON_COMMAND(ID_HELP, OnHelp)
207 ON_COMMAND(ID_VIEW_FILEMARGIN, OnViewMargin)
208 ON_UPDATE_COMMAND_UI(ID_VIEW_FILEMARGIN, OnUpdateViewMargin)
209 ON_UPDATE_COMMAND_UI(ID_VIEW_CHANGESCHEME, OnUpdateViewChangeScheme)
210 ON_COMMAND_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnChangeScheme)
211 ON_UPDATE_COMMAND_UI_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnUpdateChangeScheme)
213 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
214 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
215 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
216 ON_COMMAND(ID_WINDOW_SPLIT, OnWindowSplit)
217 ON_UPDATE_COMMAND_UI(ID_WINDOW_SPLIT, OnUpdateWindowSplit)
222 /////////////////////////////////////////////////////////////////////////////
223 // CMergeEditView diagnostics
226 CMergeDoc* CMergeEditView::GetDocument() // non-debug version is inline
228 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMergeDoc)));
229 return (CMergeDoc*)m_pDocument;
234 /////////////////////////////////////////////////////////////////////////////
235 // CMergeEditView message handlers
238 * @brief Return text buffer for file in view
240 CCrystalTextBuffer *CMergeEditView::LocateTextBuffer()
242 return GetDocument()->m_ptBuf[m_nThisPane].get();
246 * @brief Update any resources necessary after a GUI language change
248 void CMergeEditView::UpdateResources()
252 CMergeEditView *CMergeEditView::GetGroupView(int nBuffer) const
254 return GetDocument()->GetView(m_nThisGroup, nBuffer);
257 void CMergeEditView::PrimeListWithFile()
259 // Set the tab size now, just in case the options change...
260 // We don't update it at the end of OnOptions,
261 // we can update it safely now
262 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
265 * @brief Return text from line given
267 CString CMergeEditView::GetLineText(int idx)
269 return GetLineChars(idx);
273 * @brief Return text from selection
275 CString CMergeEditView::GetSelectedText()
277 CPoint ptStart, ptEnd;
279 GetSelection(ptStart, ptEnd);
280 if (ptStart != ptEnd)
281 GetTextWithoutEmptys(ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, strText);
286 * @brief Get diffs inside selection.
287 * @param [out] firstDiff First diff inside selection
288 * @param [out] lastDiff Last diff inside selection
289 * @note -1 is returned in parameters if diffs cannot be determined
290 * @todo This shouldn't be called when there is no diffs, so replace
291 * first 'if' with ASSERT()?
293 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff)
298 CMergeDoc *pd = GetDocument();
299 const int nDiffs = pd->m_diffList.GetSignificantDiffs();
303 int firstLine, lastLine;
304 GetFullySelectedLines(firstLine, lastLine);
305 if (lastLine < firstLine)
308 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
309 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
310 if (firstDiff != -1 && lastDiff != -1)
314 // Check that first selected line is first diff's first line or above it
315 VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
316 if ((int)di.dbegin < firstLine)
318 if (firstDiff < lastDiff)
322 // Check that last selected line is last diff's last line or below it
323 VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
324 if ((int)di.dend > lastLine)
326 if (firstDiff < lastDiff)
330 // Special case: one-line diff is not selected if cursor is in it
331 if (firstLine == lastLine)
340 * @brief Get diffs inside selection.
341 * @param [out] firstDiff First diff inside selection
342 * @param [out] lastDiff Last diff inside selection
343 * @param [out] firstWordDiff First word level diff inside selection
344 * @param [out] lastWordDiff Last word level diff inside selection
345 * @note -1 is returned in parameters if diffs cannot be determined
346 * @todo This shouldn't be called when there is no diffs, so replace
347 * first 'if' with ASSERT()?
349 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff, int & firstWordDiff, int & lastWordDiff, const CPoint *pptStart, const CPoint *pptEnd)
356 CMergeDoc *pd = GetDocument();
357 const int nDiffs = pd->m_diffList.GetSignificantDiffs();
361 int firstLine, lastLine;
362 CPoint ptStart, ptEnd;
363 GetSelection(ptStart, ptEnd);
364 if (pptStart != nullptr)
366 if (pptEnd != nullptr)
368 firstLine = ptStart.y;
371 firstDiff = pd->m_diffList.LineToDiff(firstLine);
372 bool firstLineIsNotInDiff = firstDiff == -1;
375 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
380 lastDiff = pd->m_diffList.LineToDiff(lastLine);
381 bool lastLineIsNotInDiff = lastDiff == -1;
383 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
384 if (lastDiff < firstDiff)
391 if (firstDiff != -1 && lastDiff != -1)
395 if (ptStart != ptEnd)
397 VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
398 if (lastLineIsNotInDiff && (firstLineIsNotInDiff || (di.dbegin == firstLine && ptStart.x == 0)))
404 if (firstWordDiff == -1)
406 vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(firstDiff);
407 for (size_t i = 0; i < worddiffs.size(); ++i)
409 int worddiffLen = worddiffs[i].end[m_nThisPane] - worddiffs[i].begin[m_nThisPane];
410 if (worddiffs[i].endline[m_nThisPane] > firstLine ||
411 (firstLine == worddiffs[i].endline[m_nThisPane] &&
412 worddiffs[i].end[m_nThisPane] - (worddiffLen == 0 ? 0 : 1) >= ptStart.x))
414 firstWordDiff = static_cast<int>(i);
419 if (firstLine >= di.dbegin && firstWordDiff == -1)
426 VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
427 vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(lastDiff);
428 for (size_t i = worddiffs.size() - 1; i != (size_t)-1; --i)
430 if (worddiffs[i].beginline[m_nThisPane] < lastLine ||
431 (lastLine == worddiffs[i].beginline[m_nThisPane] && worddiffs[i].begin[m_nThisPane] + 1 <= ptEnd.x))
433 lastWordDiff = static_cast<int>(i);
438 if (lastLine <= di.dend && lastWordDiff == -1)
441 if (firstDiff == lastDiff && (lastWordDiff != -1 && lastWordDiff < firstWordDiff))
448 else if (lastDiff < firstDiff || (firstDiff == lastDiff && firstWordDiff == -1 && lastWordDiff == -1))
465 ASSERT(firstDiff == -1 ? (lastDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
466 ASSERT(lastDiff == -1 ? (firstDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
467 ASSERT(firstDiff != -1 ? firstWordDiff != -1 : true);
470 std::map<int, std::vector<int>> CMergeEditView::GetColumnSelectedWordDiffIndice()
472 CMergeDoc *pDoc = GetDocument();
473 std::map<int, std::vector<int>> ret;
474 std::map<int, std::vector<int> *> list;
475 CPoint ptStart, ptEnd;
476 GetSelection(ptStart, ptEnd);
477 for (int nLine = ptStart.y; nLine <= ptEnd.y; ++nLine)
479 if (pDoc->m_diffList.LineToDiff(nLine) != -1)
481 int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
483 GetColumnSelection(nLine, nLeft, nRight);
484 CPoint ptStart2, ptEnd2;
487 ptStart2.y = ptEnd2.y = nLine;
488 GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff, &ptStart2, &ptEnd2);
489 if (firstDiff != -1 && lastDiff != -1)
491 std::vector<int> *pWordDiffs;
492 if (list.find(firstDiff) == list.end())
493 list.insert(std::pair<int, std::vector<int> *>(firstDiff, new std::vector<int>()));
494 pWordDiffs = list[firstDiff];
495 for (int i = firstWordDiff; i <= lastWordDiff; ++i)
497 if (pWordDiffs->empty() || i != (*pWordDiffs)[pWordDiffs->size() - 1])
498 pWordDiffs->push_back(i);
503 for (auto& it : list)
504 ret.insert(std::pair<int, std::vector<int>>(it.first, *it.second));
508 void CMergeEditView::OnInitialUpdate()
511 CCrystalEditViewEx::OnInitialUpdate();
513 SetFont(dynamic_cast<CMainFrame*>(AfxGetMainWnd())->m_lfDiff);
514 SetAlternateDropTarget(new DropHandler(std::bind(&CMergeEditView::OnDropFiles, this, std::placeholders::_1)));
520 void CMergeEditView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
522 CCrystalEditViewEx::OnActivateView(bActivate, pActivateView, pDeactiveView);
524 CMergeDoc* pDoc = GetDocument();
525 pDoc->UpdateHeaderActivity(m_nThisPane, !!bActivate);
528 std::vector<CrystalLineParser::TEXTBLOCK> CMergeEditView::GetMarkerTextBlocks(int nLineIndex) const
532 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
533 return std::vector<CrystalLineParser::TEXTBLOCK>();
535 return CCrystalTextView::GetMarkerTextBlocks(nLineIndex);
538 std::vector<TEXTBLOCK> CMergeEditView::GetAdditionalTextBlocks (int nLineIndex)
540 static const std::vector<TEXTBLOCK> emptyBlocks;
543 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
547 DWORD dwLineFlags = GetLineFlags(nLineIndex);
548 if ((dwLineFlags & LF_SNP) == LF_SNP || (dwLineFlags & LF_DIFF) != LF_DIFF || (dwLineFlags & LF_MOVED) == LF_MOVED)
551 if (!GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT))
554 CMergeDoc *pDoc = GetDocument();
555 if (pDoc->IsEditedAfterRescan(m_nThisPane))
558 int nDiff = pDoc->m_diffList.LineToDiff(nLineIndex);
563 pDoc->m_diffList.GetDiff(nDiff, cd);
564 int unemptyLineCount = 0;
565 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
567 if (cd.begin[nPane] != cd.end[nPane] + 1)
570 if (unemptyLineCount < 2)
573 vector<WordDiff> worddiffs = pDoc->GetWordDiffArray(nLineIndex);
574 size_t nWordDiffs = worddiffs.size();
576 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
578 std::vector<TEXTBLOCK> blocks(nWordDiffs * 2 + 1);
579 blocks[0].m_nCharPos = 0;
580 blocks[0].m_nColorIndex = COLORINDEX_NONE;
581 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
583 for (i = 0, j = 1; i < nWordDiffs; i++)
585 if (worddiffs[i].beginline[m_nThisPane] > nLineIndex || worddiffs[i].endline[m_nThisPane] < nLineIndex )
587 if (pDoc->m_nBuffers > 2)
589 if (m_nThisPane == 0 && worddiffs[i].op == OP_3RDONLY)
591 else if (m_nThisPane == 2 && worddiffs[i].op == OP_1STONLY)
594 int begin[3], end[3];
595 bool deleted = false;
596 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
598 begin[pane] = (worddiffs[i].beginline[pane] < nLineIndex) ? 0 : worddiffs[i].begin[pane];
599 end[pane] = (worddiffs[i].endline[pane] > nLineIndex) ? GetGroupView(pane)->GetLineLength(nLineIndex) : worddiffs[i].end[pane];
600 if (worddiffs[i].beginline[pane] == worddiffs[i].endline[pane] &&
601 worddiffs[i].begin[pane] == worddiffs[i].end[pane])
604 blocks[j].m_nCharPos = begin[m_nThisPane];
605 if (lineInCurrentDiff)
607 if (m_cachedColors.clrSelDiffText != CLR_NONE)
608 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT1 | COLORINDEX_APPLYFORCE;
610 blocks[j].m_nColorIndex = COLORINDEX_NONE;
611 blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE |
612 (deleted ? COLORINDEX_HIGHLIGHTBKGND4 : COLORINDEX_HIGHLIGHTBKGND1);
616 if (m_cachedColors.clrDiffText != CLR_NONE)
617 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT2 | COLORINDEX_APPLYFORCE;
619 blocks[j].m_nColorIndex = COLORINDEX_NONE;
620 blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE |
621 (deleted ? COLORINDEX_HIGHLIGHTBKGND3 : COLORINDEX_HIGHLIGHTBKGND2);
624 blocks[j].m_nCharPos = end[m_nThisPane];
625 blocks[j].m_nColorIndex = COLORINDEX_NONE;
626 blocks[j].m_nBgColorIndex = COLORINDEX_NONE;
635 COLORREF CMergeEditView::GetColor(int nColorIndex)
637 switch (nColorIndex & ~COLORINDEX_APPLYFORCE)
639 case COLORINDEX_HIGHLIGHTBKGND1:
640 return m_cachedColors.clrSelWordDiff;
641 case COLORINDEX_HIGHLIGHTTEXT1:
642 return m_cachedColors.clrSelWordDiffText;
643 case COLORINDEX_HIGHLIGHTBKGND2:
644 return m_cachedColors.clrWordDiff;
645 case COLORINDEX_HIGHLIGHTTEXT2:
646 return m_cachedColors.clrWordDiffText;
647 case COLORINDEX_HIGHLIGHTBKGND3:
648 return m_cachedColors.clrWordDiffDeleted;
649 case COLORINDEX_HIGHLIGHTBKGND4:
650 return m_cachedColors.clrSelWordDiffDeleted;
653 return CCrystalTextView::GetColor(nColorIndex);
658 * @brief Determine text and background color for line
659 * @param [in] nLineIndex Index of line in view (NOT line in file)
660 * @param [out] crBkgnd Backround color for line
661 * @param [out] crText Text color for line
663 void CMergeEditView::GetLineColors(int nLineIndex, COLORREF & crBkgnd,
664 COLORREF & crText, bool & bDrawWhitespace)
666 DWORD ignoreFlags = 0;
667 GetLineColors2(nLineIndex, ignoreFlags, crBkgnd, crText, bDrawWhitespace);
671 * @brief Determine text and background color for line
672 * @param [in] nLineIndex Index of line in view (NOT line in file)
673 * @param [in] ignoreFlags Flags that caller wishes ignored
674 * @param [out] crBkgnd Backround color for line
675 * @param [out] crText Text color for line
677 * This version allows caller to suppress particular flags
679 void CMergeEditView::GetLineColors2(int nLineIndex, DWORD ignoreFlags, COLORREF & crBkgnd,
680 COLORREF & crText, bool & bDrawWhitespace)
682 if (GetLineCount() <= nLineIndex)
685 DWORD dwLineFlags = GetLineFlags(nLineIndex);
687 if (dwLineFlags & ignoreFlags)
688 dwLineFlags &= (~ignoreFlags);
692 // Line with WinMerge flag,
693 // Lines with only the LF_DIFF/LF_TRIVIAL flags are not colored with Winmerge colors
694 if (dwLineFlags & (LF_WINMERGE_FLAGS & ~LF_DIFF & ~LF_TRIVIAL & ~LF_MOVED & ~LF_SNP))
696 crText = m_cachedColors.clrDiffText;
697 bDrawWhitespace = true;
699 if (dwLineFlags & LF_GHOST)
701 crBkgnd = m_cachedColors.clrDiffDeleted;
706 // If no syntax hilighting
707 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
709 crBkgnd = GetColor (COLORINDEX_BKGND);
710 crText = GetColor (COLORINDEX_NORMALTEXT);
711 bDrawWhitespace = false;
714 // Line not inside diff, get colors from CrystalEditor
715 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
716 crText, bDrawWhitespace);
718 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
720 crBkgnd = GetColor (COLORINDEX_WHITESPACE);
721 crText = GetColor (COLORINDEX_WHITESPACE);
722 bDrawWhitespace = false;
728 if (dwLineFlags & LF_WINMERGE_FLAGS)
730 crText = m_cachedColors.clrDiffText;
731 bDrawWhitespace = true;
732 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
734 if (dwLineFlags & LF_SNP)
736 if (lineInCurrentDiff)
738 if (dwLineFlags & LF_GHOST)
739 crBkgnd = m_cachedColors.clrSelSNPDeleted;
741 crBkgnd = m_cachedColors.clrSelSNP;
742 crText = m_cachedColors.clrSelSNPText;
746 if (dwLineFlags & LF_GHOST)
747 crBkgnd = m_cachedColors.clrSNPDeleted;
749 crBkgnd = m_cachedColors.clrSNP;
750 crText = m_cachedColors.clrSNPText;
754 else if (dwLineFlags & LF_DIFF)
756 if (lineInCurrentDiff)
758 if (dwLineFlags & LF_MOVED)
760 if (dwLineFlags & LF_GHOST)
761 crBkgnd = m_cachedColors.clrSelMovedDeleted;
763 crBkgnd = m_cachedColors.clrSelMoved;
764 crText = m_cachedColors.clrSelMovedText;
768 crBkgnd = m_cachedColors.clrSelDiff;
769 crText = m_cachedColors.clrSelDiffText;
775 if (dwLineFlags & LF_MOVED)
777 if (dwLineFlags & LF_GHOST)
778 crBkgnd = m_cachedColors.clrMovedDeleted;
780 crBkgnd = m_cachedColors.clrMoved;
781 crText = m_cachedColors.clrMovedText;
785 crBkgnd = m_cachedColors.clrDiff;
786 crText = m_cachedColors.clrDiffText;
791 else if (dwLineFlags & LF_TRIVIAL)
793 // trivial diff can not be selected
794 if (dwLineFlags & LF_GHOST)
795 // ghost lines in trivial diff has their own color
796 crBkgnd = m_cachedColors.clrTrivialDeleted;
798 crBkgnd = m_cachedColors.clrTrivial;
799 crText = m_cachedColors.clrTrivialText;
802 else if (dwLineFlags & LF_GHOST)
804 if (lineInCurrentDiff)
805 crBkgnd = m_cachedColors.clrSelDiffDeleted;
807 crBkgnd = m_cachedColors.clrDiffDeleted;
813 // Line not inside diff,
814 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
816 // If no syntax hilighting, get windows default colors
817 crBkgnd = GetColor (COLORINDEX_BKGND);
818 crText = GetColor (COLORINDEX_NORMALTEXT);
819 bDrawWhitespace = false;
822 // Syntax highlighting, get colors from CrystalEditor
823 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
824 crText, bDrawWhitespace);
829 * @brief Sync other pane position
831 void CMergeEditView::UpdateSiblingScrollPos (bool bHorz)
833 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
834 if (pSplitterWnd != nullptr)
836 // See CSplitterWnd::IdFromRowCol() implementation for details
837 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
838 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
839 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
840 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
842 // limit the TopLine : must be smaller than GetLineCount for all the panels
843 int newTopSubLine = m_nTopSubLine;
844 int nRows = pSplitterWnd->GetRowCount ();
845 int nCols = pSplitterWnd->GetColumnCount ();
847 // for (nRow = 0; nRow < nRows; nRow++)
849 // for (int nCol = 0; nCol < nCols; nCol++)
851 // CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
852 // if (pSiblingView != nullptr)
853 // if (pSiblingView->GetSubLineCount() <= newTopSubLine)
854 // newTopSubLine = pSiblingView->GetSubLineCount()-1;
857 if (m_nTopSubLine != newTopSubLine)
858 ScrollToSubLine(newTopSubLine);
860 for (nRow = 0; nRow < nRows; nRow++)
862 for (int nCol = 0; nCol < nCols; nCol++)
864 if (!(nRow == nCurrentRow && nCol == nCurrentCol)) // We don't need to update ourselves
866 CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
867 if (pSiblingView != nullptr && pSiblingView->m_nThisGroup == m_nThisGroup)
868 pSiblingView->OnUpdateSibling (this, bHorz);
876 * @brief Update other panes
878 void CMergeEditView::OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
880 if (pUpdateSource != this)
882 ASSERT (pUpdateSource != nullptr);
883 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
884 CMergeEditView *pSrcView = static_cast<CMergeEditView*>(pUpdateSource);
885 if (!bHorz) // changed this so bHorz works right
887 ASSERT (pSrcView->m_nTopSubLine >= 0);
889 // This ASSERT is wrong: panes have different files and
890 // different linecounts
891 // ASSERT (pSrcView->m_nTopLine < GetLineCount ());
892 if (pSrcView->m_nTopSubLine != m_nTopSubLine)
894 ScrollToSubLine (pSrcView->m_nTopSubLine, true, false);
896 RecalcVertScrollBar(true);
897 RecalcHorzScrollBar();
902 ASSERT (pSrcView->m_nOffsetChar >= 0);
904 // This ASSERT is wrong: panes have different files and
905 // different linelengths
906 // ASSERT (pSrcView->m_nOffsetChar < GetMaxLineLength ());
907 if (pSrcView->m_nOffsetChar != m_nOffsetChar)
909 ScrollToChar (pSrcView->m_nOffsetChar, true, false);
911 RecalcHorzScrollBar(true);
912 RecalcHorzScrollBar();
918 void CMergeEditView::OnDisplayDiff(int nDiff /*=0*/)
920 int newlineBegin, newlineEnd;
921 CMergeDoc *pd = GetDocument();
922 if (nDiff < 0 || nDiff >= pd->m_diffList.GetSize())
930 VERIFY(pd->m_diffList.GetDiff(nDiff, curDiff));
932 newlineBegin = curDiff.dbegin;
933 ASSERT (newlineBegin >= 0);
934 newlineEnd = curDiff.dend;
937 m_lineBegin = newlineBegin;
938 m_lineEnd = newlineEnd;
940 int nLineCount = GetLineCount();
941 if (m_lineBegin > nLineCount)
942 m_lineBegin = nLineCount - 1;
943 if (m_lineEnd > nLineCount)
944 m_lineEnd = nLineCount - 1;
946 if (m_nTopLine == newlineBegin)
949 // scroll to the first line of the diff
950 ScrollToLine(m_lineBegin);
952 // update the width of the horizontal scrollbar
953 RecalcHorzScrollBar();
957 * @brief Selects diff by number and syncs other file
958 * @param [in] nDiff Diff to select, must be >= 0
959 * @param [in] bScroll Scroll diff to view
960 * @param [in] bSelectText Select diff text
961 * @sa CMergeEditView::ShowDiff()
962 * @sa CMergeDoc::SetCurrentDiff()
963 * @todo Parameter bSelectText is never used?
965 void CMergeEditView::SelectDiff(int nDiff, bool bScroll /*= true*/, bool bSelectText /*= true*/)
967 CMergeDoc *pd = GetDocument();
969 // Check that nDiff is valid
971 _RPTF1(_CRT_ERROR, "Diffnumber negative (%d)", nDiff);
972 if (nDiff >= pd->m_diffList.GetSize())
973 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d >= %d)",
974 nDiff, pd->m_diffList.GetSize());
977 pd->SetCurrentDiff(nDiff);
978 ShowDiff(bScroll, bSelectText);
979 pd->UpdateAllViews(this);
980 UpdateSiblingScrollPos(false);
982 // notify either side, as it will notify the other one
983 pd->ForEachView ([&](auto& pView) { if (pView->m_bDetailView) pView->OnDisplayDiff(nDiff); });
986 void CMergeEditView::DeselectDiffIfCursorNotInCurrentDiff()
988 CMergeDoc *pd = GetDocument();
989 // If we have a selected diff, deselect it
990 int nCurrentDiff = pd->GetCurrentDiff();
991 if (nCurrentDiff != -1)
993 CPoint pos = GetCursorPos();
994 if (!IsLineInCurrentDiff(pos.y))
996 pd->SetCurrentDiff(-1);
998 pd->UpdateAllViews(this);
1004 * @brief Called when user selects "Current Difference".
1005 * Goes to active diff. If no active diff, selects diff under cursor
1006 * @sa CMergeEditView::SelectDiff()
1007 * @sa CMergeDoc::GetCurrentDiff()
1008 * @sa CMergeDoc::LineToDiff()
1010 void CMergeEditView::OnCurdiff()
1012 CMergeDoc *pd = GetDocument();
1014 // If no diffs, nothing to select
1015 if (!pd->m_diffList.HasSignificantDiffs())
1018 // GetCurrentDiff() returns -1 if no diff selected
1019 int nDiff = pd->GetCurrentDiff();
1022 // Scroll to the first line of the currently selected diff
1023 SelectDiff(nDiff, true, false);
1027 // If cursor is inside diff, select that diff
1028 CPoint pos = GetCursorPos();
1029 nDiff = pd->m_diffList.LineToDiff(pos.y);
1030 if (nDiff != -1 && pd->m_diffList.IsDiffSignificant(nDiff))
1031 SelectDiff(nDiff, true, false);
1036 * @brief Called when "Current diff" item is updated
1038 void CMergeEditView::OnUpdateCurdiff(CCmdUI* pCmdUI)
1040 CMergeDoc *pd = GetDocument();
1041 CPoint pos = GetCursorPos();
1042 int nCurrentDiff = pd->GetCurrentDiff();
1043 if (nCurrentDiff == -1)
1045 int nNewDiff = pd->m_diffList.LineToDiff(pos.y);
1046 pCmdUI->Enable(nNewDiff != -1 && pd->m_diffList.IsDiffSignificant(nNewDiff));
1049 pCmdUI->Enable(true);
1053 * @brief Copy selected text to clipboard
1055 void CMergeEditView::OnEditCopy()
1057 CMergeDoc * pDoc = GetDocument();
1058 CPoint ptSelStart, ptSelEnd;
1059 GetSelection(ptSelStart, ptSelEnd);
1062 if (ptSelStart == ptSelEnd)
1067 if (!m_bColumnSelection)
1069 CDiffTextBuffer * buffer = pDoc->m_ptBuf[m_nThisPane].get();
1071 buffer->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1072 ptSelEnd.y, ptSelEnd.x, text);
1075 GetTextWithoutEmptysInColumnSelection(text);
1077 PutToClipboard(text, text.GetLength(), m_bColumnSelection);
1081 * @brief Called when "Copy" item is updated
1083 void CMergeEditView::OnUpdateEditCopy(CCmdUI* pCmdUI)
1085 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
1089 * @brief Cut current selection to clipboard
1091 void CMergeEditView::OnEditCut()
1093 if (!QueryEditable())
1096 CPoint ptSelStart, ptSelEnd;
1097 CMergeDoc * pDoc = GetDocument();
1098 GetSelection(ptSelStart, ptSelEnd);
1101 if (ptSelStart == ptSelEnd)
1105 if (!m_bColumnSelection)
1106 pDoc->m_ptBuf[m_nThisPane]->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1107 ptSelEnd.y, ptSelEnd.x, text);
1109 GetTextWithoutEmptysInColumnSelection(text);
1111 PutToClipboard(text, text.GetLength(), m_bColumnSelection);
1113 if (!m_bColumnSelection)
1115 CPoint ptCursorPos = ptSelStart;
1116 ASSERT_VALIDTEXTPOS(ptCursorPos);
1117 SetAnchor(ptCursorPos);
1118 SetSelection(ptCursorPos, ptCursorPos);
1119 SetCursorPos(ptCursorPos);
1120 EnsureVisible(ptCursorPos);
1122 pDoc->m_ptBuf[m_nThisPane]->DeleteText(this, ptSelStart.y, ptSelStart.x, ptSelEnd.y,
1123 ptSelEnd.x, CE_ACTION_CUT);
1126 DeleteCurrentColumnSelection (CE_ACTION_CUT);
1128 m_pTextBuffer->SetModified(true);
1132 * @brief Called when "Cut" item is updated
1134 void CMergeEditView::OnUpdateEditCut(CCmdUI* pCmdUI)
1136 if (QueryEditable())
1137 CCrystalEditViewEx::OnUpdateEditCut(pCmdUI);
1139 pCmdUI->Enable(false);
1143 * @brief Paste text from clipboard
1145 void CMergeEditView::OnEditPaste()
1147 if (!QueryEditable())
1150 CCrystalEditViewEx::Paste();
1151 m_pTextBuffer->SetModified(true);
1155 * @brief Called when "Paste" item is updated
1157 void CMergeEditView::OnUpdateEditPaste(CCmdUI* pCmdUI)
1159 if (QueryEditable())
1160 CCrystalEditViewEx::OnUpdateEditPaste(pCmdUI);
1162 pCmdUI->Enable(false);
1166 * @brief Undo last action
1168 void CMergeEditView::OnEditUndo()
1170 CWaitCursor waitstatus;
1171 CMergeDoc* pDoc = GetDocument();
1172 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1175 if (!QueryEditable())
1178 GetParentFrame()->SetActiveView(this, true);
1179 if(CCrystalEditViewEx::DoEditUndo())
1182 pDoc->UpdateHeaderPath(m_nThisPane);
1183 pDoc->FlushAndRescan();
1186 m_pTextBuffer->GetRedoActionCode(nAction);
1187 if (nAction == CE_ACTION_MERGE)
1188 // select the diff so we may just merge it again
1194 tgt->SendMessage(WM_COMMAND, ID_EDIT_UNDO);
1196 if (!pDoc->CanUndo())
1197 pDoc->SetAutoMerged(false);
1201 * @brief Called when "Undo" item is updated
1203 void CMergeEditView::OnUpdateEditUndo(CCmdUI* pCmdUI)
1205 CMergeDoc* pDoc = GetDocument();
1206 if (pDoc->curUndo!=pDoc->undoTgt.begin())
1208 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1209 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
1212 pCmdUI->Enable(false);
1216 * @brief Go to first diff
1218 * Called when user selects "First Difference"
1219 * @sa CMergeEditView::SelectDiff()
1221 void CMergeEditView::OnFirstdiff()
1223 CMergeDoc *pd = GetDocument();
1224 if (pd->m_diffList.HasSignificantDiffs())
1226 int nDiff = pd->m_diffList.FirstSignificantDiff();
1227 SelectDiff(nDiff, true, false);
1232 * @brief Update "First diff" UI items
1234 void CMergeEditView::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1236 OnUpdatePrevdiff(pCmdUI);
1240 * @brief Go to last diff
1242 void CMergeEditView::OnLastdiff()
1244 CMergeDoc *pd = GetDocument();
1245 if (pd->m_diffList.HasSignificantDiffs())
1247 int nDiff = pd->m_diffList.LastSignificantDiff();
1248 SelectDiff(nDiff, true, false);
1253 * @brief Update "Last diff" UI items
1255 void CMergeEditView::OnUpdateLastdiff(CCmdUI* pCmdUI)
1257 OnUpdateNextdiff(pCmdUI);
1261 * @brief Go to next diff and select it.
1263 * Finds and selects next difference. There are several cases:
1264 * - if there is selected difference, and that difference is visible
1265 * on screen, next found difference is selected.
1266 * - if there is selected difference but it is not visible, next
1267 * difference from cursor position is selected. This is what user
1268 * expects to happen and is natural thing to do. Also reduces
1269 * needless scrolling.
1270 * - if there is no selected difference, next difference from cursor
1271 * position is selected.
1273 void CMergeEditView::OnNextdiff()
1275 CMergeDoc *pd = GetDocument();
1276 int cnt = pd->m_ptBuf[0]->GetLineCount();
1280 // Returns -1 if no diff selected
1282 int curDiff = pd->GetCurrentDiff();
1286 if (!IsDiffVisible(curDiff))
1288 // Selected difference not visible, select next from cursor
1289 int line = GetCursorPos().y;
1290 // Make sure we aren't in the first line of the diff
1292 if (!IsValidTextPosY(CPoint(0, line)))
1294 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1298 // Find out if there is a following significant diff
1299 if (curDiff < pd->m_diffList.GetSize() - 1)
1301 nextDiff = pd->m_diffList.NextSignificantDiff(curDiff);
1307 // We don't have a selected difference,
1308 // but cursor can be inside inactive diff
1309 int line = GetCursorPos().y;
1310 if (!IsValidTextPosY(CPoint(0, line)))
1312 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1315 int lastDiff = pd->m_diffList.LastSignificantDiff();
1316 if (nextDiff >= 0 && nextDiff <= lastDiff)
1317 SelectDiff(nextDiff, true, false);
1318 else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1320 if (pDirDoc->MoveableToNextDiff())
1321 pDirDoc->MoveToNextDiff(pd);
1326 * @brief Update "Next diff" UI items
1328 void CMergeEditView::OnUpdateNextdiff(CCmdUI* pCmdUI)
1330 CMergeDoc *pd = GetDocument();
1331 const DIFFRANGE * dfi = pd->m_diffList.LastSignificantDiffRange();
1336 // There aren't any significant differences
1341 // Enable if the beginning of the last significant difference is after caret
1342 enabled = (GetCursorPos().y < (long)dfi->dbegin);
1345 if (!enabled && pd->GetDirDoc())
1346 enabled = pd->GetDirDoc()->MoveableToNextDiff();
1348 pCmdUI->Enable(enabled);
1352 * @brief Go to previous diff and select it.
1354 * Finds and selects previous difference. There are several cases:
1355 * - if there is selected difference, and that difference is visible
1356 * on screen, previous found difference is selected.
1357 * - if there is selected difference but it is not visible, previous
1358 * difference from cursor position is selected. This is what user
1359 * expects to happen and is natural thing to do. Also reduces
1360 * needless scrolling.
1361 * - if there is no selected difference, previous difference from cursor
1362 * position is selected.
1364 void CMergeEditView::OnPrevdiff()
1366 CMergeDoc *pd = GetDocument();
1367 int cnt = pd->m_ptBuf[0]->GetLineCount();
1371 // GetCurrentDiff() returns -1 if no diff selected
1373 int curDiff = pd->GetCurrentDiff();
1377 if (!IsDiffVisible(curDiff))
1379 // Selected difference not visible, select previous from cursor
1380 int line = GetCursorPos().y;
1381 // Make sure we aren't in the last line of the diff
1383 if (!IsValidTextPosY(CPoint(0, line)))
1385 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1389 // Find out if there is a preceding significant diff
1392 prevDiff = pd->m_diffList.PrevSignificantDiff(curDiff);
1398 // We don't have a selected difference,
1399 // but cursor can be inside inactive diff
1400 int line = GetCursorPos().y;
1401 if (!IsValidTextPosY(CPoint(0, line)))
1403 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1406 int firstDiff = pd->m_diffList.FirstSignificantDiff();
1407 if (prevDiff >= 0 && prevDiff >= firstDiff)
1408 SelectDiff(prevDiff, true, false);
1409 else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1411 if (pDirDoc->MoveableToPrevDiff())
1412 pDirDoc->MoveToPrevDiff(pd);
1417 * @brief Update "Previous diff" UI items
1419 void CMergeEditView::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1421 CMergeDoc *pd = GetDocument();
1422 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificantDiffRange();
1427 // There aren't any significant differences
1432 // Enable if the end of the first significant difference is before caret
1433 enabled = (GetCursorPos().y > (long)dfi->dend);
1436 if (!enabled && pd->GetDirDoc())
1437 enabled = pd->GetDirDoc()->MoveableToPrevDiff();
1439 pCmdUI->Enable(enabled);
1442 void CMergeEditView::OnNextConflict()
1444 OnNext3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1448 * @brief Update "Next Conflict" UI items
1450 void CMergeEditView::OnUpdateNextConflict(CCmdUI* pCmdUI)
1452 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1455 void CMergeEditView::OnPrevConflict()
1457 OnPrev3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1461 * @brief Update "Prev Conflict" UI items
1463 void CMergeEditView::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1465 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1469 * @brief Go to next 3-way diff and select it.
1471 void CMergeEditView::OnNext3wayDiff(int nDiffType)
1473 CMergeDoc *pd = GetDocument();
1474 int cnt = pd->m_ptBuf[0]->GetLineCount();
1478 // Returns -1 if no diff selected
1479 int curDiff = pd->GetCurrentDiff();
1483 int nextDiff = curDiff;
1484 if (!IsDiffVisible(curDiff))
1486 // Selected difference not visible, select next from cursor
1487 int line = GetCursorPos().y;
1488 // Make sure we aren't in the first line of the diff
1490 if (!IsValidTextPosY(CPoint(0, line)))
1492 nextDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1496 // Find out if there is a following significant diff
1497 if (curDiff < pd->m_diffList.GetSize() - 1)
1499 nextDiff = pd->m_diffList.NextSignificant3wayDiff(curDiff, nDiffType);
1505 // nextDiff is the next one if there is one, else it is the one we're on
1506 SelectDiff(nextDiff, true, false);
1510 // We don't have a selected difference,
1511 // but cursor can be inside inactive diff
1512 int line = GetCursorPos().y;
1513 if (!IsValidTextPosY(CPoint(0, line)))
1515 curDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1517 SelectDiff(curDiff, true, false);
1522 * @brief Update "Next 3-way diff" UI items
1524 void CMergeEditView::OnUpdateNext3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1526 CMergeDoc *pd = GetDocument();
1528 if (pd->m_nBuffers < 3)
1530 pCmdUI->Enable(false);
1534 const DIFFRANGE * dfi = pd->m_diffList.LastSignificant3wayDiffRange(nDiffType);
1538 // There aren't any significant differences
1539 pCmdUI->Enable(false);
1543 // Enable if the beginning of the last significant difference is after caret
1544 CPoint pos = GetCursorPos();
1545 pCmdUI->Enable(pos.y < (long)dfi->dbegin);
1550 * @brief Go to previous 3-way diff and select it.
1552 void CMergeEditView::OnPrev3wayDiff(int nDiffType)
1554 CMergeDoc *pd = GetDocument();
1556 int cnt = pd->m_ptBuf[0]->GetLineCount();
1560 // GetCurrentDiff() returns -1 if no diff selected
1561 int curDiff = pd->GetCurrentDiff();
1565 int prevDiff = curDiff;
1566 if (!IsDiffVisible(curDiff))
1568 // Selected difference not visible, select previous from cursor
1569 int line = GetCursorPos().y;
1570 // Make sure we aren't in the last line of the diff
1572 if (!IsValidTextPosY(CPoint(0, line)))
1574 prevDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1578 // Find out if there is a preceding significant diff
1581 prevDiff = pd->m_diffList.PrevSignificant3wayDiff(curDiff, nDiffType);
1587 // prevDiff is the preceding one if there is one, else it is the one we're on
1588 SelectDiff(prevDiff, true, false);
1592 // We don't have a selected difference,
1593 // but cursor can be inside inactive diff
1594 int line = GetCursorPos().y;
1595 if (!IsValidTextPosY(CPoint(0, line)))
1597 curDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1599 SelectDiff(curDiff, true, false);
1604 * @brief Update "Previous diff X and Y" UI items
1606 void CMergeEditView::OnUpdatePrev3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1608 CMergeDoc *pd = GetDocument();
1610 if (pd->m_nBuffers < 3)
1612 pCmdUI->Enable(false);
1616 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificant3wayDiffRange(nDiffType);
1620 // There aren't any significant differences
1621 pCmdUI->Enable(false);
1625 // Enable if the end of the first significant difference is before caret
1626 CPoint pos = GetCursorPos();
1627 pCmdUI->Enable(pos.y > (long)dfi->dend);
1631 void CMergeEditView::OnNextdiffLM()
1633 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1636 void CMergeEditView::OnUpdateNextdiffLM(CCmdUI* pCmdUI)
1638 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1641 void CMergeEditView::OnNextdiffLR()
1643 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1646 void CMergeEditView::OnUpdateNextdiffLR(CCmdUI* pCmdUI)
1648 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1651 void CMergeEditView::OnNextdiffMR()
1653 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1656 void CMergeEditView::OnUpdateNextdiffMR(CCmdUI* pCmdUI)
1658 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1661 void CMergeEditView::OnNextdiffLO()
1663 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1666 void CMergeEditView::OnUpdateNextdiffLO(CCmdUI* pCmdUI)
1668 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1671 void CMergeEditView::OnNextdiffMO()
1673 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1676 void CMergeEditView::OnUpdateNextdiffMO(CCmdUI* pCmdUI)
1678 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1681 void CMergeEditView::OnNextdiffRO()
1683 OnNext3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1686 void CMergeEditView::OnUpdateNextdiffRO(CCmdUI* pCmdUI)
1688 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1691 void CMergeEditView::OnPrevdiffLM()
1693 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1696 void CMergeEditView::OnUpdatePrevdiffLM(CCmdUI* pCmdUI)
1698 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1701 void CMergeEditView::OnPrevdiffLR()
1703 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1706 void CMergeEditView::OnUpdatePrevdiffLR(CCmdUI* pCmdUI)
1708 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1711 void CMergeEditView::OnPrevdiffMR()
1713 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1716 void CMergeEditView::OnUpdatePrevdiffMR(CCmdUI* pCmdUI)
1718 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1721 void CMergeEditView::OnPrevdiffLO()
1723 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1726 void CMergeEditView::OnUpdatePrevdiffLO(CCmdUI* pCmdUI)
1728 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1731 void CMergeEditView::OnPrevdiffMO()
1733 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1736 void CMergeEditView::OnUpdatePrevdiffMO(CCmdUI* pCmdUI)
1738 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1741 void CMergeEditView::OnPrevdiffRO()
1743 OnPrev3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1746 void CMergeEditView::OnUpdatePrevdiffRO(CCmdUI* pCmdUI)
1748 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1752 * @brief Clear selection
1754 void CMergeEditView::SelectNone()
1756 SetSelection (GetCursorPos(), GetCursorPos());
1761 * @brief Check if line is inside currently selected diff
1762 * @param [in] nLine 0-based linenumber in view
1763 * @sa CMergeDoc::GetCurrentDiff()
1764 * @sa CMergeDoc::LineInDiff()
1766 bool CMergeEditView::IsLineInCurrentDiff(int nLine) const
1768 // Check validity of nLine
1771 _RPTF1(_CRT_ERROR, "Linenumber is negative (%d)!", nLine);
1772 int nLineCount = LocateTextBuffer()->GetLineCount();
1773 if (nLine >= nLineCount)
1774 _RPTF2(_CRT_ERROR, "Linenumber > linecount (%d>%d)!", nLine, nLineCount);
1777 const CMergeDoc *pd = GetDocument();
1778 int curDiff = pd->GetCurrentDiff();
1781 return pd->m_diffList.LineInDiff(nLine, curDiff);
1785 * @brief Called when mouse left-button double-clicked
1787 * Double-clicking mouse inside diff selects that diff
1789 void CMergeEditView::OnLButtonDblClk(UINT nFlags, CPoint point)
1791 CMergeDoc *pd = GetDocument();
1792 CPoint pos = GetCursorPos();
1794 int diff = pd->m_diffList.LineToDiff(pos.y);
1795 if (diff != -1 && pd->m_diffList.IsDiffSignificant(diff))
1796 SelectDiff(diff, false, false);
1798 CCrystalEditViewEx::OnLButtonDblClk(nFlags, point);
1802 * @brief Called when mouse left button is released.
1804 * If button is released outside diffs, current diff
1807 void CMergeEditView::OnLButtonUp(UINT nFlags, CPoint point)
1809 CCrystalEditViewEx::OnLButtonUp(nFlags, point);
1810 DeselectDiffIfCursorNotInCurrentDiff();
1814 * @brief Called when mouse right button is pressed.
1816 * If right button is pressed outside diffs, current diff
1819 void CMergeEditView::OnRButtonDown(UINT nFlags, CPoint point)
1821 CCrystalEditViewEx::OnRButtonDown(nFlags, point);
1822 DeselectDiffIfCursorNotInCurrentDiff();
1825 void CMergeEditView::OnX2Y(int srcPane, int dstPane)
1827 // Check that right side is not readonly
1828 if (IsReadOnly(dstPane))
1831 CMergeDoc *pDoc = GetDocument();
1832 int currentDiff = pDoc->GetCurrentDiff();
1834 if (currentDiff == -1)
1837 // If cursor is inside diff get number of that diff
1838 if (m_bCurrentLineIsDiff)
1840 CPoint pt = GetCursorPos();
1841 currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1847 if (!m_bColumnSelection)
1849 int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
1850 GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1851 if (firstDiff != -1 && lastDiff != -1)
1853 CWaitCursor waitstatus;
1854 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1859 CWaitCursor waitstatus;
1860 auto wordDiffs = GetColumnSelectedWordDiffIndice();
1862 std::for_each(wordDiffs.rbegin(), wordDiffs.rend(), [&](auto& it) {
1863 pDoc->WordListCopy(srcPane, dstPane, it.first, it.second[0], it.second[it.second.size() - 1], &it.second, i != 0, i == 0);
1868 else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1870 CWaitCursor waitstatus;
1871 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1875 void CMergeEditView::OnUpdateX2Y(int dstPane, CCmdUI* pCmdUI)
1877 // Check that right side is not readonly
1878 if (!IsReadOnly(dstPane))
1880 // If one or more diffs inside selection OR
1881 // there is an active diff OR
1882 // cursor is inside diff
1885 if (m_bCurrentLineIsDiff || (m_pTextBuffer->GetLineFlags(m_ptSelStart.y) & LF_NONTRIVIAL_DIFF) != 0)
1887 pCmdUI->Enable(true);
1891 int firstDiff, lastDiff;
1892 GetFullySelectedDiffs(firstDiff, lastDiff);
1894 pCmdUI->Enable(firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff));
1899 const int currDiff = GetDocument()->GetCurrentDiff();
1900 pCmdUI->Enable(m_bCurrentLineIsDiff || (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff)));
1904 pCmdUI->Enable(false);
1908 * @brief Copy diff from left pane to right pane
1910 * Difference is copied from left to right when
1911 * - difference is selected
1912 * - difference is inside selection (allows merging multiple differences).
1913 * - cursor is inside diff
1915 * If there is selected diff outside selection, we copy selected
1918 void CMergeEditView::OnL2r()
1920 int dstPane = (m_nThisPane < GetDocument()->m_nBuffers - 1) ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1921 int srcPane = dstPane - 1;
1922 OnX2Y(srcPane, dstPane);
1926 * @brief Called when "Copy to left" item is updated
1928 void CMergeEditView::OnUpdateL2r(CCmdUI* pCmdUI)
1930 OnUpdateX2Y(m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1, pCmdUI);
1934 * @brief Copy diff from right pane to left pane
1936 * Difference is copied from left to right when
1937 * - difference is selected
1938 * - difference is inside selection (allows merging multiple differences).
1939 * - cursor is inside diff
1941 * If there is selected diff outside selection, we copy selected
1944 void CMergeEditView::OnR2l()
1946 int dstPane = (m_nThisPane > 0) ? m_nThisPane - 1 : 0;
1947 int srcPane = dstPane + 1;
1948 OnX2Y(srcPane, dstPane);
1952 * @brief Called when "Copy to right" item is updated
1954 void CMergeEditView::OnUpdateR2l(CCmdUI* pCmdUI)
1956 OnUpdateX2Y(m_nThisPane > 0 ? m_nThisPane - 1 : 0, pCmdUI);
1959 void CMergeEditView::OnCopyFromLeft()
1961 int dstPane = m_nThisPane;
1962 int srcPane = dstPane - 1;
1965 OnX2Y(srcPane, dstPane);
1968 void CMergeEditView::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1970 int dstPane = m_nThisPane;
1971 int srcPane = dstPane - 1;
1973 pCmdUI->Enable(false);
1975 OnUpdateX2Y(dstPane, pCmdUI);
1978 void CMergeEditView::OnCopyFromRight()
1980 int dstPane = m_nThisPane;
1981 int srcPane = dstPane + 1;
1982 if (srcPane >= GetDocument()->m_nBuffers)
1984 OnX2Y(srcPane, dstPane);
1987 void CMergeEditView::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
1989 int dstPane = m_nThisPane;
1990 int srcPane = dstPane + 1;
1991 if (srcPane >= GetDocument()->m_nBuffers)
1992 pCmdUI->Enable(false);
1994 OnUpdateX2Y(dstPane, pCmdUI);
1998 * @brief Copy all diffs from right pane to left pane
2000 void CMergeEditView::OnAllLeft()
2002 // Check that left side is not readonly
2003 int srcPane = m_nThisPane > 0 ? m_nThisPane : 1;
2004 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
2005 if (IsReadOnly(dstPane))
2007 CWaitCursor waitstatus;
2009 GetDocument()->CopyAllList(srcPane, dstPane);
2013 * @brief Called when "Copy all to left" item is updated
2015 void CMergeEditView::OnUpdateAllLeft(CCmdUI* pCmdUI)
2017 // Check that left side is not readonly
2018 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
2019 if (!IsReadOnly(dstPane))
2020 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
2022 pCmdUI->Enable(false);
2026 * @brief Copy all diffs from left pane to right pane
2028 void CMergeEditView::OnAllRight()
2030 // Check that right side is not readonly
2031 int srcPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane : m_nThisPane - 1;
2032 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
2033 if (IsReadOnly(dstPane))
2036 CWaitCursor waitstatus;
2038 GetDocument()->CopyAllList(srcPane, dstPane);
2042 * @brief Called when "Copy all to right" item is updated
2044 void CMergeEditView::OnUpdateAllRight(CCmdUI* pCmdUI)
2046 // Check that right side is not readonly
2047 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
2048 if (!IsReadOnly(dstPane))
2049 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
2051 pCmdUI->Enable(false);
2055 * @brief Do Auto merge
2057 void CMergeEditView::OnAutoMerge()
2059 // Check current pane is not readonly
2060 if (GetDocument()->IsModified() || GetDocument()->GetAutoMerged() || !QueryEditable())
2063 CWaitCursor waitstatus;
2065 GetDocument()->DoAutoMerge(m_nThisPane);
2069 * @brief Called when "Auto Merge" item is updated
2071 void CMergeEditView::OnUpdateAutoMerge(CCmdUI* pCmdUI)
2073 pCmdUI->Enable(GetDocument()->m_nBuffers == 3 &&
2074 !GetDocument()->IsModified() &&
2075 !GetDocument()->GetAutoMerged() &&
2080 * @brief Add synchronization point
2082 void CMergeEditView::OnAddSyncPoint()
2084 GetDocument()->AddSyncPoint();
2088 * @brief Clear synchronization points
2090 void CMergeEditView::OnClearSyncPoints()
2092 GetDocument()->ClearSyncPoints();
2096 * @brief Called when "Clear Synchronization Points" item is updated
2098 void CMergeEditView::OnUpdateClearSyncPoints(CCmdUI* pCmdUI)
2100 pCmdUI->Enable(GetDocument()->HasSyncPoints());
2104 * @brief This function is called before other edit events.
2105 * @param [in] nAction Edit operation to do
2106 * @param [in] pszText Text to insert, delete etc
2107 * @sa CCrystalEditView::OnEditOperation()
2108 * @todo More edit-events for rescan delaying?
2110 void CMergeEditView::OnEditOperation(int nAction, LPCTSTR pszText, size_t cchText)
2112 if (!QueryEditable())
2114 // We must not arrive here, and assert helps detect troubles
2119 CMergeDoc* pDoc = GetDocument();
2120 pDoc->SetEditedAfterRescan(m_nThisPane);
2122 // simple hook for multiplex undo operations
2123 // deleted by jtuc 2003-06-28
2124 // now AddUndoRecords does it (so we don't create entry for OnEditOperation with no Undo data in m_pTextBuffer)
2125 /*if(dynamic_cast<CMergeDoc::CDiffTextBuffer*>(m_pTextBuffer)->curUndoGroup())
2127 pDoc->undoTgt.erase(pDoc->curUndo, pDoc->undoTgt.end());
2128 pDoc->undoTgt.push_back(this);
2129 pDoc->curUndo = pDoc->undoTgt.end();
2132 // perform original function
2133 CCrystalEditViewEx::OnEditOperation(nAction, pszText, cchText);
2135 // augment with additional operations
2137 // Change header to inform about changed doc
2138 pDoc->UpdateHeaderPath(m_nThisPane);
2140 // If automatic rescan enabled, rescan after edit events
2141 if (m_bAutomaticRescan)
2143 // keep document up to date
2144 // (Re)start timer to rescan only when user edits text
2145 // If timer starting fails, rescan immediately
2146 if (nAction == CE_ACTION_TYPING ||
2147 nAction == CE_ACTION_REPLACE ||
2148 nAction == CE_ACTION_BACKSPACE ||
2149 nAction == CE_ACTION_INDENT ||
2150 nAction == CE_ACTION_PASTE ||
2151 nAction == CE_ACTION_DELSEL ||
2152 nAction == CE_ACTION_DELETE ||
2153 nAction == CE_ACTION_CUT)
2155 if (!SetTimer(IDT_RESCAN, RESCAN_TIMEOUT, nullptr))
2156 pDoc->FlushAndRescan();
2159 pDoc->FlushAndRescan();
2165 // Update other pane for sync line.
2166 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
2168 if (nPane == m_nThisPane)
2170 CCrystalEditView *pView = GetGroupView(nPane);
2171 if (pView != nullptr)
2172 pView->Invalidate();
2179 * @brief Redo last action
2181 void CMergeEditView::OnEditRedo()
2183 CWaitCursor waitstatus;
2184 CMergeDoc* pDoc = GetDocument();
2185 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2188 if (!QueryEditable())
2191 GetParentFrame()->SetActiveView(this, true);
2192 if(CCrystalEditViewEx::DoEditRedo())
2195 pDoc->UpdateHeaderPath(m_nThisPane);
2196 pDoc->FlushAndRescan();
2201 tgt->SendMessage(WM_COMMAND, ID_EDIT_REDO);
2206 * @brief Called when "Redo" item is updated
2208 void CMergeEditView::OnUpdateEditRedo(CCmdUI* pCmdUI)
2210 CMergeDoc* pDoc = GetDocument();
2211 if (pDoc->curUndo!=pDoc->undoTgt.end())
2213 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2214 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
2217 pCmdUI->Enable(false);
2220 void CMergeEditView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
2222 CCrystalEditViewEx::OnUpdate(pSender, lHint, pHint);
2226 * @brief Scrolls to current diff and/or selects diff text
2227 * @param [in] bScroll If true scroll diff to view
2228 * @param [in] bSelectText If true select diff text
2229 * @note If bScroll and bSelectText are false, this does nothing!
2230 * @todo This shouldn't be called when no diff is selected, so
2231 * somebody could try to ASSERT(nDiff > -1)...
2233 void CMergeEditView::ShowDiff(bool bScroll, bool bSelectText)
2235 CMergeDoc *pd = GetDocument();
2236 const int nDiff = pd->GetCurrentDiff();
2238 // Try to trap some errors
2239 if (nDiff >= pd->m_diffList.GetSize())
2240 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d > %d)!",
2241 nDiff, pd->m_diffList.GetSize());
2243 if (nDiff >= 0 && nDiff < pd->m_diffList.GetSize())
2245 CPoint ptStart, ptEnd;
2247 pd->m_diffList.GetDiff(nDiff, curDiff);
2250 ptStart.y = curDiff.dbegin;
2252 ptEnd.y = curDiff.dend;
2254 if (bScroll && !m_bDetailView)
2256 if (!IsDiffVisible(curDiff, CONTEXT_LINES_BELOW))
2258 // Difference is not visible, scroll it so that max amount of
2259 // scrolling is done while keeping the diff in screen. So if
2260 // scrolling is downwards, scroll the diff to as up in screen
2261 // as possible. This usually brings next diff to the screen
2262 // and we don't need to scroll into it.
2263 int nLine = GetSubLineIndex(ptStart.y);
2264 if (nLine > CONTEXT_LINES_ABOVE)
2266 nLine -= CONTEXT_LINES_ABOVE;
2268 GetGroupView(m_nThisPane)->ScrollToSubLine(nLine);
2269 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2271 if (nPane != m_nThisPane)
2272 GetGroupView(nPane)->ScrollToSubLine(nLine);
2275 GetGroupView(m_nThisPane)->SetCursorPos(ptStart);
2276 GetGroupView(m_nThisPane)->SetAnchor(ptStart);
2277 GetGroupView(m_nThisPane)->SetSelection(ptStart, ptStart);
2278 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2280 if (nPane != m_nThisPane)
2282 GetGroupView(nPane)->SetCursorPos(ptStart);
2283 GetGroupView(nPane)->SetAnchor(ptStart);
2284 GetGroupView(nPane)->SetSelection(ptStart, ptStart);
2291 ptEnd.x = GetLineLength(ptEnd.y);
2292 SetSelection(ptStart, ptEnd);
2301 void CMergeEditView::OnTimer(UINT_PTR nIDEvent)
2303 // Maybe we want theApp::OnIdle to proceed before processing a timer message
2304 // ...but for this the queue must be empty
2305 // The timer message is a low priority message but the queue is maybe not yet empty
2306 // So we set a flag, wait for OnIdle to proceed, then come back here...
2307 // We come back here with a IDLE_TIMER OnTimer message (send with SendMessage
2308 // not with SetTimer so there is no delay)
2310 // IDT_RESCAN was posted because the app wanted to do a flushAndRescan with some delay
2312 // IDLE_TIMER is the false timer used to come back here after OnIdle
2313 // fTimerWaitingForIdle is a bool to store the commands waiting for idle
2314 // (one normal timer = one flag = one command)
2316 if (nIDEvent == IDT_RESCAN)
2318 KillTimer(IDT_RESCAN);
2319 fTimerWaitingForIdle |= FLAG_RESCAN_WAITS_FOR_IDLE;
2320 // notify the app to come back after OnIdle
2321 theApp.SetNeedIdleTimer();
2324 if (nIDEvent == IDLE_TIMER)
2326 // not a real timer, just come back after OnIdle
2327 // look to flags to know what to do
2328 if (fTimerWaitingForIdle & FLAG_RESCAN_WAITS_FOR_IDLE)
2329 GetDocument()->RescanIfNeeded(RESCAN_TIMEOUT/1000);
2330 fTimerWaitingForIdle = 0;
2333 CCrystalEditViewEx::OnTimer(nIDEvent);
2337 * @brief Returns if buffer is read-only
2338 * @note This has no any relation to file being read-only!
2340 bool CMergeEditView::IsReadOnly(int pane) const
2342 return m_bDetailView ? true : (GetDocument()->m_ptBuf[pane]->GetReadOnly() != false);
2346 * @brief Called when "Save left (as...)" item is updated
2348 void CMergeEditView::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
2350 CMergeDoc *pd = GetDocument();
2351 pCmdUI->Enable(!IsReadOnly(0) && pd->m_ptBuf[0]->IsModified());
2355 * @brief Called when "Save middle (as...)" item is updated
2357 void CMergeEditView::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
2359 CMergeDoc *pd = GetDocument();
2360 pCmdUI->Enable(pd->m_nBuffers == 3 && !IsReadOnly(1) && pd->m_ptBuf[1]->IsModified());
2364 * @brief Called when "Save right (as...)" item is updated
2366 void CMergeEditView::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
2368 CMergeDoc *pd = GetDocument();
2369 pCmdUI->Enable(!IsReadOnly(pd->m_nBuffers - 1) && pd->m_ptBuf[pd->m_nBuffers - 1]->IsModified());
2373 * @brief Refresh display using text-buffers
2374 * @note This DOES NOT reload files!
2376 void CMergeEditView::OnRefresh()
2378 CMergeDoc *pd = GetDocument();
2379 ASSERT(pd != nullptr);
2380 pd->FlushAndRescan(true);
2384 * @brief Handle some keys when in merging mode
2386 bool CMergeEditView::MergeModeKeyDown(MSG* pMsg)
2388 bool bHandled = false;
2390 // Allow default text selection when SHIFT pressed
2391 if (::GetAsyncKeyState(VK_SHIFT))
2394 // Allow default editor functions when CTRL pressed
2395 if (::GetAsyncKeyState(VK_CONTROL))
2398 // If we are in merging mode (merge with cursor keys)
2399 // handle some keys here
2400 switch (pMsg->wParam)
2427 * @brief Called before messages are translated.
2429 * Checks if ESC key was pressed, saves and closes doc.
2430 * Also if in merge mode traps cursor keys.
2432 BOOL CMergeEditView::PreTranslateMessage(MSG* pMsg)
2434 if (pMsg->message == WM_KEYDOWN)
2436 // If we are in merging mode (merge with cursor keys)
2437 // handle some keys here
2438 if (theApp.GetMergingMode())
2440 bool bHandled = MergeModeKeyDown(pMsg);
2445 // Close window if user has allowed it from options
2446 if (pMsg->wParam == VK_ESCAPE)
2448 int nCloseWithEsc = GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC);
2449 if (nCloseWithEsc != 0)
2450 GetParentFrame()->PostMessage(WM_CLOSE, 0, 0);
2455 return CCrystalEditViewEx::PreTranslateMessage(pMsg);
2459 * @brief Called when "Save" item is updated
2461 void CMergeEditView::OnUpdateFileSave(CCmdUI* pCmdUI)
2463 CMergeDoc *pd = GetDocument();
2465 bool bModified = false;
2466 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2468 if (pd->m_ptBuf[nPane]->IsModified())
2471 pCmdUI->Enable(bModified);
2475 * @brief Enable/disable left buffer read-only
2477 void CMergeEditView::OnLeftReadOnly()
2479 CMergeDoc *pd = GetDocument();
2480 bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2481 pd->m_ptBuf[0]->SetReadOnly(!bReadOnly);
2485 * @brief Called when "Left read-only" item is updated
2487 void CMergeEditView::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
2489 CMergeDoc *pd = GetDocument();
2490 bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2491 pCmdUI->Enable(true);
2492 pCmdUI->SetCheck(bReadOnly);
2496 * @brief Enable/disable middle buffer read-only
2498 void CMergeEditView::OnMiddleReadOnly()
2500 CMergeDoc *pd = GetDocument();
2501 if (pd->m_nBuffers == 3)
2503 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2504 pd->m_ptBuf[1]->SetReadOnly(!bReadOnly);
2509 * @brief Called when "Middle read-only" item is updated
2511 void CMergeEditView::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
2513 CMergeDoc *pd = GetDocument();
2514 if (pd->m_nBuffers < 3)
2516 pCmdUI->Enable(false);
2520 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2521 pCmdUI->Enable(true);
2522 pCmdUI->SetCheck(bReadOnly);
2527 * @brief Enable/disable right buffer read-only
2529 void CMergeEditView::OnRightReadOnly()
2531 CMergeDoc *pd = GetDocument();
2532 bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2533 pd->m_ptBuf[pd->m_nBuffers - 1]->SetReadOnly(!bReadOnly);
2537 * @brief Called when "Left read-only" item is updated
2539 void CMergeEditView::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
2541 CMergeDoc *pd = GetDocument();
2542 bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2543 pCmdUI->Enable(true);
2544 pCmdUI->SetCheck(bReadOnly);
2547 /// Store interface we use to display status line info
2548 void CMergeEditView::SetStatusInterface(IMergeEditStatus * piMergeEditStatus)
2550 ASSERT(m_piMergeEditStatus == nullptr);
2551 m_piMergeEditStatus = piMergeEditStatus;
2555 * @brief Update status bar contents.
2557 void CMergeEditView::UpdateStatusbar()
2563 * @brief Update statusbar info, Override from CCrystalTextView
2564 * @note we tab-expand column, but we don't tab-expand char count,
2565 * since we want to show how many chars there are and tab is just one
2566 * character although it expands to several spaces.
2568 void CMergeEditView::OnUpdateCaret()
2570 if (m_piMergeEditStatus == nullptr || !IsTextBufferInitialized())
2573 CPoint cursorPos = GetCursorPos();
2574 int nScreenLine = cursorPos.y;
2575 const int nRealLine = ComputeRealLine(nScreenLine);
2582 DWORD dwLineFlags = 0;
2584 dwLineFlags = m_pTextBuffer->GetLineFlags(nScreenLine);
2585 // Is this a ghost line ?
2586 if (dwLineFlags & LF_GHOST)
2588 // Ghost lines display eg "Line 12-13"
2589 sLine.Format(_T("%d-%d"), nRealLine, nRealLine+1);
2590 sEol = _T("hidden");
2594 // Regular lines display eg "Line 13 Characters: 25 EOL: CRLF"
2595 sLine.Format(_T("%d"), nRealLine+1);
2596 curChar = cursorPos.x + 1;
2597 chars = GetLineLength(nScreenLine);
2598 column = CalculateActualOffset(nScreenLine, cursorPos.x, true) + 1;
2599 columns = CalculateActualOffset(nScreenLine, chars, true) + 1;
2601 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2602 GetDocument()->IsMixedEOL(m_nThisPane))
2604 sEol = GetTextBufferEol(nScreenLine);
2607 sEol = _T("hidden");
2609 m_piMergeEditStatus->SetLineInfo(sLine, column, columns,
2610 curChar, chars, sEol, GetDocument()->m_ptBuf[m_nThisPane]->getCodepage(), GetDocument()->m_ptBuf[m_nThisPane]->getHasBom());
2612 // Is cursor inside difference?
2613 if (dwLineFlags & LF_NONTRIVIAL_DIFF)
2614 m_bCurrentLineIsDiff = true;
2616 m_bCurrentLineIsDiff = false;
2618 CWnd* pWnd = GetFocus();
2619 if (!m_bDetailView || (pWnd && pWnd->m_hWnd == this->m_hWnd))
2620 UpdateLocationViewPosition(m_nTopSubLine, m_nTopSubLine + GetScreenLines());
2623 * @brief Select linedifference in the current line.
2625 * Select line difference in current line. Selection type
2626 * is choosed by highlight type.
2628 template<bool reversed>
2629 void CMergeEditView::OnSelectLineDiff()
2631 // Pass this to the document, to compare this file to other
2632 GetDocument()->Showlinediff(this, reversed);
2635 /// Enable select difference menuitem if current line is inside difference.
2636 void CMergeEditView::OnUpdateSelectLineDiff(CCmdUI* pCmdUI)
2638 int line = GetCursorPos().y;
2639 bool enable = ((GetLineFlags(line) & (LF_DIFF | LF_GHOST)) != 0);
2640 if (GetDocument()->IsEditedAfterRescan(m_nThisPane))
2642 pCmdUI->Enable(enable);
2646 * @brief Enable/disable Replace-menuitem
2648 void CMergeEditView::OnUpdateEditReplace(CCmdUI* pCmdUI)
2650 CMergeDoc *pd = GetDocument();
2651 bool bReadOnly = pd->m_ptBuf[m_nThisPane]->GetReadOnly();
2653 pCmdUI->Enable(!bReadOnly);
2657 * @brief Update readonly statusbaritem
2659 void CMergeEditView::OnUpdateStatusRO(CCmdUI* pCmdUI)
2661 bool bRO = GetDocument()->m_ptBuf[pCmdUI->m_nID - ID_STATUS_PANE0FILE_RO]->GetReadOnly();
2662 pCmdUI->Enable(bRO);
2666 * @brief Create the dynamic submenu for scripts
2668 HMENU CMergeEditView::createScriptsSubmenu(HMENU hMenu)
2671 std::vector<String> functionNamesList = FileTransform::GetFreeFunctionsInScripts(L"EDITOR_SCRIPT");
2674 size_t i = GetMenuItemCount(hMenu);
2676 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2678 if (functionNamesList.size() == 0)
2680 // no script : create a <empty> entry
2681 AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, _("< Empty >").c_str());
2685 // or fill in the submenu with the scripts names
2686 int ID = ID_SCRIPT_FIRST; // first ID in menu
2687 for (i = 0 ; i < functionNamesList.size() ; i++, ID++)
2688 AppendMenu(hMenu, MF_STRING, ID, functionNamesList[i].c_str());
2690 functionNamesList.clear();
2693 if (!plugin::IsWindowsScriptThere())
2694 AppendMenu(hMenu, MF_STRING, ID_NO_SCT_SCRIPTS, _("WSH not found - .sct scripts disabled").c_str());
2700 * @brief Create the dynamic submenu for prediffers
2702 * @note The plugins are grouped in (suggested) and (not suggested)
2703 * The IDs follow the order of GetAvailableScripts
2705 * suggested 0 ID_1ST + 0
2706 * suggested 1 ID_1ST + 2
2707 * suggested 2 ID_1ST + 5
2708 * not suggested 0 ID_1ST + 1
2709 * not suggested 1 ID_1ST + 3
2710 * not suggested 2 ID_1ST + 4
2712 HMENU CMergeEditView::createPrediffersSubmenu(HMENU hMenu)
2715 int i = GetMenuItemCount(hMenu);
2717 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2719 CMergeDoc *pd = GetDocument();
2720 ASSERT(pd != nullptr);
2723 AppendMenu(hMenu, MF_STRING, ID_NO_PREDIFFER, _("No prediffer (normal)").c_str());
2725 // get the scriptlet files
2726 PluginArray * piScriptArray =
2727 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
2728 PluginArray * piScriptArray2 =
2729 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
2731 // build the menu : first part, suggested plugins
2733 AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2734 AppendMenu(hMenu, MF_STRING, ID_SUGGESTED_PLUGINS, _("Suggested plugins").c_str());
2736 int ID = ID_PREDIFFERS_FIRST; // first ID in menu
2738 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2740 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2741 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2744 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2746 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2748 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2749 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2752 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2755 // build the menu : second part, others plugins
2757 AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2758 AppendMenu(hMenu, MF_STRING, ID_NOT_SUGGESTED_PLUGINS, _("Other plugins").c_str());
2760 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2761 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2763 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2764 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2767 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2769 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2771 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2772 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2775 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2778 // compute the m_CurrentPredifferID (to set the radio button)
2779 PrediffingInfo prediffer;
2780 pd->GetPrediffer(&prediffer);
2782 if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
2783 m_CurrentPredifferID = 0;
2784 else if (prediffer.m_PluginName.empty())
2785 m_CurrentPredifferID = ID_NO_PREDIFFER;
2788 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2789 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2791 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2792 if (prediffer.m_PluginName == plugin->m_name)
2793 m_CurrentPredifferID = ID;
2796 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2798 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2799 if (prediffer.m_PluginName == plugin->m_name)
2800 m_CurrentPredifferID = ID;
2808 * @brief Offer a context menu built with scriptlet/ActiveX functions
2810 void CMergeEditView::OnContextMenu(CWnd* pWnd, CPoint point)
2812 // Create the menu and populate it with the available functions
2814 VERIFY(menu.LoadMenu(IDR_POPUP_MERGEVIEW));
2816 // Remove copying item copying from active side
2817 if (m_nThisPane == 0) // left?
2819 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2820 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2822 if (m_nThisPane == GetDocument()->m_nBuffers - 1)
2824 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2825 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2828 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
2829 theApp.TranslateMenu(menu.m_hMenu);
2831 BCMenu *pSub = static_cast<BCMenu *>(menu.GetSubMenu(0));
2832 ASSERT(pSub != nullptr);
2834 // Context menu opened using keyboard has no coordinates
2835 if (point.x == -1 && point.y == -1)
2838 GetClientRect(rect);
2839 ClientToScreen(rect);
2841 point = rect.TopLeft();
2845 pSub->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
2846 point.x, point.y, AfxGetMainWnd());
2851 * @brief Update EOL mode in status bar
2853 void CMergeEditView::OnUpdateStatusEOL(CCmdUI* pCmdUI)
2855 GetGroupView(pCmdUI->m_nID - ID_STATUS_PANE0FILE_EOL)->OnUpdateIndicatorCRLF(pCmdUI);
2859 * @brief Change EOL mode and unify all the lines EOL to this new mode
2861 void CMergeEditView::OnConvertEolTo(UINT nID )
2863 CRLFSTYLE nStyle = CRLF_STYLE_AUTOMATIC;;
2867 nStyle = CRLF_STYLE_DOS;
2869 case ID_EOL_TO_UNIX:
2870 nStyle = CRLF_STYLE_UNIX;
2873 nStyle = CRLF_STYLE_MAC;
2877 _RPTF0(_CRT_ERROR, "Unhandled EOL type conversion!");
2880 m_pTextBuffer->SetCRLFMode(nStyle);
2882 // we don't need a derived applyEOLMode for ghost lines as they have no EOL char
2883 if (m_pTextBuffer->applyEOLMode())
2885 CMergeDoc *pd = GetDocument();
2886 ASSERT(pd != nullptr);
2887 pd->UpdateHeaderPath(m_nThisPane);
2888 pd->FlushAndRescan(true);
2893 * @brief allow convert to entries in file submenu
2895 void CMergeEditView::OnUpdateConvertEolTo(CCmdUI* pCmdUI)
2897 int nStyle = CRLF_STYLE_AUTOMATIC;
2898 switch (pCmdUI->m_nID)
2901 nStyle = CRLF_STYLE_DOS;
2903 case ID_EOL_TO_UNIX:
2904 nStyle = CRLF_STYLE_UNIX;
2907 nStyle = CRLF_STYLE_MAC;
2911 _RPTF0(_CRT_ERROR, "Missing menuitem handler for EOL convert menu!");
2915 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2916 GetDocument()->IsMixedEOL(m_nThisPane) ||
2917 nStyle != m_pTextBuffer->GetCRLFMode())
2919 pCmdUI->SetRadio(false);
2921 // Don't allow selecting other EOL style for protected pane
2922 if (!QueryEditable())
2923 pCmdUI->Enable(false);
2926 pCmdUI->SetRadio(true);
2930 * @brief Copy diff from left to right and advance to next diff
2932 void CMergeEditView::OnL2RNext()
2935 if (IsCursorInDiff()) // for 3-way file compare
2941 * @brief Update "Copy right and advance" UI item
2943 void CMergeEditView::OnUpdateL2RNext(CCmdUI* pCmdUI)
2945 OnUpdateL2r(pCmdUI);
2949 * @brief Copy diff from right to left and advance to next diff
2951 void CMergeEditView::OnR2LNext()
2954 if (IsCursorInDiff()) // for 3-way file compare
2960 * @brief Update "Copy left and advance" UI item
2962 void CMergeEditView::OnUpdateR2LNext(CCmdUI* pCmdUI)
2964 OnUpdateR2l(pCmdUI);
2968 * @brief Change active pane in MergeView.
2969 * Changes active pane and makes sure cursor position is kept in
2970 * screen. Currently we put cursor in same line than in original
2971 * active pane but we could be smarter too? Maybe update cursor
2972 * only when it is not visible in new pane?
2974 void CMergeEditView::OnChangePane()
2976 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
2977 CMergeEditView *pWnd = static_cast<CMergeEditView*>(pSplitterWnd->GetActivePane());
2978 CMergeDoc *pDoc = GetDocument();
2979 bool bFound = false;
2980 CMergeEditView *pNextActiveView = nullptr;
2981 std::vector<CMergeEditView *> list = pDoc->GetViewList();
2982 list.insert(list.end(), list.begin(), list.end());
2983 for (auto& pView : list)
2985 if (bFound && pView->m_bDetailView == pWnd->m_bDetailView)
2987 pNextActiveView = pView;
2993 GetParentFrame()->SetActiveView(pNextActiveView);
2994 CPoint ptCursor = pWnd->GetCursorPos();
2996 if (ptCursor.y >= pNextActiveView->GetLineCount())
2997 ptCursor.y = pNextActiveView->GetLineCount() - 1;
2998 pNextActiveView->SetCursorPos(ptCursor);
2999 pNextActiveView->SetAnchor(ptCursor);
3000 pNextActiveView->SetSelection(ptCursor, ptCursor);
3004 * @brief Show "Go To" dialog and scroll views to line or diff.
3006 * Before dialog is opened, current line and file is determined
3008 * @note Conversions needed between apparent and real lines
3010 void CMergeEditView::OnWMGoto()
3013 CMergeDoc *pDoc = GetDocument();
3014 CPoint pos = GetCursorPos();
3018 nRealLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(pos.y);
3019 int nLineCount = pDoc->m_ptBuf[m_nThisPane]->GetLineCount();
3020 nLastLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(nLineCount - 1);
3022 // Set active file and current line selected in dialog
3023 dlg.m_strParam = strutils::to_str(nRealLine + 1);
3024 dlg.m_nFile = (pDoc->m_nBuffers < 3) ? (m_nThisPane == 1 ? 2 : 0) : m_nThisPane;
3025 dlg.m_nGotoWhat = 0;
3027 if (dlg.DoModal() == IDOK)
3029 CMergeDoc * pDoc1 = GetDocument();
3030 CMergeEditView * pCurrentView = nullptr;
3033 pCurrentView = GetGroupView(m_nThisPane);
3036 try { num = std::stoi(dlg.m_strParam) - 1; } catch(...) {}
3038 if (dlg.m_nGotoWhat == 0)
3040 int nRealLine1 = num;
3043 if (nRealLine1 > nLastLine)
3044 nRealLine1 = nLastLine;
3046 GotoLine(nRealLine1, true, (pDoc1->m_nBuffers < 3) ? (dlg.m_nFile == 2 ? 1 : 0) : dlg.m_nFile);
3053 if (diff >= pDoc1->m_diffList.GetSize())
3054 diff = pDoc1->m_diffList.GetSize();
3056 pCurrentView->SelectDiff(diff, true, false);
3061 void CMergeEditView::OnShellMenu()
3063 CFrameWnd *pFrame = GetTopLevelFrame();
3064 ASSERT(pFrame != nullptr);
3065 BOOL bAutoMenuEnableOld = pFrame->m_bAutoMenuEnable;
3066 pFrame->m_bAutoMenuEnable = FALSE;
3068 String path = GetDocument()->m_filePaths[m_nThisPane];
3069 std::unique_ptr<CShellContextMenu> pContextMenu(new CShellContextMenu(0x9000, 0x9FFF));
3070 pContextMenu->Initialize();
3071 pContextMenu->AddItem(paths::GetParentPath(path), paths::FindFileName(path));
3072 pContextMenu->RequeryShellContextMenu();
3074 ::GetCursorPos(&point);
3075 HWND hWnd = GetSafeHwnd();
3076 BOOL nCmd = TrackPopupMenu(pContextMenu->GetHMENU(), TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, 0, hWnd, nullptr);
3078 pContextMenu->InvokeCommand(nCmd, hWnd);
3079 pContextMenu->ReleaseShellContextMenu();
3081 pFrame->m_bAutoMenuEnable = bAutoMenuEnableOld;
3084 void CMergeEditView::OnUpdateShellMenu(CCmdUI* pCmdUI)
3086 pCmdUI->Enable(!GetDocument()->m_filePaths[m_nThisPane].empty());
3090 * @brief Reload options.
3092 void CMergeEditView::RefreshOptions()
3094 RENDERING_MODE nRenderingMode = static_cast<RENDERING_MODE>(GetOptionsMgr()->GetInt(OPT_RENDERING_MODE));
3095 SetRenderingMode(nRenderingMode);
3097 m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
3099 if (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0)
3100 SetInsertTabs(true);
3102 SetInsertTabs(false);
3104 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3106 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
3107 SetTextType(CCrystalTextView::SRC_PLAIN);
3109 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3110 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3112 SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3113 SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE),
3114 GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3115 GetDocument()->IsMixedEOL(m_nThisPane));
3117 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
3120 void CMergeEditView::OnScripts(UINT nID )
3122 // text is CHAR if compiled without UNICODE, WCHAR with UNICODE
3123 String text = GetSelectedText();
3125 // transform the text with a script/ActiveX function, event=EDITOR_SCRIPT
3126 bool bChanged = FileTransform::Interactive(text, L"EDITOR_SCRIPT", nID - ID_SCRIPT_FIRST);
3128 // now replace the text
3129 ReplaceSelection(text.c_str(), text.length(), 0);
3133 * @brief Called when an editor script item is updated
3135 void CMergeEditView::OnUpdateNoEditScripts(CCmdUI* pCmdUI)
3137 // append the scripts submenu
3138 HMENU scriptsSubmenu = dynamic_cast<CMainFrame*>(AfxGetMainWnd())->GetScriptsSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu);
3139 if (scriptsSubmenu != nullptr)
3140 createScriptsSubmenu(scriptsSubmenu);
3142 pCmdUI->Enable(true);
3146 * @brief Called when an editor script item is updated
3148 void CMergeEditView::OnUpdatePrediffer(CCmdUI* pCmdUI)
3150 pCmdUI->Enable(true);
3152 CMergeDoc *pd = GetDocument();
3153 ASSERT(pd != nullptr);
3154 PrediffingInfo prediffer;
3155 pd->GetPrediffer(&prediffer);
3157 if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
3159 pCmdUI->SetRadio(false);
3163 // Detect when CDiffWrapper::RunFileDiff has canceled a buggy prediffer
3164 if (prediffer.m_PluginName.empty())
3165 m_CurrentPredifferID = ID_NO_PREDIFFER;
3167 pCmdUI->SetRadio(pCmdUI->m_nID == static_cast<UINT>(m_CurrentPredifferID));
3171 * @brief Update "Prediffer" menuitem
3173 void CMergeEditView::OnUpdateNoPrediffer(CCmdUI* pCmdUI)
3175 // recreate the sub menu (to fill the "selected prediffers")
3176 GetMainFrame()->UpdatePrediffersMenu();
3180 void CMergeEditView::OnNoPrediffer()
3182 OnPrediffer(ID_NO_PREDIFFER);
3185 * @brief Handler for all prediffer choices, including ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, ID_NO_PREDIFFER, & specific prediffers
3187 void CMergeEditView::OnPrediffer(UINT nID )
3189 CMergeDoc *pd = GetDocument();
3190 ASSERT(pd != nullptr);
3192 SetPredifferByMenu(nID);
3193 pd->FlushAndRescan(true);
3197 * @brief Handler for all prediffer choices.
3198 * Prediffer choises include ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO,
3199 * ID_NO_PREDIFFER, & specific prediffers.
3201 void CMergeEditView::SetPredifferByMenu(UINT nID )
3203 CMergeDoc *pd = GetDocument();
3204 ASSERT(pd != nullptr);
3206 if (nID == ID_NO_PREDIFFER)
3208 m_CurrentPredifferID = nID;
3209 // All flags are set correctly during the construction
3210 PrediffingInfo *infoPrediffer = new PrediffingInfo;
3211 infoPrediffer->m_PluginOrPredifferMode = PLUGIN_MANUAL;
3212 infoPrediffer->m_PluginName.clear();
3213 pd->SetPrediffer(infoPrediffer);
3214 pd->FlushAndRescan(true);
3218 // get the scriptlet files
3219 PluginArray * piScriptArray =
3220 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3221 PluginArray * piScriptArray2 =
3222 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3224 // build a PrediffingInfo structure fom the ID
3225 PrediffingInfo prediffer;
3226 prediffer.m_PluginOrPredifferMode = PLUGIN_MANUAL;
3228 size_t pluginNumber = nID - ID_PREDIFFERS_FIRST;
3229 if (pluginNumber < piScriptArray->size())
3231 prediffer.m_bWithFile = true;
3232 const PluginInfoPtr & plugin = piScriptArray->at(pluginNumber);
3233 prediffer.m_PluginName = plugin->m_name;
3237 pluginNumber -= piScriptArray->size();
3238 if (pluginNumber >= piScriptArray2->size())
3240 prediffer.m_bWithFile = false;
3241 const PluginInfoPtr & plugin = piScriptArray2->at(pluginNumber);
3242 prediffer.m_PluginName = plugin->m_name;
3245 // update data for the radio button
3246 m_CurrentPredifferID = nID;
3248 // update the prediffer and rescan
3249 pd->SetPrediffer(&prediffer);
3253 * @brief Look through available prediffers, and return ID of requested one, if found
3255 int CMergeEditView::FindPrediffer(LPCTSTR prediffer) const
3258 int ID = ID_PREDIFFERS_FIRST;
3260 // Search file prediffers
3261 PluginArray * piScriptArray =
3262 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3263 for (i=0; i<piScriptArray->size(); ++i, ++ID)
3265 const PluginInfoPtr & plugin = piScriptArray->at(i);
3266 if (plugin->m_name == prediffer)
3270 // Search buffer prediffers
3271 PluginArray * piScriptArray2 =
3272 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3273 for (i=0; i<piScriptArray2->size(); ++i, ++ID)
3275 const PluginInfoPtr & plugin = piScriptArray2->at(i);
3276 if (plugin->m_name == prediffer)
3284 * @brief Look through available prediffers, and return ID of requested one, if found
3286 bool CMergeEditView::SetPredifferByName(const CString & prediffer)
3288 int id = FindPrediffer(prediffer);
3289 if (id<0) return false;
3290 SetPredifferByMenu(id);
3295 * @brief Goto given line.
3296 * @param [in] nLine Destination linenumber
3297 * @param [in] bRealLine if true linenumber is real line, otherwise
3298 * it is apparent line (including deleted lines)
3299 * @param [in] pane Pane index of goto target pane (0 = left, 1 = right).
3301 void CMergeEditView::GotoLine(UINT nLine, bool bRealLine, int pane)
3303 CMergeDoc *pDoc = GetDocument();
3304 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3305 CMergeEditView *pCurrentView = nullptr;
3306 if (pSplitterWnd != nullptr)
3307 pCurrentView = static_cast<CMergeEditView*>
3308 (pSplitterWnd->GetActivePane());
3310 int nRealLine = nLine;
3311 int nApparentLine = nLine;
3313 // Compute apparent (shown linenumber) line
3316 if (nRealLine > pDoc->m_ptBuf[pane]->GetLineCount() - 1)
3317 nRealLine = pDoc->m_ptBuf[pane]->GetLineCount() - 1;
3319 nApparentLine = pDoc->m_ptBuf[pane]->ComputeApparentLine(nRealLine);
3323 ptPos.y = nApparentLine;
3325 // Scroll line to center of view
3326 int nScrollLine = GetSubLineIndex(nApparentLine);
3327 nScrollLine -= GetScreenLines() / 2;
3328 if (nScrollLine < 0)
3331 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3333 CMergeEditView *pView = GetGroupView(nPane);
3334 pView->ScrollToSubLine(nScrollLine);
3335 if (ptPos.y < pView->GetLineCount())
3337 pView->SetCursorPos(ptPos);
3338 pView->SetAnchor(ptPos);
3342 CPoint ptPos1(0, pView->GetLineCount() - 1);
3343 pView->SetCursorPos(ptPos1);
3344 pView->SetAnchor(ptPos1);
3348 // If goto target is another view - activate another view.
3349 // This is done for user convenience as user probably wants to
3350 // work with goto target file.
3351 if (GetGroupView(pane) != pCurrentView)
3353 if (pSplitterWnd != nullptr)
3355 if (pSplitterWnd->GetColumnCount() > 1)
3356 pSplitterWnd->SetActivePane(0, pane);
3358 pSplitterWnd->SetActivePane(pane, 0);
3364 * @brief Check for horizontal scroll. Re-route to CSplitterEx if not from
3367 void CMergeEditView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3369 if (pScrollBar == nullptr)
3371 // Scroll did not come frome a scroll bar
3372 // Find the appropriate scroll bar
3373 // and send the message to the splitter window instead
3374 // The event should eventually come back here but with a valid scrollbar
3375 // Along the way it will be propagated to other windows that need it
3376 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3377 CScrollBar* curBar = this->GetScrollBarCtrl(SB_HORZ);
3378 pSplitterWnd->SendMessage(WM_HSCROLL,
3379 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3382 CCrystalTextView::OnHScroll (nSBCode, nPos, pScrollBar);
3386 * @brief When view is scrolled using scrollbars update location pane.
3388 void CMergeEditView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3390 if (pScrollBar == nullptr)
3392 // Scroll did not come frome a scroll bar
3393 // Find the appropriate scroll bar
3394 // and send the message to the splitter window instead
3395 // The event should eventually come back here but with a valid scrollbar
3396 // Along the way it will be propagated to other windows that need it
3397 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3398 CScrollBar* curBar = this->GetScrollBarCtrl(SB_VERT);
3399 pSplitterWnd->SendMessage(WM_VSCROLL,
3400 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3403 CCrystalTextView::OnVScroll (nSBCode, nPos, pScrollBar);
3405 if (nSBCode == SB_ENDSCROLL)
3408 // Note we cannot use nPos because of its 16-bit nature
3409 SCROLLINFO si = {0};
3410 si.cbSize = sizeof (si);
3411 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3412 VERIFY (GetScrollInfo (SB_VERT, &si));
3414 // Get the current position of scroll box.
3415 int nCurPos = si.nPos;
3417 UpdateLocationViewPosition(nCurPos, nCurPos + GetScreenLines());
3421 * @brief Copy selected lines adding linenumbers.
3423 void CMergeEditView::OnEditCopyLineNumbers()
3431 CMergeDoc *pDoc = GetDocument();
3432 GetSelection(ptStart, ptEnd);
3434 // Get last selected line (having widest linenumber)
3435 int line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(ptEnd.y);
3436 size_t nNumWidth = strutils::to_str(line + 1).length();
3438 for (int i = ptStart.y; i <= ptEnd.y; i++)
3440 if (GetLineFlags(i) & LF_GHOST || (GetEnableHideLines() && (GetLineFlags(i) & LF_INVISIBLE)))
3443 // We need to convert to real linenumbers
3444 line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(i);
3446 // Insert spaces to align different width linenumbers (99, 100)
3447 strLine = GetLineText(i);
3448 CString sSpaces(' ', static_cast<int>(nNumWidth - strutils::to_str(line + 1).length()));
3451 strNumLine.Format(_T("%d: %s"), line + 1, (LPCTSTR)strLine);
3452 strText += strNumLine;
3454 PutToClipboard(strText, strText.GetLength(), m_bColumnSelection);
3457 void CMergeEditView::OnUpdateEditCopyLinenumbers(CCmdUI* pCmdUI)
3459 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
3463 * @brief Open active file with associated application.
3465 * First tries to open file using shell 'Edit' action, since that
3466 * action open scripts etc. to editor instead of running them. If
3467 * edit-action is not registered, 'Open' action is used.
3469 void CMergeEditView::OnOpenFile()
3471 CMergeDoc * pDoc = GetDocument();
3472 ASSERT(pDoc != nullptr);
3474 String sFileName = pDoc->m_filePaths[m_nThisPane];
3475 if (sFileName.empty())
3477 HINSTANCE rtn = ShellExecute(::GetDesktopWindow(), _T("edit"), sFileName.c_str(),
3478 0, 0, SW_SHOWNORMAL);
3479 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3480 rtn = ShellExecute(::GetDesktopWindow(), _T("open"), sFileName.c_str(),
3481 0, 0, SW_SHOWNORMAL);
3482 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3487 * @brief Open active file with app selection dialog
3489 void CMergeEditView::OnOpenFileWith()
3491 CMergeDoc * pDoc = GetDocument();
3492 ASSERT(pDoc != nullptr);
3494 String sFileName = pDoc->m_filePaths[m_nThisPane];
3495 if (sFileName.empty())
3499 if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH))
3501 sysdir.ReleaseBuffer();
3502 CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + sFileName.c_str();
3503 ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg,
3504 sysdir, SW_SHOWNORMAL);
3508 * @brief Open active file with external editor
3510 void CMergeEditView::OnOpenFileWithEditor()
3512 CMergeDoc * pDoc = GetDocument();
3513 ASSERT(pDoc != nullptr);
3515 String sFileName = pDoc->m_filePaths[m_nThisPane];
3516 if (sFileName.empty())
3519 int nRealLine = ComputeRealLine(GetCursorPos().y) + 1;
3520 theApp.OpenFileToExternalEditor(sFileName, nRealLine);
3524 * @brief Force repaint of the location pane.
3526 void CMergeEditView::RepaintLocationPane()
3528 // Must force recalculation due to caching of data in location pane.
3529 CLocationView *pLocationView = GetDocument()->GetLocationView();
3530 if (pLocationView != nullptr)
3531 pLocationView->ForceRecalculate();
3535 * @brief Enables/disables linediff (different color for diffs)
3537 void CMergeEditView::OnViewLineDiffs()
3539 bool bWordDiffHighlight = GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
3540 GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, !bWordDiffHighlight);
3542 // Call CMergeDoc RefreshOptions() to refresh *both* views
3543 CMergeDoc *pDoc = GetDocument();
3544 pDoc->RefreshOptions();
3545 pDoc->FlushAndRescan(true);
3548 void CMergeEditView::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
3550 pCmdUI->Enable(true);
3551 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
3555 * @brief Enables/disables line number
3557 void CMergeEditView::OnViewLineNumbers()
3559 GetOptionsMgr()->SaveOption(OPT_VIEW_LINENUMBERS, !GetViewLineNumbers());
3561 // Call CMergeDoc RefreshOptions() to refresh *both* views
3562 CMergeDoc *pDoc = GetDocument();
3563 pDoc->RefreshOptions();
3566 void CMergeEditView::OnUpdateViewLineNumbers(CCmdUI* pCmdUI)
3568 pCmdUI->Enable(true);
3569 pCmdUI->SetCheck(GetViewLineNumbers());
3573 * @brief Enables/disables word wrap
3575 void CMergeEditView::OnViewWordWrap()
3577 GetOptionsMgr()->SaveOption(OPT_WORDWRAP, !m_bWordWrap);
3579 // Call CMergeDoc RefreshOptions() to refresh *both* views
3580 CMergeDoc *pDoc = GetDocument();
3581 pDoc->RefreshOptions();
3582 pDoc->UpdateAllViews(this);
3587 void CMergeEditView::OnUpdateViewWordWrap(CCmdUI* pCmdUI)
3589 pCmdUI->Enable(true);
3590 pCmdUI->SetCheck(m_bWordWrap);
3593 void CMergeEditView::OnViewWhitespace()
3595 GetOptionsMgr()->SaveOption(OPT_VIEW_WHITESPACE, !GetViewTabs());
3597 // Call CMergeDoc RefreshOptions() to refresh *both* views
3598 CMergeDoc *pDoc = GetDocument();
3599 pDoc->RefreshOptions();
3602 void CMergeEditView::OnUpdateViewWhitespace(CCmdUI* pCmdUI)
3604 pCmdUI->SetCheck(GetViewTabs());
3607 void CMergeEditView::OnSize(UINT nType, int cx, int cy)
3609 if (!IsInitialized())
3612 CMergeDoc * pDoc = GetDocument();
3613 if (m_nThisPane < pDoc->m_nBuffers - 1)
3615 // To calculate subline index correctly
3616 // we have to invalidate line cache in all pane before calling the function related the subline.
3617 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3619 CMergeEditView *pView = GetGroupView(nPane);
3620 if (pView != nullptr)
3621 pView->InvalidateScreenRect(false);
3626 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3628 CMergeEditView *pView = GetGroupView(nPane);
3629 if (pView != nullptr)
3630 pView->Invalidate();
3633 // recalculate m_nTopSubLine
3634 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
3638 RecalcVertScrollBar (false, false);
3639 RecalcHorzScrollBar (false, false);
3643 * @brief allocates GDI resources for printing
3644 * @param pDC [in] points to the printer device context
3645 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3647 void CMergeEditView::OnBeginPrinting(CDC * pDC, CPrintInfo * pInfo)
3649 GetParentFrame()->PostMessage(WM_TIMER);
3651 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3653 CMergeEditView *pView = GetDocument()->GetView(m_nThisGroup, pane);
3654 pView->m_bPrintHeader = true;
3655 pView->m_bPrintFooter = true;
3656 pView->CGhostTextView::OnBeginPrinting(pDC, pInfo);
3661 * @brief frees GDI resources for printing
3662 * @param pDC [in] points to the printer device context
3663 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3665 void CMergeEditView::OnEndPrinting(CDC * pDC, CPrintInfo * pInfo)
3667 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3668 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::OnEndPrinting(pDC, pInfo);
3670 GetParentFrame()->PostMessage(WM_TIMER);
3674 * @brief Gets header text to print
3675 * @param [in] nPageNum the page number to print
3676 * @param [out] header text to print
3678 void CMergeEditView::GetPrintHeaderText(int nPageNum, CString & text)
3680 text = GetDocument()->GetTitle();
3684 * @brief Prints header
3685 * @param [in] nPageNum the page number to print
3687 void CMergeEditView::PrintHeader(CDC * pdc, int nPageNum)
3689 if (m_nThisPane > 0)
3691 int oldRight = m_rcPrintArea.right;
3692 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3693 CGhostTextView::PrintHeader(pdc, nPageNum);
3694 m_rcPrintArea.right = oldRight;
3698 * @brief Prints footer
3699 * @param [in] nPageNum the page number to print
3701 void CMergeEditView::PrintFooter(CDC * pdc, int nPageNum)
3703 if (m_nThisPane > 0)
3705 int oldRight = m_rcPrintArea.right;
3706 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3707 CGhostTextView::PrintFooter(pdc, nPageNum);
3708 m_rcPrintArea.right = oldRight;
3711 void CMergeEditView::RecalcPageLayouts (CDC * pDC, CPrintInfo * pInfo)
3713 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3714 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::RecalcPageLayouts(pDC, pInfo);
3718 * @brief Prints or previews both panes.
3719 * @param pDC [in] points to the printer device context
3720 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3722 void CMergeEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
3724 CRect rDraw = pInfo->m_rectDraw;
3725 CSize sz = rDraw.Size();
3726 CMergeDoc *pDoc = GetDocument();
3728 SIZE szLeftTop, szRightBottom;
3729 GetPrintMargins(szLeftTop.cx, szLeftTop.cy, szRightBottom.cx, szRightBottom.cy);
3730 pDC->HIMETRICtoLP(&szLeftTop);
3731 pDC->HIMETRICtoLP(&szRightBottom);
3733 int midX = (sz.cx - szLeftTop.cx - szRightBottom.cx) / pDoc->m_nBuffers;
3736 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
3738 pInfo->m_rectDraw.left = rDraw.left + midX * pane;
3739 pInfo->m_rectDraw.right = pInfo->m_rectDraw.left + midX + szLeftTop.cx + szRightBottom.cx;
3740 CMergeEditView* pPane = pDoc->GetView(m_nThisGroup, pane);
3741 pPane->CGhostTextView::OnPrint(pDC, pInfo);
3745 bool CMergeEditView::IsInitialized() const
3747 CMergeEditView * pThis = const_cast<CMergeEditView *>(this);
3748 CDiffTextBuffer * pBuffer = dynamic_cast<CDiffTextBuffer *>(pThis->LocateTextBuffer());
3749 return pBuffer->IsInitialized();
3753 * @brief returns the number of empty lines which are added for synchronizing the line in two/three panes.
3755 int CMergeEditView::GetEmptySubLines( int nLineIndex )
3757 int nBreaks[3] = {0};
3758 int nMaxBreaks = -1;
3759 CMergeDoc * pDoc = GetDocument();
3760 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3762 CMergeEditView *pView = GetGroupView(nPane);
3763 if (pView != nullptr)
3765 if (nLineIndex >= pView->GetLineCount())
3767 pView->WrapLineCached( nLineIndex, pView->GetScreenChars(), nullptr, nBreaks[nPane] );
3769 nMaxBreaks = max(nMaxBreaks, nBreaks[nPane]);
3772 if (nBreaks[m_nThisPane] < nMaxBreaks)
3773 return nMaxBreaks - nBreaks[m_nThisPane];
3779 * @brief Invalidate sub line index cache from the specified index to the end of file.
3780 * @param [in] nLineIndex Index of the first line to invalidate
3782 void CMergeEditView::InvalidateSubLineIndexCache( int nLineIndex )
3784 CMergeDoc * pDoc = GetDocument();
3785 ASSERT(pDoc != nullptr);
3787 // We have to invalidate sub line index cache on both panes.
3788 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3790 CMergeEditView *pView = GetGroupView(nPane);
3791 if (pView != nullptr)
3792 pView->CCrystalTextView::InvalidateSubLineIndexCache( nLineIndex );
3796 void CMergeEditView::SetWordWrapping( bool bWordWrap )
3798 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3799 GetGroupView(pane)->m_bWordWrap = bWordWrap;
3800 CCrystalTextView::SetWordWrapping(bWordWrap);
3804 * @brief Swap the positions of the two panes
3806 void CMergeEditView::OnViewSwapPanes()
3808 GetDocument()->SwapFiles();
3812 * @brief Determine if difference is visible on screen.
3813 * @param [in] nDiff Number of diff to check.
3814 * @return true if difference is visible.
3816 bool CMergeEditView::IsDiffVisible(int nDiff)
3818 const CMergeDoc *pd = GetDocument();
3821 pd->m_diffList.GetDiff(nDiff, diff);
3823 return IsDiffVisible(diff);
3827 * @brief Determine if difference is visible on screen.
3828 * @param [in] diff diff to check.
3829 * @param [in] nLinesBelow Allow "minimizing" the number of visible lines.
3830 * @return true if difference is visible, false otherwise.
3832 bool CMergeEditView::IsDiffVisible(const DIFFRANGE& diff, int nLinesBelow /*=0*/)
3834 const int nDiffStart = GetSubLineIndex(diff.dbegin);
3835 const int nDiffEnd = GetSubLineIndex(diff.dend);
3836 // Diff's height is last line - first line + last line's line count
3837 const int nDiffHeight = nDiffEnd - nDiffStart + GetSubLines(diff.dend) + 1;
3839 // If diff first line outside current view - context OR
3840 // if diff last line outside current view - context OR
3841 // if diff is bigger than screen
3842 if ((nDiffStart < m_nTopSubLine) ||
3843 (nDiffEnd >= m_nTopSubLine + GetScreenLines() - nLinesBelow) ||
3844 (nDiffHeight >= GetScreenLines()))
3854 /** @brief Open help from mainframe when user presses F1*/
3855 void CMergeEditView::OnHelp()
3857 theApp.ShowHelp(MergeViewHelpLocation);
3861 * @brief Called after document is loaded.
3862 * This function is called from CMergeDoc::OpenDocs() after documents are
3863 * loaded. So this is good place to set View's options etc.
3865 void CMergeEditView::DocumentsLoaded()
3867 // Enable/disable automatic rescan (rescanning after edit)
3868 EnableRescan(GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN));
3870 // SetTextType will revert to language dependent defaults for tab
3871 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
3872 SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3873 const bool mixedEOLs = GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3874 GetDocument()->IsMixedEOL(m_nThisPane);
3875 SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE), mixedEOLs);
3876 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3877 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3878 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3880 // Enable Backspace at beginning of line
3881 SetDisableBSAtSOL(false);
3883 // Set tab type (tabs/spaces)
3884 bool bInsertTabs = (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0);
3885 SetInsertTabs(bInsertTabs);
3887 // Sometimes WinMerge doesn't update scrollbars correctly (they remain
3888 // disabled) after docs are open in screen. So lets make sure they are
3889 // really updated, even though this is unnecessary in most cases.
3890 RecalcHorzScrollBar();
3891 RecalcVertScrollBar();
3895 * @brief Update LocationView position.
3896 * This function updates LocationView position to given lines.
3897 * Usually we want to lines in file compare view and area in
3898 * LocationView to match. Be extra carefull to not call non-existing
3900 * @param [in] nTopLine Top line of current view.
3901 * @param [in] nBottomLine Bottom line of current view.
3903 void CMergeEditView::UpdateLocationViewPosition(int nTopLine /*=-1*/,
3904 int nBottomLine /*= -1*/)
3906 CMergeDoc *pDoc = GetDocument();
3907 if (pDoc == nullptr)
3910 CLocationView *pLocationView = pDoc->GetLocationView();
3912 if (pLocationView != nullptr && IsWindow(pLocationView->GetSafeHwnd()))
3914 pLocationView->UpdateVisiblePos(nTopLine, nBottomLine);
3919 * @brief Enable/Disable view's selection margins.
3920 * Selection margins show bookmarks and word-wrap symbols, so they are pretty
3921 * useful. But it appears many users don't use/need those features and for them
3922 * selection margins are just wasted screen estate.
3924 void CMergeEditView::OnViewMargin()
3926 bool bViewMargin = GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN);
3927 GetOptionsMgr()->SaveOption(OPT_VIEW_FILEMARGIN, !bViewMargin);
3929 SetSelectionMargin(!bViewMargin);
3930 CMergeDoc *pDoc = GetDocument();
3931 pDoc->RefreshOptions();
3932 pDoc->UpdateAllViews(this);
3936 * @brief Update GUI for Enable/Disable view's selection margin.
3937 * @param [in] pCmdUI Pointer to UI item to update.
3939 void CMergeEditView::OnUpdateViewMargin(CCmdUI* pCmdUI)
3941 pCmdUI->Enable(true);
3942 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3946 * @brief Create the "Change Scheme" sub menu.
3947 * @param [in] pCmdUI Pointer to UI item to update.
3949 void CMergeEditView::OnUpdateViewChangeScheme(CCmdUI *pCmdUI)
3951 // Delete the place holder menu.
3952 pCmdUI->m_pSubMenu->DeleteMenu(0, MF_BYPOSITION);
3954 const HMENU hSubMenu = pCmdUI->m_pSubMenu->m_hMenu;
3956 String name = theApp.LoadString(ID_COLORSCHEME_FIRST);
3957 AppendMenu(hSubMenu, MF_STRING, ID_COLORSCHEME_FIRST, name.c_str());
3958 AppendMenu(hSubMenu, MF_SEPARATOR, 0, nullptr);
3960 for (int i = ID_COLORSCHEME_FIRST + 1; i <= ID_COLORSCHEME_LAST; ++i)
3962 name = theApp.LoadString(i);
3963 AppendMenu(hSubMenu, MF_STRING, i, name.c_str());
3966 pCmdUI->Enable(true);
3970 * @brief Change the editor's syntax highlighting scheme.
3971 * @param [in] nID Selected color scheme sub menu id.
3973 void CMergeEditView::OnChangeScheme(UINT nID)
3975 CMergeDoc *pDoc = GetDocument();
3976 ASSERT(pDoc != nullptr);
3978 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3980 CMergeEditView *pView = GetGroupView(nPane);
3981 ASSERT(pView != nullptr);
3983 if (pView != nullptr)
3985 pView->SetTextType(CCrystalTextView::TextType(nID - ID_COLORSCHEME_FIRST));
3986 pView->SetDisableBSAtSOL(false);
3990 pDoc->UpdateAllViews(nullptr);
3994 * @brief Enable all color schemes sub menu items.
3995 * @param [in] pCmdUI Pointer to UI item to update.
3997 void CMergeEditView::OnUpdateChangeScheme(CCmdUI* pCmdUI)
3999 const bool bIsCurrentScheme = (static_cast<UINT>(m_CurSourceDef->type) == (pCmdUI->m_nID - ID_COLORSCHEME_FIRST));
4000 pCmdUI->SetRadio(bIsCurrentScheme);
4001 pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT));
4005 * @brief Called when mouse's wheel is scrolled.
4007 BOOL CMergeEditView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
4009 if ( nFlags == MK_CONTROL )
4011 short amount = zDelta < 0 ? -1: 1;
4014 // no default CCrystalTextView
4015 return CView::OnMouseWheel(nFlags, zDelta, pt);
4018 if (nFlags == MK_SHIFT)
4020 SCROLLINFO si = { sizeof SCROLLINFO };
4021 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
4023 VERIFY(GetScrollInfo(SB_HORZ, &si));
4026 si.nPos -= zDelta / 40;
4027 if (si.nPos > si.nMax) si.nPos = si.nMax;
4028 if (si.nPos < si.nMin) si.nPos = si.nMin;
4030 SetScrollInfo(SB_HORZ, &si);
4033 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, si.nPos) , NULL );
4035 // no default CCrystalTextView
4036 return CView::OnMouseWheel(nFlags, zDelta, pt);
4039 return CGhostTextView::OnMouseWheel(nFlags, zDelta, pt);
4043 * @brief Change font size (zoom) in views.
4044 * @param [in] amount Amount of change/zoom, negative number makes
4045 * font smaller, positive number bigger and 0 reset the font size.
4047 void CMergeEditView::ZoomText(short amount)
4052 const int nLogPixelsY = CClientDC(this).GetDeviceCaps(LOGPIXELSY);
4053 int nPointSize = -MulDiv(lf.lfHeight, 72, nLogPixelsY);
4057 nPointSize = -MulDiv(GetOptionsMgr()->GetInt(OPT_FONT_FILECMP + OPT_FONT_HEIGHT), 72, nLogPixelsY);
4060 nPointSize += amount;
4064 lf.lfHeight = -MulDiv(nPointSize, nLogPixelsY, 72);
4066 CMergeDoc *pDoc = GetDocument();
4067 ASSERT(pDoc != nullptr);
4069 if (pDoc != nullptr)
4071 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
4073 CMergeEditView *pView = GetGroupView(nPane);
4074 ASSERT(pView != nullptr);
4076 if (pView != nullptr)
4084 bool CMergeEditView::QueryEditable()
4086 return m_bDetailView ? false : !GetDocument()->m_ptBuf[m_nThisPane]->GetReadOnly();
4090 * @brief Adjust the point to remain in the displayed diff
4092 * @return Tells if the point has been changed
4094 bool CMergeEditView::EnsureInDiff(CPoint& pt)
4096 int nLineCount = GetLineCount();
4097 if (m_lineBegin >= nLineCount)
4098 m_lineBegin = nLineCount - 1;
4099 if (m_lineEnd >= nLineCount)
4100 m_lineEnd = nLineCount - 1;
4102 int diffLength = m_lineEnd - m_lineBegin + 1;
4103 // first get the degenerate case out of the way
4105 if (diffLength == 0)
4107 if (pt.y == m_lineBegin && pt.x == 0)
4115 if (pt.y < m_lineBegin)
4121 // diff is defined and not below diff
4122 if (m_lineEnd > -1 && pt.y > m_lineEnd)
4125 pt.x = GetLineLength(pt.y);
4131 void CMergeEditView::EnsureVisible(CPoint pt)
4136 // ensure we remain in diff
4137 if (EnsureInDiff(ptNew))
4138 SetCursorPos(ptNew);
4140 CCrystalTextView::EnsureVisible(ptNew);
4143 void CMergeEditView::EnsureVisible(CPoint ptStart, CPoint ptEnd)
4145 CCrystalTextView::EnsureVisible(ptStart, ptEnd);
4148 void CMergeEditView::SetSelection(const CPoint& ptStart, const CPoint& ptEnd, bool bUpdateView)
4150 CPoint ptStartNew = ptStart;
4151 CPoint ptEndNew = ptEnd;
4154 // ensure we remain in diff
4155 EnsureInDiff(ptStartNew);
4156 EnsureInDiff(ptEndNew);
4158 CCrystalTextView::SetSelection(ptStartNew, ptEndNew, bUpdateView);
4161 void CMergeEditView::ScrollToSubLine(int nNewTopLine, bool bNoSmoothScroll /*= FALSE*/, bool bTrackScrollBar /*= TRUE*/)
4165 int nLineCount = GetLineCount();
4166 if (m_lineBegin >= nLineCount)
4167 m_lineBegin = nLineCount - 1;
4168 if (m_lineEnd >= nLineCount)
4169 m_lineEnd = nLineCount - 1;
4171 // ensure we remain in diff
4172 int sublineBegin = GetSubLineIndex(m_lineBegin);
4173 int sublineEnd = GetSubLineIndex(m_lineEnd) + GetSubLines(m_lineEnd) - 1;
4174 int diffLength = sublineEnd - sublineBegin + 1;
4175 int displayLength = GetScreenLines();
4176 if (diffLength <= displayLength)
4177 nNewTopLine = sublineBegin;
4180 if (nNewTopLine < sublineBegin)
4181 nNewTopLine = sublineBegin;
4182 if (nNewTopLine + displayLength - 1 > sublineEnd)
4183 nNewTopLine = GetSubLineIndex(sublineEnd - displayLength + 1);
4186 CPoint pt = GetCursorPos();
4187 if (EnsureInDiff(pt))
4190 CPoint ptSelStart, ptSelEnd;
4191 GetSelection(ptSelStart, ptSelEnd);
4192 if (EnsureInDiff(ptSelStart) || EnsureInDiff(ptSelEnd))
4193 SetSelection(ptSelStart, ptSelEnd);
4195 CCrystalTextView::ScrollToSubLine(nNewTopLine, bNoSmoothScroll, bTrackScrollBar);
4199 * @brief Called when user selects View/Zoom In from menu.
4201 void CMergeEditView::OnViewZoomIn()
4207 * @brief Called when user selects View/Zoom Out from menu.
4209 void CMergeEditView::OnViewZoomOut()
4215 * @brief Called when user selects View/Zoom Normal from menu.
4217 void CMergeEditView::OnViewZoomNormal()
4222 void CMergeEditView::OnDropFiles(const std::vector<String>& tFiles)
4224 if (tFiles.size() > 1 || paths::IsDirectory(tFiles[0]))
4226 GetMainFrame()->GetDropHandler()->GetCallback()(tFiles);
4230 GetDocument()->ChangeFile(m_nThisPane, tFiles[0]);
4233 void CMergeEditView::OnWindowSplit()
4236 auto& wndSplitter = dynamic_cast<CMergeEditFrame *>(GetParentFrame())->GetSplitter();
4237 CMergeDoc *pDoc = GetDocument();
4238 CMergeEditView *pView = pDoc->GetView(0, m_nThisPane);
4239 auto* pwndSplitterChild = pView->GetParentSplitter(pView, false);
4240 int nBuffer = m_nThisPane;
4241 if (pDoc->m_nGroups <= 2)
4243 wndSplitter.SplitRow(1);
4244 wndSplitter.EqualizeRows();
4248 wndSplitter.SetActivePane(0, 0);
4249 wndSplitter.DeleteRow(1);
4250 if (pwndSplitterChild->GetColumnCount() > 1)
4251 pwndSplitterChild->SetActivePane(0, nBuffer);
4253 pwndSplitterChild->SetActivePane(nBuffer, 0);
4257 void CMergeEditView::OnUpdateWindowSplit(CCmdUI* pCmdUI)
4259 pCmdUI->Enable(!m_bDetailView);
4260 pCmdUI->SetCheck(GetDocument()->m_nGroups > 2);