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"
41 using CrystalLineParser::TEXTBLOCK;
43 /** @brief Timer ID for delayed rescan. */
44 const UINT IDT_RESCAN = 2;
45 /** @brief Timer timeout for delayed rescan. */
46 const UINT RESCAN_TIMEOUT = 1000;
48 /** @brief Location for file compare specific help to open. */
49 static TCHAR MergeViewHelpLocation[] = _T("::/htmlhelp/Compare_files.html");
51 /////////////////////////////////////////////////////////////////////////////
54 IMPLEMENT_DYNCREATE(CMergeEditView, CCrystalEditViewEx)
56 CMergeEditView::CMergeEditView()
57 : m_bCurrentLineIsDiff(false)
60 , m_bDetailView(false)
61 , m_piMergeEditStatus(nullptr)
62 , m_bAutomaticRescan(false)
63 , fTimerWaitingForIdle(0)
66 , m_CurrentPredifferID(0)
68 SetParser(&m_xParser);
70 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
73 CMergeEditView::~CMergeEditView()
78 BEGIN_MESSAGE_MAP(CMergeEditView, CCrystalEditViewEx)
79 //{{AFX_MSG_MAP(CMergeEditView)
80 ON_COMMAND(ID_CURDIFF, OnCurdiff)
81 ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
82 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
83 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
84 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
85 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
86 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
87 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
88 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
89 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
90 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
91 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
92 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
93 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
94 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
95 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
96 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
97 ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
98 ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
99 ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
100 ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
101 ON_COMMAND(ID_NEXTDIFFLM, OnNextdiffLM)
102 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLM, OnUpdateNextdiffLM)
103 ON_COMMAND(ID_PREVDIFFLM, OnPrevdiffLM)
104 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLM, OnUpdatePrevdiffLM)
105 ON_COMMAND(ID_NEXTDIFFLR, OnNextdiffLR)
106 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLR, OnUpdateNextdiffLR)
107 ON_COMMAND(ID_PREVDIFFLR, OnPrevdiffLR)
108 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLR, OnUpdatePrevdiffLR)
109 ON_COMMAND(ID_NEXTDIFFMR, OnNextdiffMR)
110 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMR, OnUpdateNextdiffMR)
111 ON_COMMAND(ID_PREVDIFFMR, OnPrevdiffMR)
112 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMR, OnUpdatePrevdiffMR)
113 ON_COMMAND(ID_NEXTDIFFLO, OnNextdiffLO)
114 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLO, OnUpdateNextdiffLO)
115 ON_COMMAND(ID_PREVDIFFLO, OnPrevdiffLO)
116 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLO, OnUpdatePrevdiffLO)
117 ON_COMMAND(ID_NEXTDIFFMO, OnNextdiffMO)
118 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMO, OnUpdateNextdiffMO)
119 ON_COMMAND(ID_PREVDIFFMO, OnPrevdiffMO)
120 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMO, OnUpdatePrevdiffMO)
121 ON_COMMAND(ID_NEXTDIFFRO, OnNextdiffRO)
122 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFRO, OnUpdateNextdiffRO)
123 ON_COMMAND(ID_PREVDIFFRO, OnPrevdiffRO)
124 ON_UPDATE_COMMAND_UI(ID_PREVDIFFRO, OnUpdatePrevdiffRO)
125 ON_WM_LBUTTONDBLCLK()
128 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
129 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
130 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
131 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
132 ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
133 ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
134 ON_COMMAND(ID_L2R, OnL2r)
135 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
136 ON_COMMAND(ID_R2L, OnR2l)
137 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
138 ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
139 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
140 ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
141 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
142 ON_COMMAND(ID_ADD_SYNCPOINT, OnAddSyncPoint)
143 ON_COMMAND(ID_CLEAR_SYNCPOINTS, OnClearSyncPoints)
144 ON_UPDATE_COMMAND_UI(ID_CLEAR_SYNCPOINTS, OnUpdateClearSyncPoints)
145 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
146 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
147 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
149 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
150 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
151 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
152 ON_COMMAND(ID_REFRESH, OnRefresh)
153 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
154 ON_COMMAND(ID_SELECTLINEDIFF, OnSelectLineDiff<false>)
155 ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
156 ON_COMMAND(ID_SELECTPREVLINEDIFF, OnSelectLineDiff<true>)
157 ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
159 ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateEditReplace)
160 ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
161 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
162 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
163 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
164 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
165 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
166 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_RO, OnUpdateStatusRO)
167 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_RO, OnUpdateStatusRO)
168 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_RO, OnUpdateStatusRO)
169 ON_COMMAND_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnConvertEolTo)
170 ON_UPDATE_COMMAND_UI_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnUpdateConvertEolTo)
171 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_EOL, OnUpdateStatusEOL)
172 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_EOL, OnUpdateStatusEOL)
173 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_EOL, OnUpdateStatusEOL)
174 ON_COMMAND(ID_L2RNEXT, OnL2RNext)
175 ON_UPDATE_COMMAND_UI(ID_L2RNEXT, OnUpdateL2RNext)
176 ON_COMMAND(ID_R2LNEXT, OnR2LNext)
177 ON_UPDATE_COMMAND_UI(ID_R2LNEXT, OnUpdateR2LNext)
178 ON_COMMAND(ID_WINDOW_CHANGE_PANE, OnChangePane)
179 ON_COMMAND(ID_NEXT_PANE, OnChangePane)
180 ON_COMMAND(ID_EDIT_WMGOTO, OnWMGoto)
181 ON_COMMAND(ID_FILE_SHELLMENU, OnShellMenu)
182 ON_UPDATE_COMMAND_UI(ID_FILE_SHELLMENU, OnUpdateShellMenu)
183 ON_COMMAND_RANGE(ID_SCRIPT_FIRST, ID_SCRIPT_LAST, OnScripts)
184 ON_COMMAND(ID_NO_PREDIFFER, OnNoPrediffer)
185 ON_UPDATE_COMMAND_UI(ID_NO_PREDIFFER, OnUpdateNoPrediffer)
186 ON_COMMAND_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnPrediffer)
187 ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnUpdatePrediffer)
190 ON_COMMAND(ID_EDIT_COPY_LINENUMBERS, OnEditCopyLineNumbers)
191 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY_LINENUMBERS, OnUpdateEditCopyLinenumbers)
192 ON_COMMAND(ID_VIEW_LINEDIFFS, OnViewLineDiffs)
193 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewLineDiffs)
194 ON_COMMAND(ID_VIEW_WORDWRAP, OnViewWordWrap)
195 ON_UPDATE_COMMAND_UI(ID_VIEW_WORDWRAP, OnUpdateViewWordWrap)
196 ON_COMMAND(ID_VIEW_LINENUMBERS, OnViewLineNumbers)
197 ON_UPDATE_COMMAND_UI(ID_VIEW_LINENUMBERS, OnUpdateViewLineNumbers)
198 ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace)
199 ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace)
200 ON_COMMAND(ID_FILE_OPEN_REGISTERED, OnOpenFile)
201 ON_COMMAND(ID_FILE_OPEN_WITHEDITOR, OnOpenFileWithEditor)
202 ON_COMMAND(ID_FILE_OPEN_WITH, OnOpenFileWith)
203 ON_COMMAND(ID_VIEW_SWAPPANES, OnViewSwapPanes)
204 ON_UPDATE_COMMAND_UI(ID_NO_EDIT_SCRIPTS, OnUpdateNoEditScripts)
207 ON_COMMAND(ID_HELP, OnHelp)
208 ON_COMMAND(ID_VIEW_SELMARGIN, OnViewMargin)
209 ON_UPDATE_COMMAND_UI(ID_VIEW_SELMARGIN, OnUpdateViewMargin)
210 ON_UPDATE_COMMAND_UI(ID_VIEW_CHANGESCHEME, OnUpdateViewChangeScheme)
211 ON_COMMAND_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnChangeScheme)
212 ON_UPDATE_COMMAND_UI_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnUpdateChangeScheme)
214 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
215 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
216 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
217 ON_COMMAND(ID_WINDOW_SPLIT, OnWindowSplit)
218 ON_UPDATE_COMMAND_UI(ID_WINDOW_SPLIT, OnUpdateWindowSplit)
223 /////////////////////////////////////////////////////////////////////////////
224 // CMergeEditView diagnostics
227 CMergeDoc* CMergeEditView::GetDocument() // non-debug version is inline
229 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMergeDoc)));
230 return (CMergeDoc*)m_pDocument;
235 /////////////////////////////////////////////////////////////////////////////
236 // CMergeEditView message handlers
239 * @brief Return text buffer for file in view
241 CCrystalTextBuffer *CMergeEditView::LocateTextBuffer()
243 return GetDocument()->m_ptBuf[m_nThisPane].get();
247 * @brief Update any resources necessary after a GUI language change
249 void CMergeEditView::UpdateResources()
253 CMergeEditView *CMergeEditView::GetGroupView(int nBuffer) const
255 return GetDocument()->GetView(m_nThisGroup, nBuffer);
258 void CMergeEditView::PrimeListWithFile()
260 // Set the tab size now, just in case the options change...
261 // We don't update it at the end of OnOptions,
262 // we can update it safely now
263 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
266 * @brief Return text from line given
268 CString CMergeEditView::GetLineText(int idx)
270 return GetLineChars(idx);
274 * @brief Return text from selection
276 CString CMergeEditView::GetSelectedText()
278 CPoint ptStart, ptEnd;
280 GetSelection(ptStart, ptEnd);
281 if (ptStart != ptEnd)
282 GetTextWithoutEmptys(ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, strText);
287 * @brief Get diffs inside selection.
288 * @param [out] firstDiff First diff inside selection
289 * @param [out] lastDiff Last diff inside selection
290 * @note -1 is returned in parameters if diffs cannot be determined
291 * @todo This shouldn't be called when there is no diffs, so replace
292 * first 'if' with ASSERT()?
294 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff)
299 CMergeDoc *pd = GetDocument();
300 const int nDiffs = pd->m_diffList.GetSignificantDiffs();
304 int firstLine, lastLine;
305 GetFullySelectedLines(firstLine, lastLine);
306 if (lastLine < firstLine)
309 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
310 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
311 if (firstDiff != -1 && lastDiff != -1)
315 // Check that first selected line is first diff's first line or above it
316 VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
317 if ((int)di.dbegin < firstLine)
319 if (firstDiff < lastDiff)
323 // Check that last selected line is last diff's last line or below it
324 VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
325 if ((int)di.dend > lastLine)
327 if (firstDiff < lastDiff)
331 // Special case: one-line diff is not selected if cursor is in it
332 if (firstLine == lastLine)
341 * @brief Get diffs inside selection.
342 * @param [out] firstDiff First diff inside selection
343 * @param [out] lastDiff Last diff inside selection
344 * @param [out] firstWordDiff First word level diff inside selection
345 * @param [out] lastWordDiff Last word level diff inside selection
346 * @note -1 is returned in parameters if diffs cannot be determined
347 * @todo This shouldn't be called when there is no diffs, so replace
348 * first 'if' with ASSERT()?
350 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff, int & firstWordDiff, int & lastWordDiff, const CPoint *pptStart, const CPoint *pptEnd)
357 CMergeDoc *pd = GetDocument();
358 const int nDiffs = pd->m_diffList.GetSignificantDiffs();
362 int firstLine, lastLine;
363 CPoint ptStart, ptEnd;
364 GetSelection(ptStart, ptEnd);
365 if (pptStart != nullptr)
367 if (pptEnd != nullptr)
369 firstLine = ptStart.y;
372 firstDiff = pd->m_diffList.LineToDiff(firstLine);
373 bool firstLineIsNotInDiff = firstDiff == -1;
376 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
381 lastDiff = pd->m_diffList.LineToDiff(lastLine);
382 bool lastLineIsNotInDiff = lastDiff == -1;
384 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
385 if (lastDiff < firstDiff)
392 if (firstDiff != -1 && lastDiff != -1)
396 if (ptStart != ptEnd)
398 VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
399 if (lastLineIsNotInDiff && (firstLineIsNotInDiff || (di.dbegin == firstLine && ptStart.x == 0)))
405 if (firstWordDiff == -1)
407 vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(firstDiff);
408 for (size_t i = 0; i < worddiffs.size(); ++i)
410 int worddiffLen = worddiffs[i].end[m_nThisPane] - worddiffs[i].begin[m_nThisPane];
411 if (worddiffs[i].endline[m_nThisPane] > firstLine ||
412 (firstLine == worddiffs[i].endline[m_nThisPane] &&
413 worddiffs[i].end[m_nThisPane] - (worddiffLen == 0 ? 0 : 1) >= ptStart.x))
415 firstWordDiff = static_cast<int>(i);
420 if (firstLine >= di.dbegin && firstWordDiff == -1)
427 VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
428 vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(lastDiff);
429 for (size_t i = worddiffs.size() - 1; i != (size_t)-1; --i)
431 if (worddiffs[i].beginline[m_nThisPane] < lastLine ||
432 (lastLine == worddiffs[i].beginline[m_nThisPane] && worddiffs[i].begin[m_nThisPane] + 1 <= ptEnd.x))
434 lastWordDiff = static_cast<int>(i);
439 if (lastLine <= di.dend && lastWordDiff == -1)
442 if (firstDiff == lastDiff && (lastWordDiff != -1 && lastWordDiff < firstWordDiff))
449 else if (lastDiff < firstDiff || (firstDiff == lastDiff && firstWordDiff == -1 && lastWordDiff == -1))
466 ASSERT(firstDiff == -1 ? (lastDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
467 ASSERT(lastDiff == -1 ? (firstDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
468 ASSERT(firstDiff != -1 ? firstWordDiff != -1 : true);
471 std::map<int, std::vector<int>> CMergeEditView::GetColumnSelectedWordDiffIndice()
473 CMergeDoc *pDoc = GetDocument();
474 std::map<int, std::vector<int>> ret;
475 std::map<int, std::vector<int> *> list;
476 CPoint ptStart, ptEnd;
477 GetSelection(ptStart, ptEnd);
478 for (int nLine = ptStart.y; nLine <= ptEnd.y; ++nLine)
480 if (pDoc->m_diffList.LineToDiff(nLine) != -1)
482 int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
484 GetColumnSelection(nLine, nLeft, nRight);
485 CPoint ptStart2, ptEnd2;
488 ptStart2.y = ptEnd2.y = nLine;
489 GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff, &ptStart2, &ptEnd2);
490 if (firstDiff != -1 && lastDiff != -1)
492 std::vector<int> *pWordDiffs;
493 if (list.find(firstDiff) == list.end())
494 list.insert(std::pair<int, std::vector<int> *>(firstDiff, new std::vector<int>()));
495 pWordDiffs = list[firstDiff];
496 for (int i = firstWordDiff; i <= lastWordDiff; ++i)
498 if (pWordDiffs->empty() || i != (*pWordDiffs)[pWordDiffs->size() - 1])
499 pWordDiffs->push_back(i);
504 for (auto& it : list)
505 ret.insert(std::pair<int, std::vector<int>>(it.first, *it.second));
509 void CMergeEditView::OnInitialUpdate()
512 CCrystalEditViewEx::OnInitialUpdate();
514 SetFont(dynamic_cast<CMainFrame*>(AfxGetMainWnd())->m_lfDiff);
515 SetAlternateDropTarget(new DropHandler(std::bind(&CMergeEditView::OnDropFiles, this, std::placeholders::_1)));
521 void CMergeEditView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
523 CCrystalEditViewEx::OnActivateView(bActivate, pActivateView, pDeactiveView);
525 CMergeDoc* pDoc = GetDocument();
526 pDoc->UpdateHeaderActivity(m_nThisPane, !!bActivate);
529 std::vector<CrystalLineParser::TEXTBLOCK> CMergeEditView::GetMarkerTextBlocks(int nLineIndex) const
533 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
534 return std::vector<CrystalLineParser::TEXTBLOCK>();
536 return CCrystalTextView::GetMarkerTextBlocks(nLineIndex);
539 std::vector<TEXTBLOCK> CMergeEditView::GetAdditionalTextBlocks (int nLineIndex)
541 static const std::vector<TEXTBLOCK> emptyBlocks;
544 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
548 DWORD dwLineFlags = GetLineFlags(nLineIndex);
549 if ((dwLineFlags & LF_SNP) == LF_SNP || (dwLineFlags & LF_DIFF) != LF_DIFF || (dwLineFlags & LF_MOVED) == LF_MOVED)
552 if (!GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT))
555 CMergeDoc *pDoc = GetDocument();
556 if (pDoc->IsEditedAfterRescan(m_nThisPane))
559 int nDiff = pDoc->m_diffList.LineToDiff(nLineIndex);
564 pDoc->m_diffList.GetDiff(nDiff, cd);
565 int unemptyLineCount = 0;
566 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
568 if (cd.begin[nPane] != cd.end[nPane] + 1)
571 if (unemptyLineCount < 2)
574 vector<WordDiff> worddiffs = pDoc->GetWordDiffArray(nLineIndex);
575 size_t nWordDiffs = worddiffs.size();
577 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
579 std::vector<TEXTBLOCK> blocks(nWordDiffs * 2 + 1);
580 blocks[0].m_nCharPos = 0;
581 blocks[0].m_nColorIndex = COLORINDEX_NONE;
582 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
584 for (i = 0, j = 1; i < nWordDiffs; i++)
586 if (worddiffs[i].beginline[m_nThisPane] > nLineIndex || worddiffs[i].endline[m_nThisPane] < nLineIndex )
588 if (pDoc->m_nBuffers > 2)
590 if (m_nThisPane == 0 && worddiffs[i].op == OP_3RDONLY)
592 else if (m_nThisPane == 2 && worddiffs[i].op == OP_1STONLY)
595 int begin[3], end[3];
596 bool deleted = false;
597 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
599 begin[pane] = (worddiffs[i].beginline[pane] < nLineIndex) ? 0 : worddiffs[i].begin[pane];
600 end[pane] = (worddiffs[i].endline[pane] > nLineIndex) ? GetGroupView(pane)->GetLineLength(nLineIndex) : worddiffs[i].end[pane];
601 if (worddiffs[i].beginline[pane] == worddiffs[i].endline[pane] &&
602 worddiffs[i].begin[pane] == worddiffs[i].end[pane])
605 blocks[j].m_nCharPos = begin[m_nThisPane];
606 if (lineInCurrentDiff)
608 if (m_cachedColors.clrSelDiffText != CLR_NONE)
609 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT1 | COLORINDEX_APPLYFORCE;
611 blocks[j].m_nColorIndex = COLORINDEX_NONE;
612 blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE |
613 (deleted ? COLORINDEX_HIGHLIGHTBKGND4 : COLORINDEX_HIGHLIGHTBKGND1);
617 if (m_cachedColors.clrDiffText != CLR_NONE)
618 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT2 | COLORINDEX_APPLYFORCE;
620 blocks[j].m_nColorIndex = COLORINDEX_NONE;
621 blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE |
622 (deleted ? COLORINDEX_HIGHLIGHTBKGND3 : COLORINDEX_HIGHLIGHTBKGND2);
625 blocks[j].m_nCharPos = end[m_nThisPane];
626 blocks[j].m_nColorIndex = COLORINDEX_NONE;
627 blocks[j].m_nBgColorIndex = COLORINDEX_NONE;
636 COLORREF CMergeEditView::GetColor(int nColorIndex)
638 switch (nColorIndex & ~COLORINDEX_APPLYFORCE)
640 case COLORINDEX_HIGHLIGHTBKGND1:
641 return m_cachedColors.clrSelWordDiff;
642 case COLORINDEX_HIGHLIGHTTEXT1:
643 return m_cachedColors.clrSelWordDiffText;
644 case COLORINDEX_HIGHLIGHTBKGND2:
645 return m_cachedColors.clrWordDiff;
646 case COLORINDEX_HIGHLIGHTTEXT2:
647 return m_cachedColors.clrWordDiffText;
648 case COLORINDEX_HIGHLIGHTBKGND3:
649 return m_cachedColors.clrWordDiffDeleted;
650 case COLORINDEX_HIGHLIGHTBKGND4:
651 return m_cachedColors.clrSelWordDiffDeleted;
654 return CCrystalTextView::GetColor(nColorIndex);
659 * @brief Determine text and background color for line
660 * @param [in] nLineIndex Index of line in view (NOT line in file)
661 * @param [out] crBkgnd Backround color for line
662 * @param [out] crText Text color for line
664 void CMergeEditView::GetLineColors(int nLineIndex, COLORREF & crBkgnd,
665 COLORREF & crText, bool & bDrawWhitespace)
667 DWORD ignoreFlags = 0;
668 GetLineColors2(nLineIndex, ignoreFlags, crBkgnd, crText, bDrawWhitespace);
672 * @brief Determine text and background color for line
673 * @param [in] nLineIndex Index of line in view (NOT line in file)
674 * @param [in] ignoreFlags Flags that caller wishes ignored
675 * @param [out] crBkgnd Backround color for line
676 * @param [out] crText Text color for line
678 * This version allows caller to suppress particular flags
680 void CMergeEditView::GetLineColors2(int nLineIndex, DWORD ignoreFlags, COLORREF & crBkgnd,
681 COLORREF & crText, bool & bDrawWhitespace)
683 if (GetLineCount() <= nLineIndex)
686 DWORD dwLineFlags = GetLineFlags(nLineIndex);
688 if (dwLineFlags & ignoreFlags)
689 dwLineFlags &= (~ignoreFlags);
693 // Line with WinMerge flag,
694 // Lines with only the LF_DIFF/LF_TRIVIAL flags are not colored with Winmerge colors
695 if (dwLineFlags & (LF_WINMERGE_FLAGS & ~LF_DIFF & ~LF_TRIVIAL & ~LF_MOVED & ~LF_SNP))
697 crText = m_cachedColors.clrDiffText;
698 bDrawWhitespace = true;
700 if (dwLineFlags & LF_GHOST)
702 crBkgnd = m_cachedColors.clrDiffDeleted;
707 // If no syntax hilighting
708 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
710 crBkgnd = GetColor (COLORINDEX_BKGND);
711 crText = GetColor (COLORINDEX_NORMALTEXT);
712 bDrawWhitespace = false;
715 // Line not inside diff, get colors from CrystalEditor
716 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
717 crText, bDrawWhitespace);
719 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
721 crBkgnd = GetColor (COLORINDEX_WHITESPACE);
722 crText = GetColor (COLORINDEX_WHITESPACE);
723 bDrawWhitespace = false;
729 if (dwLineFlags & LF_WINMERGE_FLAGS)
731 crText = m_cachedColors.clrDiffText;
732 bDrawWhitespace = true;
733 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
735 if (dwLineFlags & LF_SNP)
737 if (lineInCurrentDiff)
739 if (dwLineFlags & LF_GHOST)
740 crBkgnd = m_cachedColors.clrSelSNPDeleted;
742 crBkgnd = m_cachedColors.clrSelSNP;
743 crText = m_cachedColors.clrSelSNPText;
747 if (dwLineFlags & LF_GHOST)
748 crBkgnd = m_cachedColors.clrSNPDeleted;
750 crBkgnd = m_cachedColors.clrSNP;
751 crText = m_cachedColors.clrSNPText;
755 else if (dwLineFlags & LF_DIFF)
757 if (lineInCurrentDiff)
759 if (dwLineFlags & LF_MOVED)
761 crBkgnd = m_cachedColors.clrSelMoved;
762 crText = m_cachedColors.clrSelMovedText;
766 crBkgnd = m_cachedColors.clrSelDiff;
767 crText = m_cachedColors.clrSelDiffText;
773 if (dwLineFlags & LF_MOVED)
775 crBkgnd = m_cachedColors.clrMoved;
776 crText = m_cachedColors.clrMovedText;
780 crBkgnd = m_cachedColors.clrDiff;
781 crText = m_cachedColors.clrDiffText;
786 else if (dwLineFlags & LF_TRIVIAL)
788 // trivial diff can not be selected
789 if (dwLineFlags & LF_GHOST)
790 // ghost lines in trivial diff has their own color
791 crBkgnd = m_cachedColors.clrTrivialDeleted;
793 crBkgnd = m_cachedColors.clrTrivial;
794 crText = m_cachedColors.clrTrivialText;
797 else if (dwLineFlags & LF_GHOST)
799 if (lineInCurrentDiff)
801 if (dwLineFlags & LF_MOVED)
802 crBkgnd = m_cachedColors.clrSelMovedDeleted;
804 crBkgnd = m_cachedColors.clrSelDiffDeleted;
808 if (dwLineFlags & LF_MOVED)
809 crBkgnd = m_cachedColors.clrMovedDeleted;
811 crBkgnd = m_cachedColors.clrDiffDeleted;
818 // Line not inside diff,
819 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
821 // If no syntax hilighting, get windows default colors
822 crBkgnd = GetColor (COLORINDEX_BKGND);
823 crText = GetColor (COLORINDEX_NORMALTEXT);
824 bDrawWhitespace = false;
827 // Syntax highlighting, get colors from CrystalEditor
828 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
829 crText, bDrawWhitespace);
834 * @brief Sync other pane position
836 void CMergeEditView::UpdateSiblingScrollPos (bool bHorz)
838 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
839 if (pSplitterWnd != nullptr)
841 // See CSplitterWnd::IdFromRowCol() implementation for details
842 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
843 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
844 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
845 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
847 // limit the TopLine : must be smaller than GetLineCount for all the panels
848 int newTopSubLine = m_nTopSubLine;
849 int nRows = pSplitterWnd->GetRowCount ();
850 int nCols = pSplitterWnd->GetColumnCount ();
852 // for (nRow = 0; nRow < nRows; nRow++)
854 // for (int nCol = 0; nCol < nCols; nCol++)
856 // CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
857 // if (pSiblingView != nullptr)
858 // if (pSiblingView->GetSubLineCount() <= newTopSubLine)
859 // newTopSubLine = pSiblingView->GetSubLineCount()-1;
862 if (m_nTopSubLine != newTopSubLine)
863 ScrollToSubLine(newTopSubLine);
865 for (nRow = 0; nRow < nRows; nRow++)
867 for (int nCol = 0; nCol < nCols; nCol++)
869 if (!(nRow == nCurrentRow && nCol == nCurrentCol)) // We don't need to update ourselves
871 CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
872 if (pSiblingView != nullptr && pSiblingView->m_nThisGroup == m_nThisGroup)
873 pSiblingView->OnUpdateSibling (this, bHorz);
881 * @brief Update other panes
883 void CMergeEditView::OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
885 if (pUpdateSource != this)
887 ASSERT (pUpdateSource != nullptr);
888 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
889 CMergeEditView *pSrcView = static_cast<CMergeEditView*>(pUpdateSource);
890 if (!bHorz) // changed this so bHorz works right
892 ASSERT (pSrcView->m_nTopSubLine >= 0);
894 // This ASSERT is wrong: panes have different files and
895 // different linecounts
896 // ASSERT (pSrcView->m_nTopLine < GetLineCount ());
897 if (pSrcView->m_nTopSubLine != m_nTopSubLine)
899 ScrollToSubLine (pSrcView->m_nTopSubLine, true, false);
901 RecalcVertScrollBar(true);
902 RecalcHorzScrollBar();
907 ASSERT (pSrcView->m_nOffsetChar >= 0);
909 // This ASSERT is wrong: panes have different files and
910 // different linelengths
911 // ASSERT (pSrcView->m_nOffsetChar < GetMaxLineLength ());
912 if (pSrcView->m_nOffsetChar != m_nOffsetChar)
914 ScrollToChar (pSrcView->m_nOffsetChar, true, false);
916 RecalcHorzScrollBar(true);
917 RecalcHorzScrollBar();
923 void CMergeEditView::OnDisplayDiff(int nDiff /*=0*/)
925 int newlineBegin, newlineEnd;
926 CMergeDoc *pd = GetDocument();
927 if (nDiff < 0 || nDiff >= pd->m_diffList.GetSize())
935 VERIFY(pd->m_diffList.GetDiff(nDiff, curDiff));
937 newlineBegin = curDiff.dbegin;
938 ASSERT (newlineBegin >= 0);
939 newlineEnd = curDiff.dend;
942 m_lineBegin = newlineBegin;
943 m_lineEnd = newlineEnd;
945 int nLineCount = GetLineCount();
946 if (m_lineBegin > nLineCount)
947 m_lineBegin = nLineCount - 1;
948 if (m_lineEnd > nLineCount)
949 m_lineEnd = nLineCount - 1;
951 if (m_nTopLine == newlineBegin)
954 // scroll to the first line of the diff
955 ScrollToLine(m_lineBegin);
957 // update the width of the horizontal scrollbar
958 RecalcHorzScrollBar();
962 * @brief Selects diff by number and syncs other file
963 * @param [in] nDiff Diff to select, must be >= 0
964 * @param [in] bScroll Scroll diff to view
965 * @param [in] bSelectText Select diff text
966 * @sa CMergeEditView::ShowDiff()
967 * @sa CMergeDoc::SetCurrentDiff()
968 * @todo Parameter bSelectText is never used?
970 void CMergeEditView::SelectDiff(int nDiff, bool bScroll /*= true*/, bool bSelectText /*= true*/)
972 CMergeDoc *pd = GetDocument();
974 // Check that nDiff is valid
976 _RPTF1(_CRT_ERROR, "Diffnumber negative (%d)", nDiff);
977 if (nDiff >= pd->m_diffList.GetSize())
978 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d >= %d)",
979 nDiff, pd->m_diffList.GetSize());
982 pd->SetCurrentDiff(nDiff);
983 ShowDiff(bScroll, bSelectText);
984 pd->UpdateAllViews(this);
985 UpdateSiblingScrollPos(false);
987 // notify either side, as it will notify the other one
988 pd->ForEachView ([&](auto& pView) { if (pView->m_bDetailView) pView->OnDisplayDiff(nDiff); });
991 void CMergeEditView::DeselectDiffIfCursorNotInCurrentDiff()
993 CMergeDoc *pd = GetDocument();
994 // If we have a selected diff, deselect it
995 int nCurrentDiff = pd->GetCurrentDiff();
996 if (nCurrentDiff != -1)
998 CPoint pos = GetCursorPos();
999 if (!IsLineInCurrentDiff(pos.y))
1001 pd->SetCurrentDiff(-1);
1003 pd->UpdateAllViews(this);
1009 * @brief Called when user selects "Current Difference".
1010 * Goes to active diff. If no active diff, selects diff under cursor
1011 * @sa CMergeEditView::SelectDiff()
1012 * @sa CMergeDoc::GetCurrentDiff()
1013 * @sa CMergeDoc::LineToDiff()
1015 void CMergeEditView::OnCurdiff()
1017 CMergeDoc *pd = GetDocument();
1019 // If no diffs, nothing to select
1020 if (!pd->m_diffList.HasSignificantDiffs())
1023 // GetCurrentDiff() returns -1 if no diff selected
1024 int nDiff = pd->GetCurrentDiff();
1027 // Scroll to the first line of the currently selected diff
1028 SelectDiff(nDiff, true, false);
1032 // If cursor is inside diff, select that diff
1033 CPoint pos = GetCursorPos();
1034 nDiff = pd->m_diffList.LineToDiff(pos.y);
1035 if (nDiff != -1 && pd->m_diffList.IsDiffSignificant(nDiff))
1036 SelectDiff(nDiff, true, false);
1041 * @brief Called when "Current diff" item is updated
1043 void CMergeEditView::OnUpdateCurdiff(CCmdUI* pCmdUI)
1045 CMergeDoc *pd = GetDocument();
1046 CPoint pos = GetCursorPos();
1047 int nCurrentDiff = pd->GetCurrentDiff();
1048 if (nCurrentDiff == -1)
1050 int nNewDiff = pd->m_diffList.LineToDiff(pos.y);
1051 pCmdUI->Enable(nNewDiff != -1 && pd->m_diffList.IsDiffSignificant(nNewDiff));
1054 pCmdUI->Enable(true);
1058 * @brief Copy selected text to clipboard
1060 void CMergeEditView::OnEditCopy()
1062 CMergeDoc * pDoc = GetDocument();
1063 CPoint ptSelStart, ptSelEnd;
1064 GetSelection(ptSelStart, ptSelEnd);
1067 if (ptSelStart == ptSelEnd)
1072 if (!m_bColumnSelection)
1074 CDiffTextBuffer * buffer = pDoc->m_ptBuf[m_nThisPane].get();
1076 buffer->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1077 ptSelEnd.y, ptSelEnd.x, text);
1080 GetTextWithoutEmptysInColumnSelection(text);
1082 PutToClipboard(text, text.GetLength(), m_bColumnSelection);
1086 * @brief Called when "Copy" item is updated
1088 void CMergeEditView::OnUpdateEditCopy(CCmdUI* pCmdUI)
1090 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
1094 * @brief Cut current selection to clipboard
1096 void CMergeEditView::OnEditCut()
1098 if (!QueryEditable())
1101 CPoint ptSelStart, ptSelEnd;
1102 CMergeDoc * pDoc = GetDocument();
1103 GetSelection(ptSelStart, ptSelEnd);
1106 if (ptSelStart == ptSelEnd)
1110 if (!m_bColumnSelection)
1111 pDoc->m_ptBuf[m_nThisPane]->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1112 ptSelEnd.y, ptSelEnd.x, text);
1114 GetTextWithoutEmptysInColumnSelection(text);
1116 PutToClipboard(text, text.GetLength(), m_bColumnSelection);
1118 if (!m_bColumnSelection)
1120 CPoint ptCursorPos = ptSelStart;
1121 ASSERT_VALIDTEXTPOS(ptCursorPos);
1122 SetAnchor(ptCursorPos);
1123 SetSelection(ptCursorPos, ptCursorPos);
1124 SetCursorPos(ptCursorPos);
1125 EnsureVisible(ptCursorPos);
1127 pDoc->m_ptBuf[m_nThisPane]->DeleteText(this, ptSelStart.y, ptSelStart.x, ptSelEnd.y,
1128 ptSelEnd.x, CE_ACTION_CUT);
1131 DeleteCurrentColumnSelection (CE_ACTION_CUT);
1133 m_pTextBuffer->SetModified(true);
1137 * @brief Called when "Cut" item is updated
1139 void CMergeEditView::OnUpdateEditCut(CCmdUI* pCmdUI)
1141 if (QueryEditable())
1142 CCrystalEditViewEx::OnUpdateEditCut(pCmdUI);
1144 pCmdUI->Enable(false);
1148 * @brief Paste text from clipboard
1150 void CMergeEditView::OnEditPaste()
1152 if (!QueryEditable())
1155 CCrystalEditViewEx::Paste();
1156 m_pTextBuffer->SetModified(true);
1160 * @brief Called when "Paste" item is updated
1162 void CMergeEditView::OnUpdateEditPaste(CCmdUI* pCmdUI)
1164 if (QueryEditable())
1165 CCrystalEditViewEx::OnUpdateEditPaste(pCmdUI);
1167 pCmdUI->Enable(false);
1171 * @brief Undo last action
1173 void CMergeEditView::OnEditUndo()
1175 CWaitCursor waitstatus;
1176 CMergeDoc* pDoc = GetDocument();
1177 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1180 if (!QueryEditable())
1183 GetParentFrame()->SetActiveView(this, true);
1184 if(CCrystalEditViewEx::DoEditUndo())
1187 pDoc->UpdateHeaderPath(m_nThisPane);
1188 pDoc->FlushAndRescan();
1191 m_pTextBuffer->GetRedoActionCode(nAction);
1192 if (nAction == CE_ACTION_MERGE)
1193 // select the diff so we may just merge it again
1199 tgt->SendMessage(WM_COMMAND, ID_EDIT_UNDO);
1201 if (!pDoc->CanUndo())
1202 pDoc->SetAutoMerged(false);
1206 * @brief Called when "Undo" item is updated
1208 void CMergeEditView::OnUpdateEditUndo(CCmdUI* pCmdUI)
1210 CMergeDoc* pDoc = GetDocument();
1211 if (pDoc->curUndo!=pDoc->undoTgt.begin())
1213 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1214 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
1217 pCmdUI->Enable(false);
1221 * @brief Go to first diff
1223 * Called when user selects "First Difference"
1224 * @sa CMergeEditView::SelectDiff()
1226 void CMergeEditView::OnFirstdiff()
1228 CMergeDoc *pd = GetDocument();
1229 if (pd->m_diffList.HasSignificantDiffs())
1231 int nDiff = pd->m_diffList.FirstSignificantDiff();
1232 SelectDiff(nDiff, true, false);
1237 * @brief Update "First diff" UI items
1239 void CMergeEditView::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1241 OnUpdatePrevdiff(pCmdUI);
1245 * @brief Go to last diff
1247 void CMergeEditView::OnLastdiff()
1249 CMergeDoc *pd = GetDocument();
1250 if (pd->m_diffList.HasSignificantDiffs())
1252 int nDiff = pd->m_diffList.LastSignificantDiff();
1253 SelectDiff(nDiff, true, false);
1258 * @brief Update "Last diff" UI items
1260 void CMergeEditView::OnUpdateLastdiff(CCmdUI* pCmdUI)
1262 OnUpdateNextdiff(pCmdUI);
1266 * @brief Go to next diff and select it.
1268 * Finds and selects next difference. There are several cases:
1269 * - if there is selected difference, and that difference is visible
1270 * on screen, next found difference is selected.
1271 * - if there is selected difference but it is not visible, next
1272 * difference from cursor position is selected. This is what user
1273 * expects to happen and is natural thing to do. Also reduces
1274 * needless scrolling.
1275 * - if there is no selected difference, next difference from cursor
1276 * position is selected.
1278 void CMergeEditView::OnNextdiff()
1280 CMergeDoc *pd = GetDocument();
1281 int cnt = pd->m_ptBuf[0]->GetLineCount();
1285 // Returns -1 if no diff selected
1287 int curDiff = pd->GetCurrentDiff();
1291 if (!IsDiffVisible(curDiff))
1293 // Selected difference not visible, select next from cursor
1294 int line = GetCursorPos().y;
1295 // Make sure we aren't in the first line of the diff
1297 if (!IsValidTextPosY(CPoint(0, line)))
1299 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1303 // Find out if there is a following significant diff
1304 if (curDiff < pd->m_diffList.GetSize() - 1)
1306 nextDiff = pd->m_diffList.NextSignificantDiff(curDiff);
1312 // We don't have a selected difference,
1313 // but cursor can be inside inactive diff
1314 int line = GetCursorPos().y;
1315 if (!IsValidTextPosY(CPoint(0, line)))
1317 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1320 int lastDiff = pd->m_diffList.LastSignificantDiff();
1321 if (nextDiff >= 0 && nextDiff <= lastDiff)
1322 SelectDiff(nextDiff, true, false);
1323 else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1325 if (pDirDoc->MoveableToNextDiff())
1326 pDirDoc->MoveToNextDiff(pd);
1331 * @brief Update "Next diff" UI items
1333 void CMergeEditView::OnUpdateNextdiff(CCmdUI* pCmdUI)
1335 CMergeDoc *pd = GetDocument();
1336 const DIFFRANGE * dfi = pd->m_diffList.LastSignificantDiffRange();
1341 // There aren't any significant differences
1346 // Enable if the beginning of the last significant difference is after caret
1347 enabled = (GetCursorPos().y < (long)dfi->dbegin);
1350 if (!enabled && pd->GetDirDoc())
1351 enabled = pd->GetDirDoc()->MoveableToNextDiff();
1353 pCmdUI->Enable(enabled);
1357 * @brief Go to previous diff and select it.
1359 * Finds and selects previous difference. There are several cases:
1360 * - if there is selected difference, and that difference is visible
1361 * on screen, previous found difference is selected.
1362 * - if there is selected difference but it is not visible, previous
1363 * difference from cursor position is selected. This is what user
1364 * expects to happen and is natural thing to do. Also reduces
1365 * needless scrolling.
1366 * - if there is no selected difference, previous difference from cursor
1367 * position is selected.
1369 void CMergeEditView::OnPrevdiff()
1371 CMergeDoc *pd = GetDocument();
1372 int cnt = pd->m_ptBuf[0]->GetLineCount();
1376 // GetCurrentDiff() returns -1 if no diff selected
1378 int curDiff = pd->GetCurrentDiff();
1382 if (!IsDiffVisible(curDiff))
1384 // Selected difference not visible, select previous from cursor
1385 int line = GetCursorPos().y;
1386 // Make sure we aren't in the last line of the diff
1388 if (!IsValidTextPosY(CPoint(0, line)))
1390 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1394 // Find out if there is a preceding significant diff
1397 prevDiff = pd->m_diffList.PrevSignificantDiff(curDiff);
1403 // We don't have a selected difference,
1404 // but cursor can be inside inactive diff
1405 int line = GetCursorPos().y;
1406 if (!IsValidTextPosY(CPoint(0, line)))
1408 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1411 int firstDiff = pd->m_diffList.FirstSignificantDiff();
1412 if (prevDiff >= 0 && prevDiff >= firstDiff)
1413 SelectDiff(prevDiff, true, false);
1414 else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1416 if (pDirDoc->MoveableToPrevDiff())
1417 pDirDoc->MoveToPrevDiff(pd);
1422 * @brief Update "Previous diff" UI items
1424 void CMergeEditView::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1426 CMergeDoc *pd = GetDocument();
1427 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificantDiffRange();
1432 // There aren't any significant differences
1437 // Enable if the end of the first significant difference is before caret
1438 enabled = (GetCursorPos().y > (long)dfi->dend);
1441 if (!enabled && pd->GetDirDoc())
1442 enabled = pd->GetDirDoc()->MoveableToPrevDiff();
1444 pCmdUI->Enable(enabled);
1447 void CMergeEditView::OnNextConflict()
1449 OnNext3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1453 * @brief Update "Next Conflict" UI items
1455 void CMergeEditView::OnUpdateNextConflict(CCmdUI* pCmdUI)
1457 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1460 void CMergeEditView::OnPrevConflict()
1462 OnPrev3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1466 * @brief Update "Prev Conflict" UI items
1468 void CMergeEditView::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1470 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1474 * @brief Go to next 3-way diff and select it.
1476 void CMergeEditView::OnNext3wayDiff(int nDiffType)
1478 CMergeDoc *pd = GetDocument();
1479 int cnt = pd->m_ptBuf[0]->GetLineCount();
1483 // Returns -1 if no diff selected
1484 int curDiff = pd->GetCurrentDiff();
1488 int nextDiff = curDiff;
1489 if (!IsDiffVisible(curDiff))
1491 // Selected difference not visible, select next from cursor
1492 int line = GetCursorPos().y;
1493 // Make sure we aren't in the first line of the diff
1495 if (!IsValidTextPosY(CPoint(0, line)))
1497 nextDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1501 // Find out if there is a following significant diff
1502 if (curDiff < pd->m_diffList.GetSize() - 1)
1504 nextDiff = pd->m_diffList.NextSignificant3wayDiff(curDiff, nDiffType);
1510 // nextDiff is the next one if there is one, else it is the one we're on
1511 SelectDiff(nextDiff, true, false);
1515 // We don't have a selected difference,
1516 // but cursor can be inside inactive diff
1517 int line = GetCursorPos().y;
1518 if (!IsValidTextPosY(CPoint(0, line)))
1520 curDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1522 SelectDiff(curDiff, true, false);
1527 * @brief Update "Next 3-way diff" UI items
1529 void CMergeEditView::OnUpdateNext3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1531 CMergeDoc *pd = GetDocument();
1533 if (pd->m_nBuffers < 3)
1535 pCmdUI->Enable(false);
1539 const DIFFRANGE * dfi = pd->m_diffList.LastSignificant3wayDiffRange(nDiffType);
1543 // There aren't any significant differences
1544 pCmdUI->Enable(false);
1548 // Enable if the beginning of the last significant difference is after caret
1549 CPoint pos = GetCursorPos();
1550 pCmdUI->Enable(pos.y < (long)dfi->dbegin);
1555 * @brief Go to previous 3-way diff and select it.
1557 void CMergeEditView::OnPrev3wayDiff(int nDiffType)
1559 CMergeDoc *pd = GetDocument();
1561 int cnt = pd->m_ptBuf[0]->GetLineCount();
1565 // GetCurrentDiff() returns -1 if no diff selected
1566 int curDiff = pd->GetCurrentDiff();
1570 int prevDiff = curDiff;
1571 if (!IsDiffVisible(curDiff))
1573 // Selected difference not visible, select previous from cursor
1574 int line = GetCursorPos().y;
1575 // Make sure we aren't in the last line of the diff
1577 if (!IsValidTextPosY(CPoint(0, line)))
1579 prevDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1583 // Find out if there is a preceding significant diff
1586 prevDiff = pd->m_diffList.PrevSignificant3wayDiff(curDiff, nDiffType);
1592 // prevDiff is the preceding one if there is one, else it is the one we're on
1593 SelectDiff(prevDiff, true, false);
1597 // We don't have a selected difference,
1598 // but cursor can be inside inactive diff
1599 int line = GetCursorPos().y;
1600 if (!IsValidTextPosY(CPoint(0, line)))
1602 curDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1604 SelectDiff(curDiff, true, false);
1609 * @brief Update "Previous diff X and Y" UI items
1611 void CMergeEditView::OnUpdatePrev3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1613 CMergeDoc *pd = GetDocument();
1615 if (pd->m_nBuffers < 3)
1617 pCmdUI->Enable(false);
1621 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificant3wayDiffRange(nDiffType);
1625 // There aren't any significant differences
1626 pCmdUI->Enable(false);
1630 // Enable if the end of the first significant difference is before caret
1631 CPoint pos = GetCursorPos();
1632 pCmdUI->Enable(pos.y > (long)dfi->dend);
1636 void CMergeEditView::OnNextdiffLM()
1638 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1641 void CMergeEditView::OnUpdateNextdiffLM(CCmdUI* pCmdUI)
1643 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1646 void CMergeEditView::OnNextdiffLR()
1648 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1651 void CMergeEditView::OnUpdateNextdiffLR(CCmdUI* pCmdUI)
1653 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1656 void CMergeEditView::OnNextdiffMR()
1658 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1661 void CMergeEditView::OnUpdateNextdiffMR(CCmdUI* pCmdUI)
1663 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1666 void CMergeEditView::OnNextdiffLO()
1668 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1671 void CMergeEditView::OnUpdateNextdiffLO(CCmdUI* pCmdUI)
1673 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1676 void CMergeEditView::OnNextdiffMO()
1678 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1681 void CMergeEditView::OnUpdateNextdiffMO(CCmdUI* pCmdUI)
1683 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1686 void CMergeEditView::OnNextdiffRO()
1688 OnNext3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1691 void CMergeEditView::OnUpdateNextdiffRO(CCmdUI* pCmdUI)
1693 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1696 void CMergeEditView::OnPrevdiffLM()
1698 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1701 void CMergeEditView::OnUpdatePrevdiffLM(CCmdUI* pCmdUI)
1703 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1706 void CMergeEditView::OnPrevdiffLR()
1708 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1711 void CMergeEditView::OnUpdatePrevdiffLR(CCmdUI* pCmdUI)
1713 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1716 void CMergeEditView::OnPrevdiffMR()
1718 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1721 void CMergeEditView::OnUpdatePrevdiffMR(CCmdUI* pCmdUI)
1723 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1726 void CMergeEditView::OnPrevdiffLO()
1728 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1731 void CMergeEditView::OnUpdatePrevdiffLO(CCmdUI* pCmdUI)
1733 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1736 void CMergeEditView::OnPrevdiffMO()
1738 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1741 void CMergeEditView::OnUpdatePrevdiffMO(CCmdUI* pCmdUI)
1743 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1746 void CMergeEditView::OnPrevdiffRO()
1748 OnPrev3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1751 void CMergeEditView::OnUpdatePrevdiffRO(CCmdUI* pCmdUI)
1753 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1757 * @brief Clear selection
1759 void CMergeEditView::SelectNone()
1761 SetSelection (GetCursorPos(), GetCursorPos());
1766 * @brief Check if line is inside currently selected diff
1767 * @param [in] nLine 0-based linenumber in view
1768 * @sa CMergeDoc::GetCurrentDiff()
1769 * @sa CMergeDoc::LineInDiff()
1771 bool CMergeEditView::IsLineInCurrentDiff(int nLine) const
1773 // Check validity of nLine
1776 _RPTF1(_CRT_ERROR, "Linenumber is negative (%d)!", nLine);
1777 int nLineCount = LocateTextBuffer()->GetLineCount();
1778 if (nLine >= nLineCount)
1779 _RPTF2(_CRT_ERROR, "Linenumber > linecount (%d>%d)!", nLine, nLineCount);
1782 const CMergeDoc *pd = GetDocument();
1783 int curDiff = pd->GetCurrentDiff();
1786 return pd->m_diffList.LineInDiff(nLine, curDiff);
1790 * @brief Called when mouse left-button double-clicked
1792 * Double-clicking mouse inside diff selects that diff
1794 void CMergeEditView::OnLButtonDblClk(UINT nFlags, CPoint point)
1796 CMergeDoc *pd = GetDocument();
1797 CPoint pos = GetCursorPos();
1799 int diff = pd->m_diffList.LineToDiff(pos.y);
1800 if (diff != -1 && pd->m_diffList.IsDiffSignificant(diff))
1801 SelectDiff(diff, false, false);
1803 CCrystalEditViewEx::OnLButtonDblClk(nFlags, point);
1807 * @brief Called when mouse left button is released.
1809 * If button is released outside diffs, current diff
1812 void CMergeEditView::OnLButtonUp(UINT nFlags, CPoint point)
1814 CCrystalEditViewEx::OnLButtonUp(nFlags, point);
1815 DeselectDiffIfCursorNotInCurrentDiff();
1819 * @brief Called when mouse right button is pressed.
1821 * If right button is pressed outside diffs, current diff
1824 void CMergeEditView::OnRButtonDown(UINT nFlags, CPoint point)
1826 CCrystalEditViewEx::OnRButtonDown(nFlags, point);
1827 DeselectDiffIfCursorNotInCurrentDiff();
1830 void CMergeEditView::OnX2Y(int srcPane, int dstPane)
1832 // Check that right side is not readonly
1833 if (IsReadOnly(dstPane))
1836 CMergeDoc *pDoc = GetDocument();
1837 int currentDiff = pDoc->GetCurrentDiff();
1839 if (currentDiff == -1)
1842 // If cursor is inside diff get number of that diff
1843 if (m_bCurrentLineIsDiff)
1845 CPoint pt = GetCursorPos();
1846 currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1852 if (!m_bColumnSelection)
1854 int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
1855 GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1856 if (firstDiff != -1 && lastDiff != -1)
1858 CWaitCursor waitstatus;
1859 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1864 CWaitCursor waitstatus;
1865 auto wordDiffs = GetColumnSelectedWordDiffIndice();
1867 std::for_each(wordDiffs.rbegin(), wordDiffs.rend(), [&](auto& it) {
1868 pDoc->WordListCopy(srcPane, dstPane, it.first, it.second[0], it.second[it.second.size() - 1], &it.second, i != 0, i == 0);
1873 else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1875 CWaitCursor waitstatus;
1876 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1880 void CMergeEditView::OnUpdateX2Y(int dstPane, CCmdUI* pCmdUI)
1882 // Check that right side is not readonly
1883 if (!IsReadOnly(dstPane))
1885 // If one or more diffs inside selection OR
1886 // there is an active diff OR
1887 // cursor is inside diff
1890 if (m_bCurrentLineIsDiff || (m_pTextBuffer->GetLineFlags(m_ptSelStart.y) & LF_NONTRIVIAL_DIFF) != 0)
1892 pCmdUI->Enable(true);
1896 int firstDiff, lastDiff;
1897 GetFullySelectedDiffs(firstDiff, lastDiff);
1899 pCmdUI->Enable(firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff));
1904 const int currDiff = GetDocument()->GetCurrentDiff();
1905 pCmdUI->Enable(m_bCurrentLineIsDiff || (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff)));
1909 pCmdUI->Enable(false);
1913 * @brief Copy diff from left pane to right pane
1915 * Difference is copied from left to right when
1916 * - difference is selected
1917 * - difference is inside selection (allows merging multiple differences).
1918 * - cursor is inside diff
1920 * If there is selected diff outside selection, we copy selected
1923 void CMergeEditView::OnL2r()
1925 int dstPane = (m_nThisPane < GetDocument()->m_nBuffers - 1) ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1926 int srcPane = dstPane - 1;
1927 OnX2Y(srcPane, dstPane);
1931 * @brief Called when "Copy to left" item is updated
1933 void CMergeEditView::OnUpdateL2r(CCmdUI* pCmdUI)
1935 OnUpdateX2Y(m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1, pCmdUI);
1939 * @brief Copy diff from right pane to left pane
1941 * Difference is copied from left to right when
1942 * - difference is selected
1943 * - difference is inside selection (allows merging multiple differences).
1944 * - cursor is inside diff
1946 * If there is selected diff outside selection, we copy selected
1949 void CMergeEditView::OnR2l()
1951 int dstPane = (m_nThisPane > 0) ? m_nThisPane - 1 : 0;
1952 int srcPane = dstPane + 1;
1953 OnX2Y(srcPane, dstPane);
1957 * @brief Called when "Copy to right" item is updated
1959 void CMergeEditView::OnUpdateR2l(CCmdUI* pCmdUI)
1961 OnUpdateX2Y(m_nThisPane > 0 ? m_nThisPane - 1 : 0, pCmdUI);
1964 void CMergeEditView::OnCopyFromLeft()
1966 int dstPane = m_nThisPane;
1967 int srcPane = dstPane - 1;
1970 OnX2Y(srcPane, dstPane);
1973 void CMergeEditView::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1975 int dstPane = m_nThisPane;
1976 int srcPane = dstPane - 1;
1978 pCmdUI->Enable(false);
1980 OnUpdateX2Y(dstPane, pCmdUI);
1983 void CMergeEditView::OnCopyFromRight()
1985 int dstPane = m_nThisPane;
1986 int srcPane = dstPane + 1;
1987 if (srcPane >= GetDocument()->m_nBuffers)
1989 OnX2Y(srcPane, dstPane);
1992 void CMergeEditView::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
1994 int dstPane = m_nThisPane;
1995 int srcPane = dstPane + 1;
1996 if (srcPane >= GetDocument()->m_nBuffers)
1997 pCmdUI->Enable(false);
1999 OnUpdateX2Y(dstPane, pCmdUI);
2003 * @brief Copy all diffs from right pane to left pane
2005 void CMergeEditView::OnAllLeft()
2007 // Check that left side is not readonly
2008 int srcPane = m_nThisPane > 0 ? m_nThisPane : 1;
2009 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
2010 if (IsReadOnly(dstPane))
2012 CWaitCursor waitstatus;
2014 GetDocument()->CopyAllList(srcPane, dstPane);
2018 * @brief Called when "Copy all to left" item is updated
2020 void CMergeEditView::OnUpdateAllLeft(CCmdUI* pCmdUI)
2022 // Check that left side is not readonly
2023 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
2024 if (!IsReadOnly(dstPane))
2025 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
2027 pCmdUI->Enable(false);
2031 * @brief Copy all diffs from left pane to right pane
2033 void CMergeEditView::OnAllRight()
2035 // Check that right side is not readonly
2036 int srcPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane : m_nThisPane - 1;
2037 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
2038 if (IsReadOnly(dstPane))
2041 CWaitCursor waitstatus;
2043 GetDocument()->CopyAllList(srcPane, dstPane);
2047 * @brief Called when "Copy all to right" item is updated
2049 void CMergeEditView::OnUpdateAllRight(CCmdUI* pCmdUI)
2051 // Check that right side is not readonly
2052 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
2053 if (!IsReadOnly(dstPane))
2054 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
2056 pCmdUI->Enable(false);
2060 * @brief Do Auto merge
2062 void CMergeEditView::OnAutoMerge()
2064 // Check current pane is not readonly
2065 if (GetDocument()->IsModified() || GetDocument()->GetAutoMerged() || !QueryEditable())
2068 CWaitCursor waitstatus;
2070 GetDocument()->DoAutoMerge(m_nThisPane);
2074 * @brief Called when "Auto Merge" item is updated
2076 void CMergeEditView::OnUpdateAutoMerge(CCmdUI* pCmdUI)
2078 pCmdUI->Enable(GetDocument()->m_nBuffers == 3 &&
2079 !GetDocument()->IsModified() &&
2080 !GetDocument()->GetAutoMerged() &&
2085 * @brief Add synchronization point
2087 void CMergeEditView::OnAddSyncPoint()
2089 GetDocument()->AddSyncPoint();
2093 * @brief Clear synchronization points
2095 void CMergeEditView::OnClearSyncPoints()
2097 GetDocument()->ClearSyncPoints();
2101 * @brief Called when "Clear Synchronization Points" item is updated
2103 void CMergeEditView::OnUpdateClearSyncPoints(CCmdUI* pCmdUI)
2105 pCmdUI->Enable(GetDocument()->HasSyncPoints());
2109 * @brief This function is called before other edit events.
2110 * @param [in] nAction Edit operation to do
2111 * @param [in] pszText Text to insert, delete etc
2112 * @sa CCrystalEditView::OnEditOperation()
2113 * @todo More edit-events for rescan delaying?
2115 void CMergeEditView::OnEditOperation(int nAction, LPCTSTR pszText, size_t cchText)
2117 if (!QueryEditable())
2119 // We must not arrive here, and assert helps detect troubles
2124 CMergeDoc* pDoc = GetDocument();
2125 pDoc->SetEditedAfterRescan(m_nThisPane);
2127 // simple hook for multiplex undo operations
2128 // deleted by jtuc 2003-06-28
2129 // now AddUndoRecords does it (so we don't create entry for OnEditOperation with no Undo data in m_pTextBuffer)
2130 /*if(dynamic_cast<CMergeDoc::CDiffTextBuffer*>(m_pTextBuffer)->curUndoGroup())
2132 pDoc->undoTgt.erase(pDoc->curUndo, pDoc->undoTgt.end());
2133 pDoc->undoTgt.push_back(this);
2134 pDoc->curUndo = pDoc->undoTgt.end();
2137 // perform original function
2138 CCrystalEditViewEx::OnEditOperation(nAction, pszText, cchText);
2140 // augment with additional operations
2142 // Change header to inform about changed doc
2143 pDoc->UpdateHeaderPath(m_nThisPane);
2145 // If automatic rescan enabled, rescan after edit events
2146 if (m_bAutomaticRescan)
2148 // keep document up to date
2149 // (Re)start timer to rescan only when user edits text
2150 // If timer starting fails, rescan immediately
2151 if (nAction == CE_ACTION_TYPING ||
2152 nAction == CE_ACTION_REPLACE ||
2153 nAction == CE_ACTION_BACKSPACE ||
2154 nAction == CE_ACTION_INDENT ||
2155 nAction == CE_ACTION_PASTE ||
2156 nAction == CE_ACTION_DELSEL ||
2157 nAction == CE_ACTION_DELETE ||
2158 nAction == CE_ACTION_CUT)
2160 if (!SetTimer(IDT_RESCAN, RESCAN_TIMEOUT, nullptr))
2161 pDoc->FlushAndRescan();
2164 pDoc->FlushAndRescan();
2170 // Update other pane for sync line.
2171 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
2173 if (nPane == m_nThisPane)
2175 CCrystalEditView *pView = GetGroupView(nPane);
2176 if (pView != nullptr)
2177 pView->Invalidate();
2184 * @brief Redo last action
2186 void CMergeEditView::OnEditRedo()
2188 CWaitCursor waitstatus;
2189 CMergeDoc* pDoc = GetDocument();
2190 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2193 if (!QueryEditable())
2196 GetParentFrame()->SetActiveView(this, true);
2197 if(CCrystalEditViewEx::DoEditRedo())
2200 pDoc->UpdateHeaderPath(m_nThisPane);
2201 pDoc->FlushAndRescan();
2206 tgt->SendMessage(WM_COMMAND, ID_EDIT_REDO);
2211 * @brief Called when "Redo" item is updated
2213 void CMergeEditView::OnUpdateEditRedo(CCmdUI* pCmdUI)
2215 CMergeDoc* pDoc = GetDocument();
2216 if (pDoc->curUndo!=pDoc->undoTgt.end())
2218 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2219 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
2222 pCmdUI->Enable(false);
2225 void CMergeEditView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
2227 CCrystalEditViewEx::OnUpdate(pSender, lHint, pHint);
2231 * @brief Scrolls to current diff and/or selects diff text
2232 * @param [in] bScroll If true scroll diff to view
2233 * @param [in] bSelectText If true select diff text
2234 * @note If bScroll and bSelectText are false, this does nothing!
2235 * @todo This shouldn't be called when no diff is selected, so
2236 * somebody could try to ASSERT(nDiff > -1)...
2238 void CMergeEditView::ShowDiff(bool bScroll, bool bSelectText)
2240 CMergeDoc *pd = GetDocument();
2241 const int nDiff = pd->GetCurrentDiff();
2243 // Try to trap some errors
2244 if (nDiff >= pd->m_diffList.GetSize())
2245 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d > %d)!",
2246 nDiff, pd->m_diffList.GetSize());
2248 if (nDiff >= 0 && nDiff < pd->m_diffList.GetSize())
2250 CPoint ptStart, ptEnd;
2252 pd->m_diffList.GetDiff(nDiff, curDiff);
2255 ptStart.y = curDiff.dbegin;
2257 ptEnd.y = curDiff.dend;
2259 if (bScroll && !m_bDetailView)
2261 if (!IsDiffVisible(curDiff, CONTEXT_LINES_BELOW))
2263 // Difference is not visible, scroll it so that max amount of
2264 // scrolling is done while keeping the diff in screen. So if
2265 // scrolling is downwards, scroll the diff to as up in screen
2266 // as possible. This usually brings next diff to the screen
2267 // and we don't need to scroll into it.
2268 int nLine = GetSubLineIndex(ptStart.y);
2269 if (nLine > CONTEXT_LINES_ABOVE)
2271 nLine -= CONTEXT_LINES_ABOVE;
2273 GetGroupView(m_nThisPane)->ScrollToSubLine(nLine);
2274 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2276 if (nPane != m_nThisPane)
2277 GetGroupView(nPane)->ScrollToSubLine(nLine);
2280 GetGroupView(m_nThisPane)->SetCursorPos(ptStart);
2281 GetGroupView(m_nThisPane)->SetAnchor(ptStart);
2282 GetGroupView(m_nThisPane)->SetSelection(ptStart, ptStart);
2283 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2285 if (nPane != m_nThisPane)
2287 GetGroupView(nPane)->SetCursorPos(ptStart);
2288 GetGroupView(nPane)->SetAnchor(ptStart);
2289 GetGroupView(nPane)->SetSelection(ptStart, ptStart);
2296 ptEnd.x = GetLineLength(ptEnd.y);
2297 SetSelection(ptStart, ptEnd);
2306 void CMergeEditView::OnTimer(UINT_PTR nIDEvent)
2308 // Maybe we want theApp::OnIdle to proceed before processing a timer message
2309 // ...but for this the queue must be empty
2310 // The timer message is a low priority message but the queue is maybe not yet empty
2311 // So we set a flag, wait for OnIdle to proceed, then come back here...
2312 // We come back here with a IDLE_TIMER OnTimer message (send with SendMessage
2313 // not with SetTimer so there is no delay)
2315 // IDT_RESCAN was posted because the app wanted to do a flushAndRescan with some delay
2317 // IDLE_TIMER is the false timer used to come back here after OnIdle
2318 // fTimerWaitingForIdle is a bool to store the commands waiting for idle
2319 // (one normal timer = one flag = one command)
2321 if (nIDEvent == IDT_RESCAN)
2323 KillTimer(IDT_RESCAN);
2324 fTimerWaitingForIdle |= FLAG_RESCAN_WAITS_FOR_IDLE;
2325 // notify the app to come back after OnIdle
2326 theApp.SetNeedIdleTimer();
2329 if (nIDEvent == IDLE_TIMER)
2331 // not a real timer, just come back after OnIdle
2332 // look to flags to know what to do
2333 if (fTimerWaitingForIdle & FLAG_RESCAN_WAITS_FOR_IDLE)
2334 GetDocument()->RescanIfNeeded(RESCAN_TIMEOUT/1000);
2335 fTimerWaitingForIdle = 0;
2338 CCrystalEditViewEx::OnTimer(nIDEvent);
2342 * @brief Returns if buffer is read-only
2343 * @note This has no any relation to file being read-only!
2345 bool CMergeEditView::IsReadOnly(int pane) const
2347 return m_bDetailView ? true : (GetDocument()->m_ptBuf[pane]->GetReadOnly() != false);
2351 * @brief Called when "Save left (as...)" item is updated
2353 void CMergeEditView::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
2355 CMergeDoc *pd = GetDocument();
2356 pCmdUI->Enable(!IsReadOnly(0) && pd->m_ptBuf[0]->IsModified());
2360 * @brief Called when "Save middle (as...)" item is updated
2362 void CMergeEditView::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
2364 CMergeDoc *pd = GetDocument();
2365 pCmdUI->Enable(pd->m_nBuffers == 3 && !IsReadOnly(1) && pd->m_ptBuf[1]->IsModified());
2369 * @brief Called when "Save right (as...)" item is updated
2371 void CMergeEditView::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
2373 CMergeDoc *pd = GetDocument();
2374 pCmdUI->Enable(!IsReadOnly(pd->m_nBuffers - 1) && pd->m_ptBuf[pd->m_nBuffers - 1]->IsModified());
2378 * @brief Refresh display using text-buffers
2379 * @note This DOES NOT reload files!
2381 void CMergeEditView::OnRefresh()
2383 CMergeDoc *pd = GetDocument();
2384 ASSERT(pd != nullptr);
2385 pd->FlushAndRescan(true);
2389 * @brief Handle some keys when in merging mode
2391 bool CMergeEditView::MergeModeKeyDown(MSG* pMsg)
2393 bool bHandled = false;
2395 // Allow default text selection when SHIFT pressed
2396 if (::GetAsyncKeyState(VK_SHIFT))
2399 // Allow default editor functions when CTRL pressed
2400 if (::GetAsyncKeyState(VK_CONTROL))
2403 // If we are in merging mode (merge with cursor keys)
2404 // handle some keys here
2405 switch (pMsg->wParam)
2432 * @brief Called before messages are translated.
2434 * Checks if ESC key was pressed, saves and closes doc.
2435 * Also if in merge mode traps cursor keys.
2437 BOOL CMergeEditView::PreTranslateMessage(MSG* pMsg)
2439 if (pMsg->message == WM_KEYDOWN)
2441 // If we are in merging mode (merge with cursor keys)
2442 // handle some keys here
2443 if (theApp.GetMergingMode())
2445 bool bHandled = MergeModeKeyDown(pMsg);
2450 // Close window if user has allowed it from options
2451 if (pMsg->wParam == VK_ESCAPE)
2453 int nCloseWithEsc = GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC);
2454 if (nCloseWithEsc != 0)
2455 GetParentFrame()->PostMessage(WM_CLOSE, 0, 0);
2460 return CCrystalEditViewEx::PreTranslateMessage(pMsg);
2464 * @brief Called when "Save" item is updated
2466 void CMergeEditView::OnUpdateFileSave(CCmdUI* pCmdUI)
2468 CMergeDoc *pd = GetDocument();
2470 bool bModified = false;
2471 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2473 if (pd->m_ptBuf[nPane]->IsModified())
2476 pCmdUI->Enable(bModified);
2480 * @brief Enable/disable left buffer read-only
2482 void CMergeEditView::OnLeftReadOnly()
2484 CMergeDoc *pd = GetDocument();
2485 bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2486 pd->m_ptBuf[0]->SetReadOnly(!bReadOnly);
2490 * @brief Called when "Left read-only" item is updated
2492 void CMergeEditView::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
2494 CMergeDoc *pd = GetDocument();
2495 bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2496 pCmdUI->Enable(true);
2497 pCmdUI->SetCheck(bReadOnly);
2501 * @brief Enable/disable middle buffer read-only
2503 void CMergeEditView::OnMiddleReadOnly()
2505 CMergeDoc *pd = GetDocument();
2506 if (pd->m_nBuffers == 3)
2508 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2509 pd->m_ptBuf[1]->SetReadOnly(!bReadOnly);
2514 * @brief Called when "Middle read-only" item is updated
2516 void CMergeEditView::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
2518 CMergeDoc *pd = GetDocument();
2519 if (pd->m_nBuffers < 3)
2521 pCmdUI->Enable(false);
2525 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2526 pCmdUI->Enable(true);
2527 pCmdUI->SetCheck(bReadOnly);
2532 * @brief Enable/disable right buffer read-only
2534 void CMergeEditView::OnRightReadOnly()
2536 CMergeDoc *pd = GetDocument();
2537 bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2538 pd->m_ptBuf[pd->m_nBuffers - 1]->SetReadOnly(!bReadOnly);
2542 * @brief Called when "Left read-only" item is updated
2544 void CMergeEditView::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
2546 CMergeDoc *pd = GetDocument();
2547 bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2548 pCmdUI->Enable(true);
2549 pCmdUI->SetCheck(bReadOnly);
2552 /// Store interface we use to display status line info
2553 void CMergeEditView::SetStatusInterface(IMergeEditStatus * piMergeEditStatus)
2555 ASSERT(m_piMergeEditStatus == nullptr);
2556 m_piMergeEditStatus = piMergeEditStatus;
2560 * @brief Update status bar contents.
2562 void CMergeEditView::UpdateStatusbar()
2568 * @brief Update statusbar info, Override from CCrystalTextView
2569 * @note we tab-expand column, but we don't tab-expand char count,
2570 * since we want to show how many chars there are and tab is just one
2571 * character although it expands to several spaces.
2573 void CMergeEditView::OnUpdateCaret()
2575 if (m_piMergeEditStatus == nullptr || !IsTextBufferInitialized())
2578 CPoint cursorPos = GetCursorPos();
2579 int nScreenLine = cursorPos.y;
2580 const int nRealLine = ComputeRealLine(nScreenLine);
2587 DWORD dwLineFlags = 0;
2589 dwLineFlags = m_pTextBuffer->GetLineFlags(nScreenLine);
2590 // Is this a ghost line ?
2591 if (dwLineFlags & LF_GHOST)
2593 // Ghost lines display eg "Line 12-13"
2594 sLine.Format(_T("%d-%d"), nRealLine, nRealLine+1);
2595 sEol = _T("hidden");
2599 // Regular lines display eg "Line 13 Characters: 25 EOL: CRLF"
2600 sLine.Format(_T("%d"), nRealLine+1);
2601 curChar = cursorPos.x + 1;
2602 chars = GetLineLength(nScreenLine);
2603 column = CalculateActualOffset(nScreenLine, cursorPos.x, true) + 1;
2604 columns = CalculateActualOffset(nScreenLine, chars, true) + 1;
2606 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2607 GetDocument()->IsMixedEOL(m_nThisPane))
2609 sEol = GetTextBufferEol(nScreenLine);
2612 sEol = _T("hidden");
2614 m_piMergeEditStatus->SetLineInfo(sLine, column, columns,
2615 curChar, chars, sEol, GetDocument()->m_ptBuf[m_nThisPane]->getCodepage(), GetDocument()->m_ptBuf[m_nThisPane]->getHasBom());
2617 // Is cursor inside difference?
2618 if (dwLineFlags & LF_NONTRIVIAL_DIFF)
2619 m_bCurrentLineIsDiff = true;
2621 m_bCurrentLineIsDiff = false;
2623 CWnd* pWnd = GetFocus();
2624 if (!m_bDetailView || (pWnd && pWnd->m_hWnd == this->m_hWnd))
2625 UpdateLocationViewPosition(m_nTopSubLine, m_nTopSubLine + GetScreenLines());
2628 * @brief Select linedifference in the current line.
2630 * Select line difference in current line. Selection type
2631 * is choosed by highlight type.
2633 template<bool reversed>
2634 void CMergeEditView::OnSelectLineDiff()
2636 // Pass this to the document, to compare this file to other
2637 GetDocument()->Showlinediff(this, reversed);
2640 /// Enable select difference menuitem if current line is inside difference.
2641 void CMergeEditView::OnUpdateSelectLineDiff(CCmdUI* pCmdUI)
2643 int line = GetCursorPos().y;
2644 bool enable = ((GetLineFlags(line) & (LF_DIFF | LF_GHOST)) != 0);
2645 if (GetDocument()->IsEditedAfterRescan(m_nThisPane))
2647 pCmdUI->Enable(enable);
2651 * @brief Enable/disable Replace-menuitem
2653 void CMergeEditView::OnUpdateEditReplace(CCmdUI* pCmdUI)
2655 CMergeDoc *pd = GetDocument();
2656 bool bReadOnly = pd->m_ptBuf[m_nThisPane]->GetReadOnly();
2658 pCmdUI->Enable(!bReadOnly);
2662 * @brief Update readonly statusbaritem
2664 void CMergeEditView::OnUpdateStatusRO(CCmdUI* pCmdUI)
2666 bool bRO = GetDocument()->m_ptBuf[pCmdUI->m_nID - ID_STATUS_PANE0FILE_RO]->GetReadOnly();
2667 pCmdUI->Enable(bRO);
2671 * @brief Create the dynamic submenu for scripts
2673 HMENU CMergeEditView::createScriptsSubmenu(HMENU hMenu)
2676 std::vector<String> functionNamesList = FileTransform::GetFreeFunctionsInScripts(L"EDITOR_SCRIPT");
2679 size_t i = GetMenuItemCount(hMenu);
2681 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2683 if (functionNamesList.size() == 0)
2685 // no script : create a <empty> entry
2686 AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, _("< Empty >").c_str());
2690 // or fill in the submenu with the scripts names
2691 int ID = ID_SCRIPT_FIRST; // first ID in menu
2692 for (i = 0 ; i < functionNamesList.size() ; i++, ID++)
2693 AppendMenu(hMenu, MF_STRING, ID, functionNamesList[i].c_str());
2695 functionNamesList.clear();
2698 if (!plugin::IsWindowsScriptThere())
2699 AppendMenu(hMenu, MF_STRING, ID_NO_SCT_SCRIPTS, _("WSH not found - .sct scripts disabled").c_str());
2705 * @brief Create the dynamic submenu for prediffers
2707 * @note The plugins are grouped in (suggested) and (not suggested)
2708 * The IDs follow the order of GetAvailableScripts
2710 * suggested 0 ID_1ST + 0
2711 * suggested 1 ID_1ST + 2
2712 * suggested 2 ID_1ST + 5
2713 * not suggested 0 ID_1ST + 1
2714 * not suggested 1 ID_1ST + 3
2715 * not suggested 2 ID_1ST + 4
2717 HMENU CMergeEditView::createPrediffersSubmenu(HMENU hMenu)
2720 int i = GetMenuItemCount(hMenu);
2722 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2724 CMergeDoc *pd = GetDocument();
2725 ASSERT(pd != nullptr);
2728 AppendMenu(hMenu, MF_STRING, ID_NO_PREDIFFER, _("No prediffer (normal)").c_str());
2730 // get the scriptlet files
2731 PluginArray * piScriptArray =
2732 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
2733 PluginArray * piScriptArray2 =
2734 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
2736 // build the menu : first part, suggested plugins
2738 AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2739 AppendMenu(hMenu, MF_STRING, ID_SUGGESTED_PLUGINS, _("Suggested plugins").c_str());
2741 int ID = ID_PREDIFFERS_FIRST; // first ID in menu
2743 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2745 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2746 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2749 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2751 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2753 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2754 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2757 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2760 // build the menu : second part, others plugins
2762 AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2763 AppendMenu(hMenu, MF_STRING, ID_NOT_SUGGESTED_PLUGINS, _("Other plugins").c_str());
2765 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2766 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2768 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2769 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2772 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2774 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2776 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2777 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2780 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2783 // compute the m_CurrentPredifferID (to set the radio button)
2784 PrediffingInfo prediffer;
2785 pd->GetPrediffer(&prediffer);
2787 if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
2788 m_CurrentPredifferID = 0;
2789 else if (prediffer.m_PluginName.empty())
2790 m_CurrentPredifferID = ID_NO_PREDIFFER;
2793 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2794 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2796 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2797 if (prediffer.m_PluginName == plugin->m_name)
2798 m_CurrentPredifferID = ID;
2801 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2803 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2804 if (prediffer.m_PluginName == plugin->m_name)
2805 m_CurrentPredifferID = ID;
2813 * @brief Offer a context menu built with scriptlet/ActiveX functions
2815 void CMergeEditView::OnContextMenu(CWnd* pWnd, CPoint point)
2817 // Create the menu and populate it with the available functions
2819 VERIFY(menu.LoadMenu(IDR_POPUP_MERGEVIEW));
2821 // Remove copying item copying from active side
2822 if (m_nThisPane == 0) // left?
2824 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2825 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2827 if (m_nThisPane == GetDocument()->m_nBuffers - 1)
2829 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2830 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2833 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
2834 theApp.TranslateMenu(menu.m_hMenu);
2836 BCMenu *pSub = static_cast<BCMenu *>(menu.GetSubMenu(0));
2837 ASSERT(pSub != nullptr);
2839 // Context menu opened using keyboard has no coordinates
2840 if (point.x == -1 && point.y == -1)
2843 GetClientRect(rect);
2844 ClientToScreen(rect);
2846 point = rect.TopLeft();
2850 pSub->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
2851 point.x, point.y, AfxGetMainWnd());
2856 * @brief Update EOL mode in status bar
2858 void CMergeEditView::OnUpdateStatusEOL(CCmdUI* pCmdUI)
2860 GetGroupView(pCmdUI->m_nID - ID_STATUS_PANE0FILE_EOL)->OnUpdateIndicatorCRLF(pCmdUI);
2864 * @brief Change EOL mode and unify all the lines EOL to this new mode
2866 void CMergeEditView::OnConvertEolTo(UINT nID )
2868 CRLFSTYLE nStyle = CRLF_STYLE_AUTOMATIC;;
2872 nStyle = CRLF_STYLE_DOS;
2874 case ID_EOL_TO_UNIX:
2875 nStyle = CRLF_STYLE_UNIX;
2878 nStyle = CRLF_STYLE_MAC;
2882 _RPTF0(_CRT_ERROR, "Unhandled EOL type conversion!");
2885 m_pTextBuffer->SetCRLFMode(nStyle);
2887 // we don't need a derived applyEOLMode for ghost lines as they have no EOL char
2888 if (m_pTextBuffer->applyEOLMode())
2890 CMergeDoc *pd = GetDocument();
2891 ASSERT(pd != nullptr);
2892 pd->UpdateHeaderPath(m_nThisPane);
2893 pd->FlushAndRescan(true);
2898 * @brief allow convert to entries in file submenu
2900 void CMergeEditView::OnUpdateConvertEolTo(CCmdUI* pCmdUI)
2902 int nStyle = CRLF_STYLE_AUTOMATIC;
2903 switch (pCmdUI->m_nID)
2906 nStyle = CRLF_STYLE_DOS;
2908 case ID_EOL_TO_UNIX:
2909 nStyle = CRLF_STYLE_UNIX;
2912 nStyle = CRLF_STYLE_MAC;
2916 _RPTF0(_CRT_ERROR, "Missing menuitem handler for EOL convert menu!");
2920 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2921 GetDocument()->IsMixedEOL(m_nThisPane) ||
2922 nStyle != m_pTextBuffer->GetCRLFMode())
2924 pCmdUI->SetRadio(false);
2926 // Don't allow selecting other EOL style for protected pane
2927 if (!QueryEditable())
2928 pCmdUI->Enable(false);
2931 pCmdUI->SetRadio(true);
2935 * @brief Copy diff from left to right and advance to next diff
2937 void CMergeEditView::OnL2RNext()
2940 if (IsCursorInDiff()) // for 3-way file compare
2946 * @brief Update "Copy right and advance" UI item
2948 void CMergeEditView::OnUpdateL2RNext(CCmdUI* pCmdUI)
2950 OnUpdateL2r(pCmdUI);
2954 * @brief Copy diff from right to left and advance to next diff
2956 void CMergeEditView::OnR2LNext()
2959 if (IsCursorInDiff()) // for 3-way file compare
2965 * @brief Update "Copy left and advance" UI item
2967 void CMergeEditView::OnUpdateR2LNext(CCmdUI* pCmdUI)
2969 OnUpdateR2l(pCmdUI);
2973 * @brief Change active pane in MergeView.
2974 * Changes active pane and makes sure cursor position is kept in
2975 * screen. Currently we put cursor in same line than in original
2976 * active pane but we could be smarter too? Maybe update cursor
2977 * only when it is not visible in new pane?
2979 void CMergeEditView::OnChangePane()
2981 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
2982 CMergeEditView *pWnd = static_cast<CMergeEditView*>(pSplitterWnd->GetActivePane());
2983 CMergeDoc *pDoc = GetDocument();
2984 bool bFound = false;
2985 CMergeEditView *pNextActiveView = nullptr;
2986 std::vector<CMergeEditView *> list = pDoc->GetViewList();
2987 list.insert(list.end(), list.begin(), list.end());
2988 for (auto& pView : list)
2990 if (bFound && pView->m_bDetailView == pWnd->m_bDetailView)
2992 pNextActiveView = pView;
2998 GetParentFrame()->SetActiveView(pNextActiveView);
2999 CPoint ptCursor = pWnd->GetCursorPos();
3001 if (ptCursor.y >= pNextActiveView->GetLineCount())
3002 ptCursor.y = pNextActiveView->GetLineCount() - 1;
3003 pNextActiveView->SetCursorPos(ptCursor);
3004 pNextActiveView->SetAnchor(ptCursor);
3005 pNextActiveView->SetSelection(ptCursor, ptCursor);
3009 * @brief Show "Go To" dialog and scroll views to line or diff.
3011 * Before dialog is opened, current line and file is determined
3013 * @note Conversions needed between apparent and real lines
3015 void CMergeEditView::OnWMGoto()
3018 CMergeDoc *pDoc = GetDocument();
3019 CPoint pos = GetCursorPos();
3023 nRealLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(pos.y);
3024 int nLineCount = pDoc->m_ptBuf[m_nThisPane]->GetLineCount();
3025 nLastLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(nLineCount - 1);
3027 // Set active file and current line selected in dialog
3028 dlg.m_strParam = strutils::to_str(nRealLine + 1);
3029 dlg.m_nFile = (pDoc->m_nBuffers < 3) ? (m_nThisPane == 1 ? 2 : 0) : m_nThisPane;
3030 dlg.m_nGotoWhat = 0;
3032 if (dlg.DoModal() == IDOK)
3034 CMergeDoc * pDoc1 = GetDocument();
3035 CMergeEditView * pCurrentView = nullptr;
3038 pCurrentView = GetGroupView(m_nThisPane);
3041 try { num = std::stoi(dlg.m_strParam) - 1; } catch(...) {}
3043 if (dlg.m_nGotoWhat == 0)
3045 int nRealLine1 = num;
3048 if (nRealLine1 > nLastLine)
3049 nRealLine1 = nLastLine;
3051 GotoLine(nRealLine1, true, (pDoc1->m_nBuffers < 3) ? (dlg.m_nFile == 2 ? 1 : 0) : dlg.m_nFile);
3058 if (diff >= pDoc1->m_diffList.GetSize())
3059 diff = pDoc1->m_diffList.GetSize();
3061 pCurrentView->SelectDiff(diff, true, false);
3066 void CMergeEditView::OnShellMenu()
3068 CFrameWnd *pFrame = GetTopLevelFrame();
3069 ASSERT(pFrame != nullptr);
3070 BOOL bAutoMenuEnableOld = pFrame->m_bAutoMenuEnable;
3071 pFrame->m_bAutoMenuEnable = FALSE;
3073 String path = GetDocument()->m_filePaths[m_nThisPane];
3074 std::unique_ptr<CShellContextMenu> pContextMenu(new CShellContextMenu(0x9000, 0x9FFF));
3075 pContextMenu->Initialize();
3076 pContextMenu->AddItem(paths::GetParentPath(path), paths::FindFileName(path));
3077 pContextMenu->RequeryShellContextMenu();
3079 ::GetCursorPos(&point);
3080 HWND hWnd = GetSafeHwnd();
3081 BOOL nCmd = TrackPopupMenu(pContextMenu->GetHMENU(), TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, 0, hWnd, nullptr);
3083 pContextMenu->InvokeCommand(nCmd, hWnd);
3084 pContextMenu->ReleaseShellContextMenu();
3086 pFrame->m_bAutoMenuEnable = bAutoMenuEnableOld;
3089 void CMergeEditView::OnUpdateShellMenu(CCmdUI* pCmdUI)
3091 pCmdUI->Enable(!GetDocument()->m_filePaths[m_nThisPane].empty());
3095 * @brief Reload options.
3097 void CMergeEditView::RefreshOptions()
3099 RENDERING_MODE nRenderingMode = static_cast<RENDERING_MODE>(GetOptionsMgr()->GetInt(OPT_RENDERING_MODE));
3100 SetRenderingMode(nRenderingMode);
3102 m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
3104 if (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0)
3105 SetInsertTabs(true);
3107 SetInsertTabs(false);
3109 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3111 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
3112 SetTextType(CCrystalTextView::SRC_PLAIN);
3114 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3115 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3117 SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3118 SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE),
3119 GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3120 GetDocument()->IsMixedEOL(m_nThisPane));
3122 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
3125 void CMergeEditView::OnScripts(UINT nID )
3127 // text is CHAR if compiled without UNICODE, WCHAR with UNICODE
3128 String text = GetSelectedText();
3130 // transform the text with a script/ActiveX function, event=EDITOR_SCRIPT
3131 bool bChanged = FileTransform::Interactive(text, L"EDITOR_SCRIPT", nID - ID_SCRIPT_FIRST);
3133 // now replace the text
3134 ReplaceSelection(text.c_str(), text.length(), 0);
3138 * @brief Called when an editor script item is updated
3140 void CMergeEditView::OnUpdateNoEditScripts(CCmdUI* pCmdUI)
3142 // append the scripts submenu
3143 HMENU scriptsSubmenu = dynamic_cast<CMainFrame*>(AfxGetMainWnd())->GetScriptsSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu);
3144 if (scriptsSubmenu != nullptr)
3145 createScriptsSubmenu(scriptsSubmenu);
3147 pCmdUI->Enable(true);
3151 * @brief Called when an editor script item is updated
3153 void CMergeEditView::OnUpdatePrediffer(CCmdUI* pCmdUI)
3155 pCmdUI->Enable(true);
3157 CMergeDoc *pd = GetDocument();
3158 ASSERT(pd != nullptr);
3159 PrediffingInfo prediffer;
3160 pd->GetPrediffer(&prediffer);
3162 if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
3164 pCmdUI->SetRadio(false);
3168 // Detect when CDiffWrapper::RunFileDiff has canceled a buggy prediffer
3169 if (prediffer.m_PluginName.empty())
3170 m_CurrentPredifferID = ID_NO_PREDIFFER;
3172 pCmdUI->SetRadio(pCmdUI->m_nID == static_cast<UINT>(m_CurrentPredifferID));
3176 * @brief Update "Prediffer" menuitem
3178 void CMergeEditView::OnUpdateNoPrediffer(CCmdUI* pCmdUI)
3180 // recreate the sub menu (to fill the "selected prediffers")
3181 GetMainFrame()->UpdatePrediffersMenu();
3185 void CMergeEditView::OnNoPrediffer()
3187 OnPrediffer(ID_NO_PREDIFFER);
3190 * @brief Handler for all prediffer choices, including ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, ID_NO_PREDIFFER, & specific prediffers
3192 void CMergeEditView::OnPrediffer(UINT nID )
3194 CMergeDoc *pd = GetDocument();
3195 ASSERT(pd != nullptr);
3197 SetPredifferByMenu(nID);
3198 pd->FlushAndRescan(true);
3202 * @brief Handler for all prediffer choices.
3203 * Prediffer choises include ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO,
3204 * ID_NO_PREDIFFER, & specific prediffers.
3206 void CMergeEditView::SetPredifferByMenu(UINT nID )
3208 CMergeDoc *pd = GetDocument();
3209 ASSERT(pd != nullptr);
3211 if (nID == ID_NO_PREDIFFER)
3213 m_CurrentPredifferID = nID;
3214 // All flags are set correctly during the construction
3215 PrediffingInfo *infoPrediffer = new PrediffingInfo;
3216 infoPrediffer->m_PluginOrPredifferMode = PLUGIN_MANUAL;
3217 infoPrediffer->m_PluginName.clear();
3218 pd->SetPrediffer(infoPrediffer);
3219 pd->FlushAndRescan(true);
3223 // get the scriptlet files
3224 PluginArray * piScriptArray =
3225 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3226 PluginArray * piScriptArray2 =
3227 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3229 // build a PrediffingInfo structure fom the ID
3230 PrediffingInfo prediffer;
3231 prediffer.m_PluginOrPredifferMode = PLUGIN_MANUAL;
3233 size_t pluginNumber = nID - ID_PREDIFFERS_FIRST;
3234 if (pluginNumber < piScriptArray->size())
3236 const PluginInfoPtr & plugin = piScriptArray->at(pluginNumber);
3237 prediffer.m_PluginName = plugin->m_name;
3241 pluginNumber -= piScriptArray->size();
3242 if (pluginNumber >= piScriptArray2->size())
3244 const PluginInfoPtr & plugin = piScriptArray2->at(pluginNumber);
3245 prediffer.m_PluginName = plugin->m_name;
3248 // update data for the radio button
3249 m_CurrentPredifferID = nID;
3251 // update the prediffer and rescan
3252 pd->SetPrediffer(&prediffer);
3256 * @brief Look through available prediffers, and return ID of requested one, if found
3258 int CMergeEditView::FindPrediffer(LPCTSTR prediffer) const
3261 int ID = ID_PREDIFFERS_FIRST;
3263 // Search file prediffers
3264 PluginArray * piScriptArray =
3265 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3266 for (i=0; i<piScriptArray->size(); ++i, ++ID)
3268 const PluginInfoPtr & plugin = piScriptArray->at(i);
3269 if (plugin->m_name == prediffer)
3273 // Search buffer prediffers
3274 PluginArray * piScriptArray2 =
3275 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3276 for (i=0; i<piScriptArray2->size(); ++i, ++ID)
3278 const PluginInfoPtr & plugin = piScriptArray2->at(i);
3279 if (plugin->m_name == prediffer)
3287 * @brief Look through available prediffers, and return ID of requested one, if found
3289 bool CMergeEditView::SetPredifferByName(const CString & prediffer)
3291 int id = FindPrediffer(prediffer);
3292 if (id<0) return false;
3293 SetPredifferByMenu(id);
3298 * @brief Goto given line.
3299 * @param [in] nLine Destination linenumber
3300 * @param [in] bRealLine if true linenumber is real line, otherwise
3301 * it is apparent line (including deleted lines)
3302 * @param [in] pane Pane index of goto target pane (0 = left, 1 = right).
3304 void CMergeEditView::GotoLine(UINT nLine, bool bRealLine, int pane)
3306 CMergeDoc *pDoc = GetDocument();
3307 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3308 CMergeEditView *pCurrentView = nullptr;
3309 if (pSplitterWnd != nullptr)
3310 pCurrentView = static_cast<CMergeEditView*>
3311 (pSplitterWnd->GetActivePane());
3313 int nRealLine = nLine;
3314 int nApparentLine = nLine;
3316 // Compute apparent (shown linenumber) line
3319 if (nRealLine > pDoc->m_ptBuf[pane]->GetLineCount() - 1)
3320 nRealLine = pDoc->m_ptBuf[pane]->GetLineCount() - 1;
3322 nApparentLine = pDoc->m_ptBuf[pane]->ComputeApparentLine(nRealLine);
3326 ptPos.y = nApparentLine;
3328 // Scroll line to center of view
3329 int nScrollLine = GetSubLineIndex(nApparentLine);
3330 nScrollLine -= GetScreenLines() / 2;
3331 if (nScrollLine < 0)
3334 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3336 CMergeEditView *pView = GetGroupView(nPane);
3337 pView->ScrollToSubLine(nScrollLine);
3338 if (ptPos.y < pView->GetLineCount())
3340 pView->SetCursorPos(ptPos);
3341 pView->SetAnchor(ptPos);
3345 CPoint ptPos1(0, pView->GetLineCount() - 1);
3346 pView->SetCursorPos(ptPos1);
3347 pView->SetAnchor(ptPos1);
3351 // If goto target is another view - activate another view.
3352 // This is done for user convenience as user probably wants to
3353 // work with goto target file.
3354 if (GetGroupView(pane) != pCurrentView)
3356 if (pSplitterWnd != nullptr)
3358 if (pSplitterWnd->GetColumnCount() > 1)
3359 pSplitterWnd->SetActivePane(0, pane);
3361 pSplitterWnd->SetActivePane(pane, 0);
3367 * @brief Check for horizontal scroll. Re-route to CSplitterEx if not from
3370 void CMergeEditView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3372 if (pScrollBar == nullptr)
3374 // Scroll did not come frome a scroll bar
3375 // Find the appropriate scroll bar
3376 // and send the message to the splitter window instead
3377 // The event should eventually come back here but with a valid scrollbar
3378 // Along the way it will be propagated to other windows that need it
3379 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3380 CScrollBar* curBar = this->GetScrollBarCtrl(SB_HORZ);
3381 pSplitterWnd->SendMessage(WM_HSCROLL,
3382 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3385 CCrystalTextView::OnHScroll (nSBCode, nPos, pScrollBar);
3389 * @brief When view is scrolled using scrollbars update location pane.
3391 void CMergeEditView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3393 if (pScrollBar == nullptr)
3395 // Scroll did not come frome a scroll bar
3396 // Find the appropriate scroll bar
3397 // and send the message to the splitter window instead
3398 // The event should eventually come back here but with a valid scrollbar
3399 // Along the way it will be propagated to other windows that need it
3400 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3401 CScrollBar* curBar = this->GetScrollBarCtrl(SB_VERT);
3402 pSplitterWnd->SendMessage(WM_VSCROLL,
3403 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3406 CCrystalTextView::OnVScroll (nSBCode, nPos, pScrollBar);
3408 if (nSBCode == SB_ENDSCROLL)
3411 // Note we cannot use nPos because of its 16-bit nature
3412 SCROLLINFO si = {0};
3413 si.cbSize = sizeof (si);
3414 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3415 VERIFY (GetScrollInfo (SB_VERT, &si));
3417 // Get the current position of scroll box.
3418 int nCurPos = si.nPos;
3420 UpdateLocationViewPosition(nCurPos, nCurPos + GetScreenLines());
3424 * @brief Copy selected lines adding linenumbers.
3426 void CMergeEditView::OnEditCopyLineNumbers()
3434 CMergeDoc *pDoc = GetDocument();
3435 GetSelection(ptStart, ptEnd);
3437 // Get last selected line (having widest linenumber)
3438 int line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(ptEnd.y);
3439 size_t nNumWidth = strutils::to_str(line + 1).length();
3441 for (int i = ptStart.y; i <= ptEnd.y; i++)
3443 if (GetLineFlags(i) & LF_GHOST || (GetEnableHideLines() && (GetLineFlags(i) & LF_INVISIBLE)))
3446 // We need to convert to real linenumbers
3447 line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(i);
3449 // Insert spaces to align different width linenumbers (99, 100)
3450 strLine = GetLineText(i);
3451 CString sSpaces(' ', static_cast<int>(nNumWidth - strutils::to_str(line + 1).length()));
3454 strNumLine.Format(_T("%d: %s"), line + 1, (LPCTSTR)strLine);
3455 strText += strNumLine;
3457 PutToClipboard(strText, strText.GetLength(), m_bColumnSelection);
3460 void CMergeEditView::OnUpdateEditCopyLinenumbers(CCmdUI* pCmdUI)
3462 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
3466 * @brief Open active file with associated application.
3468 * First tries to open file using shell 'Edit' action, since that
3469 * action open scripts etc. to editor instead of running them. If
3470 * edit-action is not registered, 'Open' action is used.
3472 void CMergeEditView::OnOpenFile()
3474 CMergeDoc * pDoc = GetDocument();
3475 ASSERT(pDoc != nullptr);
3477 String sFileName = pDoc->m_filePaths[m_nThisPane];
3478 if (sFileName.empty())
3480 HINSTANCE rtn = ShellExecute(::GetDesktopWindow(), _T("edit"), sFileName.c_str(),
3481 0, 0, SW_SHOWNORMAL);
3482 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3483 rtn = ShellExecute(::GetDesktopWindow(), _T("open"), sFileName.c_str(),
3484 0, 0, SW_SHOWNORMAL);
3485 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3490 * @brief Open active file with app selection dialog
3492 void CMergeEditView::OnOpenFileWith()
3494 CMergeDoc * pDoc = GetDocument();
3495 ASSERT(pDoc != nullptr);
3497 String sFileName = pDoc->m_filePaths[m_nThisPane];
3498 if (sFileName.empty())
3502 if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH))
3504 sysdir.ReleaseBuffer();
3505 CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + sFileName.c_str();
3506 ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg,
3507 sysdir, SW_SHOWNORMAL);
3511 * @brief Open active file with external editor
3513 void CMergeEditView::OnOpenFileWithEditor()
3515 CMergeDoc * pDoc = GetDocument();
3516 ASSERT(pDoc != nullptr);
3518 String sFileName = pDoc->m_filePaths[m_nThisPane];
3519 if (sFileName.empty())
3522 int nRealLine = ComputeRealLine(GetCursorPos().y) + 1;
3523 theApp.OpenFileToExternalEditor(sFileName, nRealLine);
3527 * @brief Force repaint of the location pane.
3529 void CMergeEditView::RepaintLocationPane()
3531 // Must force recalculation due to caching of data in location pane.
3532 CLocationView *pLocationView = GetDocument()->GetLocationView();
3533 if (pLocationView != nullptr)
3534 pLocationView->ForceRecalculate();
3538 * @brief Enables/disables linediff (different color for diffs)
3540 void CMergeEditView::OnViewLineDiffs()
3542 bool bWordDiffHighlight = GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
3543 GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, !bWordDiffHighlight);
3545 // Call CMergeDoc RefreshOptions() to refresh *both* views
3546 CMergeDoc *pDoc = GetDocument();
3547 pDoc->RefreshOptions();
3548 pDoc->FlushAndRescan(true);
3551 void CMergeEditView::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
3553 pCmdUI->Enable(true);
3554 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
3558 * @brief Enables/disables line number
3560 void CMergeEditView::OnViewLineNumbers()
3562 GetOptionsMgr()->SaveOption(OPT_VIEW_LINENUMBERS, !GetViewLineNumbers());
3564 // Call CMergeDoc RefreshOptions() to refresh *both* views
3565 CMergeDoc *pDoc = GetDocument();
3566 pDoc->RefreshOptions();
3569 void CMergeEditView::OnUpdateViewLineNumbers(CCmdUI* pCmdUI)
3571 pCmdUI->Enable(true);
3572 pCmdUI->SetCheck(GetViewLineNumbers());
3576 * @brief Enables/disables word wrap
3578 void CMergeEditView::OnViewWordWrap()
3580 GetOptionsMgr()->SaveOption(OPT_WORDWRAP, !m_bWordWrap);
3582 // Call CMergeDoc RefreshOptions() to refresh *both* views
3583 CMergeDoc *pDoc = GetDocument();
3584 pDoc->RefreshOptions();
3585 pDoc->UpdateAllViews(this);
3590 void CMergeEditView::OnUpdateViewWordWrap(CCmdUI* pCmdUI)
3592 pCmdUI->Enable(true);
3593 pCmdUI->SetCheck(m_bWordWrap);
3596 void CMergeEditView::OnViewWhitespace()
3598 GetOptionsMgr()->SaveOption(OPT_VIEW_WHITESPACE, !GetViewTabs());
3600 // Call CMergeDoc RefreshOptions() to refresh *both* views
3601 CMergeDoc *pDoc = GetDocument();
3602 pDoc->RefreshOptions();
3605 void CMergeEditView::OnUpdateViewWhitespace(CCmdUI* pCmdUI)
3607 pCmdUI->SetCheck(GetViewTabs());
3610 void CMergeEditView::OnSize(UINT nType, int cx, int cy)
3612 if (!IsInitialized())
3615 CMergeDoc * pDoc = GetDocument();
3616 if (m_nThisPane < pDoc->m_nBuffers - 1)
3618 // To calculate subline index correctly
3619 // we have to invalidate line cache in all pane before calling the function related the subline.
3620 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3622 CMergeEditView *pView = GetGroupView(nPane);
3623 if (pView != nullptr)
3624 pView->InvalidateScreenRect(false);
3629 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3631 CMergeEditView *pView = GetGroupView(nPane);
3632 if (pView != nullptr)
3633 pView->Invalidate();
3636 // recalculate m_nTopSubLine
3637 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
3641 RecalcVertScrollBar (false, false);
3642 RecalcHorzScrollBar (false, false);
3646 * @brief allocates GDI resources for printing
3647 * @param pDC [in] points to the printer device context
3648 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3650 void CMergeEditView::OnBeginPrinting(CDC * pDC, CPrintInfo * pInfo)
3652 GetParentFrame()->PostMessage(WM_TIMER);
3654 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3656 CMergeEditView *pView = GetDocument()->GetView(m_nThisGroup, pane);
3657 pView->m_bPrintHeader = true;
3658 pView->m_bPrintFooter = true;
3659 pView->CGhostTextView::OnBeginPrinting(pDC, pInfo);
3664 * @brief frees GDI resources for printing
3665 * @param pDC [in] points to the printer device context
3666 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3668 void CMergeEditView::OnEndPrinting(CDC * pDC, CPrintInfo * pInfo)
3670 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3671 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::OnEndPrinting(pDC, pInfo);
3673 GetParentFrame()->PostMessage(WM_TIMER);
3677 * @brief Gets header text to print
3678 * @param [in] nPageNum the page number to print
3679 * @param [out] header text to print
3681 void CMergeEditView::GetPrintHeaderText(int nPageNum, CString & text)
3683 text = GetDocument()->GetTitle();
3687 * @brief Prints header
3688 * @param [in] nPageNum the page number to print
3690 void CMergeEditView::PrintHeader(CDC * pdc, int nPageNum)
3692 if (m_nThisPane > 0)
3694 int oldRight = m_rcPrintArea.right;
3695 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3696 CGhostTextView::PrintHeader(pdc, nPageNum);
3697 m_rcPrintArea.right = oldRight;
3701 * @brief Prints footer
3702 * @param [in] nPageNum the page number to print
3704 void CMergeEditView::PrintFooter(CDC * pdc, int nPageNum)
3706 if (m_nThisPane > 0)
3708 int oldRight = m_rcPrintArea.right;
3709 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3710 CGhostTextView::PrintFooter(pdc, nPageNum);
3711 m_rcPrintArea.right = oldRight;
3714 void CMergeEditView::RecalcPageLayouts (CDC * pDC, CPrintInfo * pInfo)
3716 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3717 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::RecalcPageLayouts(pDC, pInfo);
3721 * @brief Prints or previews both panes.
3722 * @param pDC [in] points to the printer device context
3723 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3725 void CMergeEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
3727 CRect rDraw = pInfo->m_rectDraw;
3728 CSize sz = rDraw.Size();
3729 CMergeDoc *pDoc = GetDocument();
3731 SIZE szLeftTop, szRightBottom;
3732 GetPrintMargins(szLeftTop.cx, szLeftTop.cy, szRightBottom.cx, szRightBottom.cy);
3733 pDC->HIMETRICtoLP(&szLeftTop);
3734 pDC->HIMETRICtoLP(&szRightBottom);
3736 int midX = (sz.cx - szLeftTop.cx - szRightBottom.cx) / pDoc->m_nBuffers;
3739 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
3741 pInfo->m_rectDraw.left = rDraw.left + midX * pane;
3742 pInfo->m_rectDraw.right = pInfo->m_rectDraw.left + midX + szLeftTop.cx + szRightBottom.cx;
3743 CMergeEditView* pPane = pDoc->GetView(m_nThisGroup, pane);
3744 pPane->CGhostTextView::OnPrint(pDC, pInfo);
3748 bool CMergeEditView::IsInitialized() const
3750 CMergeEditView * pThis = const_cast<CMergeEditView *>(this);
3751 CDiffTextBuffer * pBuffer = dynamic_cast<CDiffTextBuffer *>(pThis->LocateTextBuffer());
3752 return pBuffer->IsInitialized();
3756 * @brief returns the number of empty lines which are added for synchronizing the line in two/three panes.
3758 int CMergeEditView::GetEmptySubLines( int nLineIndex )
3760 int nBreaks[3] = {0};
3761 int nMaxBreaks = -1;
3762 CMergeDoc * pDoc = GetDocument();
3763 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3765 CMergeEditView *pView = GetGroupView(nPane);
3766 if (pView != nullptr)
3768 if (nLineIndex >= pView->GetLineCount())
3770 pView->WrapLineCached( nLineIndex, pView->GetScreenChars(), nullptr, nBreaks[nPane] );
3772 nMaxBreaks = max(nMaxBreaks, nBreaks[nPane]);
3775 if (nBreaks[m_nThisPane] < nMaxBreaks)
3776 return nMaxBreaks - nBreaks[m_nThisPane];
3782 * @brief Invalidate sub line index cache from the specified index to the end of file.
3783 * @param [in] nLineIndex Index of the first line to invalidate
3785 void CMergeEditView::InvalidateSubLineIndexCache( int nLineIndex )
3787 CMergeDoc * pDoc = GetDocument();
3788 ASSERT(pDoc != nullptr);
3790 // We have to invalidate sub line index cache on both panes.
3791 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3793 CMergeEditView *pView = GetGroupView(nPane);
3794 if (pView != nullptr)
3795 pView->CCrystalTextView::InvalidateSubLineIndexCache( nLineIndex );
3799 void CMergeEditView::SetWordWrapping( bool bWordWrap )
3801 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3802 GetGroupView(pane)->m_bWordWrap = bWordWrap;
3803 CCrystalTextView::SetWordWrapping(bWordWrap);
3807 * @brief Swap the positions of the two panes
3809 void CMergeEditView::OnViewSwapPanes()
3811 GetDocument()->SwapFiles();
3815 * @brief Determine if difference is visible on screen.
3816 * @param [in] nDiff Number of diff to check.
3817 * @return true if difference is visible.
3819 bool CMergeEditView::IsDiffVisible(int nDiff)
3821 const CMergeDoc *pd = GetDocument();
3824 pd->m_diffList.GetDiff(nDiff, diff);
3826 return IsDiffVisible(diff);
3830 * @brief Determine if difference is visible on screen.
3831 * @param [in] diff diff to check.
3832 * @param [in] nLinesBelow Allow "minimizing" the number of visible lines.
3833 * @return true if difference is visible, false otherwise.
3835 bool CMergeEditView::IsDiffVisible(const DIFFRANGE& diff, int nLinesBelow /*=0*/)
3837 const int nDiffStart = GetSubLineIndex(diff.dbegin);
3838 const int nDiffEnd = GetSubLineIndex(diff.dend);
3839 // Diff's height is last line - first line + last line's line count
3840 const int nDiffHeight = nDiffEnd - nDiffStart + GetSubLines(diff.dend) + 1;
3842 // If diff first line outside current view - context OR
3843 // if diff last line outside current view - context OR
3844 // if diff is bigger than screen
3845 if ((nDiffStart < m_nTopSubLine) ||
3846 (nDiffEnd >= m_nTopSubLine + GetScreenLines() - nLinesBelow) ||
3847 (nDiffHeight >= GetScreenLines()))
3857 /** @brief Open help from mainframe when user presses F1*/
3858 void CMergeEditView::OnHelp()
3860 theApp.ShowHelp(MergeViewHelpLocation);
3864 * @brief Called after document is loaded.
3865 * This function is called from CMergeDoc::OpenDocs() after documents are
3866 * loaded. So this is good place to set View's options etc.
3868 void CMergeEditView::DocumentsLoaded()
3870 // Enable/disable automatic rescan (rescanning after edit)
3871 EnableRescan(GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN));
3873 // SetTextType will revert to language dependent defaults for tab
3874 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
3875 SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3876 const bool mixedEOLs = GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3877 GetDocument()->IsMixedEOL(m_nThisPane);
3878 SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE), mixedEOLs);
3879 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3880 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3881 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3883 // Enable Backspace at beginning of line
3884 SetDisableBSAtSOL(false);
3886 // Set tab type (tabs/spaces)
3887 bool bInsertTabs = (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0);
3888 SetInsertTabs(bInsertTabs);
3890 // Sometimes WinMerge doesn't update scrollbars correctly (they remain
3891 // disabled) after docs are open in screen. So lets make sure they are
3892 // really updated, even though this is unnecessary in most cases.
3893 RecalcHorzScrollBar();
3894 RecalcVertScrollBar();
3898 * @brief Update LocationView position.
3899 * This function updates LocationView position to given lines.
3900 * Usually we want to lines in file compare view and area in
3901 * LocationView to match. Be extra carefull to not call non-existing
3903 * @param [in] nTopLine Top line of current view.
3904 * @param [in] nBottomLine Bottom line of current view.
3906 void CMergeEditView::UpdateLocationViewPosition(int nTopLine /*=-1*/,
3907 int nBottomLine /*= -1*/)
3909 CMergeDoc *pDoc = GetDocument();
3910 if (pDoc == nullptr)
3913 CLocationView *pLocationView = pDoc->GetLocationView();
3915 if (pLocationView != nullptr && IsWindow(pLocationView->GetSafeHwnd()))
3917 pLocationView->UpdateVisiblePos(nTopLine, nBottomLine);
3922 * @brief Enable/Disable view's selection margins.
3923 * Selection margins show bookmarks and word-wrap symbols, so they are pretty
3924 * useful. But it appears many users don't use/need those features and for them
3925 * selection margins are just wasted screen estate.
3927 void CMergeEditView::OnViewMargin()
3929 bool bViewMargin = GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN);
3930 GetOptionsMgr()->SaveOption(OPT_VIEW_FILEMARGIN, !bViewMargin);
3932 SetSelectionMargin(!bViewMargin);
3933 CMergeDoc *pDoc = GetDocument();
3934 pDoc->RefreshOptions();
3935 pDoc->UpdateAllViews(this);
3939 * @brief Update GUI for Enable/Disable view's selection margin.
3940 * @param [in] pCmdUI Pointer to UI item to update.
3942 void CMergeEditView::OnUpdateViewMargin(CCmdUI* pCmdUI)
3944 pCmdUI->Enable(true);
3945 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3949 * @brief Create the "Change Scheme" sub menu.
3950 * @param [in] pCmdUI Pointer to UI item to update.
3952 void CMergeEditView::OnUpdateViewChangeScheme(CCmdUI *pCmdUI)
3954 // Delete the place holder menu.
3955 pCmdUI->m_pSubMenu->DeleteMenu(0, MF_BYPOSITION);
3957 const HMENU hSubMenu = pCmdUI->m_pSubMenu->m_hMenu;
3959 String name = theApp.LoadString(ID_COLORSCHEME_FIRST);
3960 AppendMenu(hSubMenu, MF_STRING, ID_COLORSCHEME_FIRST, name.c_str());
3961 AppendMenu(hSubMenu, MF_SEPARATOR, 0, nullptr);
3963 for (int i = ID_COLORSCHEME_FIRST + 1; i <= ID_COLORSCHEME_LAST; ++i)
3965 name = theApp.LoadString(i);
3966 AppendMenu(hSubMenu, MF_STRING, i, name.c_str());
3969 pCmdUI->Enable(true);
3973 * @brief Change the editor's syntax highlighting scheme.
3974 * @param [in] nID Selected color scheme sub menu id.
3976 void CMergeEditView::OnChangeScheme(UINT nID)
3978 CMergeDoc *pDoc = GetDocument();
3979 ASSERT(pDoc != nullptr);
3981 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3983 CMergeEditView *pView = GetGroupView(nPane);
3984 ASSERT(pView != nullptr);
3986 if (pView != nullptr)
3988 pView->SetTextType(CCrystalTextView::TextType(nID - ID_COLORSCHEME_FIRST));
3989 pView->SetDisableBSAtSOL(false);
3993 pDoc->UpdateAllViews(nullptr);
3997 * @brief Enable all color schemes sub menu items.
3998 * @param [in] pCmdUI Pointer to UI item to update.
4000 void CMergeEditView::OnUpdateChangeScheme(CCmdUI* pCmdUI)
4002 const bool bIsCurrentScheme = (static_cast<UINT>(m_CurSourceDef->type) == (pCmdUI->m_nID - ID_COLORSCHEME_FIRST));
4003 pCmdUI->SetRadio(bIsCurrentScheme);
4004 pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT));
4008 * @brief Called when mouse's wheel is scrolled.
4010 BOOL CMergeEditView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
4012 if ( nFlags == MK_CONTROL )
4014 short amount = zDelta < 0 ? -1: 1;
4017 // no default CCrystalTextView
4018 return CView::OnMouseWheel(nFlags, zDelta, pt);
4021 if (nFlags == MK_SHIFT)
4023 SCROLLINFO si = { sizeof SCROLLINFO };
4024 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
4026 VERIFY(GetScrollInfo(SB_HORZ, &si));
4029 si.nPos -= zDelta / 40;
4030 if (si.nPos > si.nMax) si.nPos = si.nMax;
4031 if (si.nPos < si.nMin) si.nPos = si.nMin;
4033 SetScrollInfo(SB_HORZ, &si);
4036 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, si.nPos) , NULL );
4038 // no default CCrystalTextView
4039 return CView::OnMouseWheel(nFlags, zDelta, pt);
4042 return CGhostTextView::OnMouseWheel(nFlags, zDelta, pt);
4046 * @brief Change font size (zoom) in views.
4047 * @param [in] amount Amount of change/zoom, negative number makes
4048 * font smaller, positive number bigger and 0 reset the font size.
4050 void CMergeEditView::ZoomText(short amount)
4055 const int nLogPixelsY = CClientDC(this).GetDeviceCaps(LOGPIXELSY);
4056 int nPointSize = -MulDiv(lf.lfHeight, 72, nLogPixelsY);
4060 nPointSize = -MulDiv(GetOptionsMgr()->GetInt(OPT_FONT_FILECMP + OPT_FONT_HEIGHT), 72, nLogPixelsY);
4063 nPointSize += amount;
4067 lf.lfHeight = -MulDiv(nPointSize, nLogPixelsY, 72);
4069 CMergeDoc *pDoc = GetDocument();
4070 ASSERT(pDoc != nullptr);
4072 if (pDoc != nullptr)
4074 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
4076 CMergeEditView *pView = GetGroupView(nPane);
4077 ASSERT(pView != nullptr);
4079 if (pView != nullptr)
4087 bool CMergeEditView::QueryEditable()
4089 return m_bDetailView ? false : !GetDocument()->m_ptBuf[m_nThisPane]->GetReadOnly();
4093 * @brief Adjust the point to remain in the displayed diff
4095 * @return Tells if the point has been changed
4097 bool CMergeEditView::EnsureInDiff(CPoint& pt)
4099 int nLineCount = GetLineCount();
4100 if (m_lineBegin >= nLineCount)
4101 m_lineBegin = nLineCount - 1;
4102 if (m_lineEnd >= nLineCount)
4103 m_lineEnd = nLineCount - 1;
4105 int diffLength = m_lineEnd - m_lineBegin + 1;
4106 // first get the degenerate case out of the way
4108 if (diffLength == 0)
4110 if (pt.y == m_lineBegin && pt.x == 0)
4118 if (pt.y < m_lineBegin)
4124 // diff is defined and not below diff
4125 if (m_lineEnd > -1 && pt.y > m_lineEnd)
4128 pt.x = GetLineLength(pt.y);
4134 void CMergeEditView::EnsureVisible(CPoint pt)
4139 // ensure we remain in diff
4140 if (EnsureInDiff(ptNew))
4141 SetCursorPos(ptNew);
4143 CCrystalTextView::EnsureVisible(ptNew);
4146 void CMergeEditView::EnsureVisible(CPoint ptStart, CPoint ptEnd)
4148 CCrystalTextView::EnsureVisible(ptStart, ptEnd);
4151 void CMergeEditView::SetSelection(const CPoint& ptStart, const CPoint& ptEnd, bool bUpdateView)
4153 CPoint ptStartNew = ptStart;
4154 CPoint ptEndNew = ptEnd;
4157 // ensure we remain in diff
4158 EnsureInDiff(ptStartNew);
4159 EnsureInDiff(ptEndNew);
4161 CCrystalTextView::SetSelection(ptStartNew, ptEndNew, bUpdateView);
4164 void CMergeEditView::ScrollToSubLine(int nNewTopLine, bool bNoSmoothScroll /*= FALSE*/, bool bTrackScrollBar /*= TRUE*/)
4168 int nLineCount = GetLineCount();
4169 if (m_lineBegin >= nLineCount)
4170 m_lineBegin = nLineCount - 1;
4171 if (m_lineEnd >= nLineCount)
4172 m_lineEnd = nLineCount - 1;
4174 // ensure we remain in diff
4175 int sublineBegin = GetSubLineIndex(m_lineBegin);
4176 int sublineEnd = m_lineEnd < 0 ? -1 : GetSubLineIndex(m_lineEnd) + GetSubLines(m_lineEnd) - 1;
4177 int diffLength = sublineEnd - sublineBegin + 1;
4178 int displayLength = GetScreenLines();
4179 if (diffLength <= displayLength)
4180 nNewTopLine = sublineBegin;
4183 if (nNewTopLine < sublineBegin)
4184 nNewTopLine = sublineBegin;
4185 if (nNewTopLine + displayLength - 1 > sublineEnd)
4186 nNewTopLine = GetSubLineIndex(sublineEnd - displayLength + 1);
4189 CPoint pt = GetCursorPos();
4190 if (EnsureInDiff(pt))
4193 CPoint ptSelStart, ptSelEnd;
4194 GetSelection(ptSelStart, ptSelEnd);
4195 if (EnsureInDiff(ptSelStart) || EnsureInDiff(ptSelEnd))
4196 SetSelection(ptSelStart, ptSelEnd);
4198 CCrystalTextView::ScrollToSubLine(nNewTopLine, bNoSmoothScroll, bTrackScrollBar);
4202 * @brief Called when user selects View/Zoom In from menu.
4204 void CMergeEditView::OnViewZoomIn()
4210 * @brief Called when user selects View/Zoom Out from menu.
4212 void CMergeEditView::OnViewZoomOut()
4218 * @brief Called when user selects View/Zoom Normal from menu.
4220 void CMergeEditView::OnViewZoomNormal()
4225 void CMergeEditView::OnDropFiles(const std::vector<String>& tFiles)
4227 if (tFiles.size() > 1 || paths::IsDirectory(tFiles[0]))
4229 GetMainFrame()->GetDropHandler()->GetCallback()(tFiles);
4233 GetDocument()->ChangeFile(m_nThisPane, tFiles[0]);
4236 void CMergeEditView::OnWindowSplit()
4239 auto& wndSplitter = dynamic_cast<CMergeEditFrame *>(GetParentFrame())->GetSplitter();
4240 CMergeDoc *pDoc = GetDocument();
4241 CMergeEditView *pView = pDoc->GetView(0, m_nThisPane);
4242 auto* pwndSplitterChild = pView->GetParentSplitter(pView, false);
4243 int nBuffer = m_nThisPane;
4244 if (pDoc->m_nGroups <= 2)
4246 wndSplitter.SplitRow(1);
4247 wndSplitter.EqualizeRows();
4251 wndSplitter.SetActivePane(0, 0);
4252 wndSplitter.DeleteRow(1);
4253 if (pwndSplitterChild->GetColumnCount() > 1)
4254 pwndSplitterChild->SetActivePane(0, nBuffer);
4256 pwndSplitterChild->SetActivePane(nBuffer, 0);
4260 void CMergeEditView::OnUpdateWindowSplit(CCmdUI* pCmdUI)
4262 pCmdUI->Enable(!m_bDetailView);
4263 pCmdUI->SetCheck(GetDocument()->m_nGroups > 2);