1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
5 // SPDX-License-Identifier: GPL-2.0-or-later
6 /////////////////////////////////////////////////////////////////////////////
8 * @file MergeEditView.cpp
10 * @brief Implementation of the CMergeEditView class
14 #include "MergeEditView.h"
18 #include "LocationView.h"
21 #include "OptionsMgr.h"
22 #include "OptionsDiffColors.h"
23 #include "FileTransform.h"
25 #include "WMGotoDlg.h"
26 #include "OptionsDef.h"
27 #include "SyntaxColors.h"
28 #include "MergeEditFrm.h"
29 #include "MergeLineFlags.h"
31 #include "DropHandler.h"
33 #include "ShellContextMenu.h"
40 #ifndef WM_MOUSEHWHEEL
41 # define WM_MOUSEHWHEEL 0x20e
45 using CrystalLineParser::TEXTBLOCK;
47 /** @brief Timer ID for delayed rescan. */
48 const UINT IDT_RESCAN = 2;
49 /** @brief Timer timeout for delayed rescan. */
50 const UINT RESCAN_TIMEOUT = 1000;
52 /** @brief Location for file compare specific help to open. */
53 static TCHAR MergeViewHelpLocation[] = _T("::/htmlhelp/Compare_files.html");
55 /////////////////////////////////////////////////////////////////////////////
58 IMPLEMENT_DYNCREATE(CMergeEditView, CCrystalEditViewEx)
60 CMergeEditView::CMergeEditView()
61 : m_bCurrentLineIsDiff(false)
64 , m_bDetailView(false)
65 , m_piMergeEditStatus(nullptr)
66 , m_bAutomaticRescan(false)
67 , fTimerWaitingForIdle(0)
70 , m_CurrentPredifferID(0)
72 SetParser(&m_xParser);
74 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
77 CMergeEditView::~CMergeEditView()
82 BEGIN_MESSAGE_MAP(CMergeEditView, CCrystalEditViewEx)
83 //{{AFX_MSG_MAP(CMergeEditView)
84 ON_COMMAND(ID_CURDIFF, OnCurdiff)
85 ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
86 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
87 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
88 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
89 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
90 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
91 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
92 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
93 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
94 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
95 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
96 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
97 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
98 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
99 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
100 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
101 ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
102 ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
103 ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
104 ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
105 ON_COMMAND(ID_NEXTDIFFLM, OnNextdiffLM)
106 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLM, OnUpdateNextdiffLM)
107 ON_COMMAND(ID_PREVDIFFLM, OnPrevdiffLM)
108 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLM, OnUpdatePrevdiffLM)
109 ON_COMMAND(ID_NEXTDIFFLR, OnNextdiffLR)
110 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLR, OnUpdateNextdiffLR)
111 ON_COMMAND(ID_PREVDIFFLR, OnPrevdiffLR)
112 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLR, OnUpdatePrevdiffLR)
113 ON_COMMAND(ID_NEXTDIFFMR, OnNextdiffMR)
114 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMR, OnUpdateNextdiffMR)
115 ON_COMMAND(ID_PREVDIFFMR, OnPrevdiffMR)
116 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMR, OnUpdatePrevdiffMR)
117 ON_COMMAND(ID_NEXTDIFFLO, OnNextdiffLO)
118 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLO, OnUpdateNextdiffLO)
119 ON_COMMAND(ID_PREVDIFFLO, OnPrevdiffLO)
120 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLO, OnUpdatePrevdiffLO)
121 ON_COMMAND(ID_NEXTDIFFMO, OnNextdiffMO)
122 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMO, OnUpdateNextdiffMO)
123 ON_COMMAND(ID_PREVDIFFMO, OnPrevdiffMO)
124 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMO, OnUpdatePrevdiffMO)
125 ON_COMMAND(ID_NEXTDIFFRO, OnNextdiffRO)
126 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFRO, OnUpdateNextdiffRO)
127 ON_COMMAND(ID_PREVDIFFRO, OnPrevdiffRO)
128 ON_UPDATE_COMMAND_UI(ID_PREVDIFFRO, OnUpdatePrevdiffRO)
129 ON_WM_LBUTTONDBLCLK()
132 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
133 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
134 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
135 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
136 ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
137 ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
138 ON_COMMAND(ID_L2R, OnL2r)
139 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
140 ON_COMMAND(ID_R2L, OnR2l)
141 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
142 ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
143 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
144 ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
145 ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
146 ON_COMMAND(ID_ADD_SYNCPOINT, OnAddSyncPoint)
147 ON_COMMAND(ID_CLEAR_SYNCPOINTS, OnClearSyncPoints)
148 ON_UPDATE_COMMAND_UI(ID_CLEAR_SYNCPOINTS, OnUpdateClearSyncPoints)
149 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
150 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
151 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
153 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
154 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
155 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
156 ON_COMMAND(ID_REFRESH, OnRefresh)
157 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
158 ON_COMMAND(ID_SELECTLINEDIFF, OnSelectLineDiff<false>)
159 ON_UPDATE_COMMAND_UI(ID_SELECTLINEDIFF, OnUpdateSelectLineDiff)
160 ON_COMMAND(ID_SELECTPREVLINEDIFF, OnSelectLineDiff<true>)
161 ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
163 ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateEditReplace)
164 ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
165 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
166 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
167 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
168 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
169 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
170 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_RO, OnUpdateStatusRO)
171 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_RO, OnUpdateStatusRO)
172 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_RO, OnUpdateStatusRO)
173 ON_COMMAND_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnConvertEolTo)
174 ON_UPDATE_COMMAND_UI_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnUpdateConvertEolTo)
175 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_EOL, OnUpdateStatusEOL)
176 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_EOL, OnUpdateStatusEOL)
177 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_EOL, OnUpdateStatusEOL)
178 ON_COMMAND(ID_L2RNEXT, OnL2RNext)
179 ON_UPDATE_COMMAND_UI(ID_L2RNEXT, OnUpdateL2RNext)
180 ON_COMMAND(ID_R2LNEXT, OnR2LNext)
181 ON_UPDATE_COMMAND_UI(ID_R2LNEXT, OnUpdateR2LNext)
182 ON_COMMAND(ID_WINDOW_CHANGE_PANE, OnChangePane)
183 ON_COMMAND(ID_NEXT_PANE, OnChangePane)
184 ON_COMMAND(ID_EDIT_WMGOTO, OnWMGoto)
185 ON_COMMAND(ID_FILE_SHELLMENU, OnShellMenu)
186 ON_UPDATE_COMMAND_UI(ID_FILE_SHELLMENU, OnUpdateShellMenu)
187 ON_COMMAND_RANGE(ID_SCRIPT_FIRST, ID_SCRIPT_LAST, OnScripts)
188 ON_COMMAND(ID_NO_PREDIFFER, OnNoPrediffer)
189 ON_UPDATE_COMMAND_UI(ID_NO_PREDIFFER, OnUpdateNoPrediffer)
190 ON_COMMAND_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnPrediffer)
191 ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnUpdatePrediffer)
194 ON_COMMAND(ID_EDIT_COPY_LINENUMBERS, OnEditCopyLineNumbers)
195 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY_LINENUMBERS, OnUpdateEditCopyLinenumbers)
196 ON_COMMAND(ID_VIEW_LINEDIFFS, OnViewLineDiffs)
197 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewLineDiffs)
198 ON_COMMAND(ID_VIEW_WORDWRAP, OnViewWordWrap)
199 ON_UPDATE_COMMAND_UI(ID_VIEW_WORDWRAP, OnUpdateViewWordWrap)
200 ON_COMMAND(ID_VIEW_LINENUMBERS, OnViewLineNumbers)
201 ON_UPDATE_COMMAND_UI(ID_VIEW_LINENUMBERS, OnUpdateViewLineNumbers)
202 ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace)
203 ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace)
204 ON_COMMAND(ID_FILE_OPEN_REGISTERED, OnOpenFile)
205 ON_COMMAND(ID_FILE_OPEN_WITHEDITOR, OnOpenFileWithEditor)
206 ON_COMMAND(ID_FILE_OPEN_WITH, OnOpenFileWith)
207 ON_COMMAND(ID_VIEW_SWAPPANES, OnViewSwapPanes)
208 ON_UPDATE_COMMAND_UI(ID_NO_EDIT_SCRIPTS, OnUpdateNoEditScripts)
211 ON_COMMAND(ID_HELP, OnHelp)
212 ON_COMMAND(ID_VIEW_SELMARGIN, OnViewMargin)
213 ON_UPDATE_COMMAND_UI(ID_VIEW_SELMARGIN, OnUpdateViewMargin)
214 ON_UPDATE_COMMAND_UI(ID_VIEW_CHANGESCHEME, OnUpdateViewChangeScheme)
215 ON_COMMAND_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnChangeScheme)
216 ON_UPDATE_COMMAND_UI_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnUpdateChangeScheme)
219 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
220 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
221 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
222 ON_COMMAND(ID_WINDOW_SPLIT, OnWindowSplit)
223 ON_UPDATE_COMMAND_UI(ID_WINDOW_SPLIT, OnUpdateWindowSplit)
228 /////////////////////////////////////////////////////////////////////////////
229 // CMergeEditView diagnostics
232 CMergeDoc* CMergeEditView::GetDocument() // non-debug version is inline
234 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMergeDoc)));
235 return (CMergeDoc*)m_pDocument;
240 /////////////////////////////////////////////////////////////////////////////
241 // CMergeEditView message handlers
244 * @brief Return text buffer for file in view
246 CCrystalTextBuffer *CMergeEditView::LocateTextBuffer()
248 return GetDocument()->m_ptBuf[m_nThisPane].get();
252 * @brief Update any resources necessary after a GUI language change
254 void CMergeEditView::UpdateResources()
258 CMergeEditView *CMergeEditView::GetGroupView(int nBuffer) const
260 return GetDocument()->GetView(m_nThisGroup, nBuffer);
263 void CMergeEditView::PrimeListWithFile()
265 // Set the tab size now, just in case the options change...
266 // We don't update it at the end of OnOptions,
267 // we can update it safely now
268 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
271 * @brief Return text from line given
273 CString CMergeEditView::GetLineText(int idx)
275 return GetLineChars(idx);
279 * @brief Return text from selection
281 CString CMergeEditView::GetSelectedText()
283 CPoint ptStart, ptEnd;
285 GetSelection(ptStart, ptEnd);
286 if (ptStart != ptEnd)
287 GetTextWithoutEmptys(ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, strText);
292 * @brief Get diffs inside selection.
293 * @param [out] firstDiff First diff inside selection
294 * @param [out] lastDiff Last diff inside selection
295 * @note -1 is returned in parameters if diffs cannot be determined
296 * @todo This shouldn't be called when there is no diffs, so replace
297 * first 'if' with ASSERT()?
299 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff)
304 CMergeDoc *pd = GetDocument();
305 const int nDiffs = pd->m_diffList.GetSignificantDiffs();
309 int firstLine, lastLine;
310 GetFullySelectedLines(firstLine, lastLine);
311 if (lastLine < firstLine)
314 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
315 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
316 if (firstDiff != -1 && lastDiff != -1)
320 // Check that first selected line is first diff's first line or above it
321 VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
322 if ((int)di.dbegin < firstLine)
324 if (firstDiff < lastDiff)
328 // Check that last selected line is last diff's last line or below it
329 VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
330 if ((int)di.dend > lastLine)
332 if (firstDiff < lastDiff)
336 // Special case: one-line diff is not selected if cursor is in it
337 if (firstLine == lastLine)
346 * @brief Get diffs inside selection.
347 * @param [out] firstDiff First diff inside selection
348 * @param [out] lastDiff Last diff inside selection
349 * @param [out] firstWordDiff First word level diff inside selection
350 * @param [out] lastWordDiff Last word level diff inside selection
351 * @note -1 is returned in parameters if diffs cannot be determined
352 * @todo This shouldn't be called when there is no diffs, so replace
353 * first 'if' with ASSERT()?
355 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff, int & firstWordDiff, int & lastWordDiff, const CPoint *pptStart, const CPoint *pptEnd)
362 CMergeDoc *pd = GetDocument();
363 const int nDiffs = pd->m_diffList.GetSignificantDiffs();
367 int firstLine, lastLine;
368 CPoint ptStart, ptEnd;
369 GetSelection(ptStart, ptEnd);
370 if (pptStart != nullptr)
372 if (pptEnd != nullptr)
374 firstLine = ptStart.y;
377 firstDiff = pd->m_diffList.LineToDiff(firstLine);
378 bool firstLineIsNotInDiff = firstDiff == -1;
381 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
386 lastDiff = pd->m_diffList.LineToDiff(lastLine);
387 bool lastLineIsNotInDiff = lastDiff == -1;
389 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
390 if (lastDiff < firstDiff)
397 if (firstDiff != -1 && lastDiff != -1)
401 if (pd->EqualCurrentWordDiff(m_nThisPane, ptStart, ptEnd))
403 firstWordDiff = lastWordDiff = static_cast<int>(pd->GetCurrentWordDiff().nWordDiff);
405 else if (ptStart != ptEnd)
407 VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
408 if (lastLineIsNotInDiff && (firstLineIsNotInDiff || (di.dbegin == firstLine && ptStart.x == 0)))
414 if (firstWordDiff == -1)
416 vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(firstDiff);
417 for (size_t i = 0; i < worddiffs.size(); ++i)
419 int worddiffLen = worddiffs[i].end[m_nThisPane] - worddiffs[i].begin[m_nThisPane];
420 if (worddiffs[i].endline[m_nThisPane] > firstLine ||
421 (firstLine == worddiffs[i].endline[m_nThisPane] &&
422 worddiffs[i].end[m_nThisPane] - (worddiffLen == 0 ? 0 : 1) >= ptStart.x))
424 firstWordDiff = static_cast<int>(i);
429 if (firstLine >= di.dbegin && firstWordDiff == -1)
436 VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
437 vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(lastDiff);
438 for (size_t i = worddiffs.size() - 1; i != (size_t)-1; --i)
440 if (worddiffs[i].beginline[m_nThisPane] < lastLine ||
441 (lastLine == worddiffs[i].beginline[m_nThisPane] && worddiffs[i].begin[m_nThisPane] + 1 <= ptEnd.x))
443 lastWordDiff = static_cast<int>(i);
448 if (lastLine <= di.dend && lastWordDiff == -1)
451 if (firstDiff == lastDiff && (lastWordDiff != -1 && lastWordDiff < firstWordDiff))
458 else if (lastDiff < firstDiff || (firstDiff == lastDiff && firstWordDiff == -1 && lastWordDiff == -1))
475 ASSERT(firstDiff == -1 ? (lastDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
476 ASSERT(lastDiff == -1 ? (firstDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
477 ASSERT(firstDiff != -1 ? firstWordDiff != -1 : true);
480 std::map<int, std::vector<int>> CMergeEditView::GetColumnSelectedWordDiffIndice()
482 CMergeDoc *pDoc = GetDocument();
483 std::map<int, std::vector<int>> ret;
484 std::map<int, std::vector<int> *> list;
485 CPoint ptStart, ptEnd;
486 GetSelection(ptStart, ptEnd);
487 for (int nLine = ptStart.y; nLine <= ptEnd.y; ++nLine)
489 if (pDoc->m_diffList.LineToDiff(nLine) != -1)
491 int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
493 GetColumnSelection(nLine, nLeft, nRight);
494 CPoint ptStart2, ptEnd2;
497 ptStart2.y = ptEnd2.y = nLine;
498 GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff, &ptStart2, &ptEnd2);
499 if (firstDiff != -1 && lastDiff != -1)
501 std::vector<int> *pWordDiffs;
502 if (list.find(firstDiff) == list.end())
503 list.insert(std::pair<int, std::vector<int> *>(firstDiff, new std::vector<int>()));
504 pWordDiffs = list[firstDiff];
505 for (int i = firstWordDiff; i <= lastWordDiff; ++i)
507 if (pWordDiffs->empty() || i != (*pWordDiffs)[pWordDiffs->size() - 1])
508 pWordDiffs->push_back(i);
513 for (auto& it : list)
514 ret.insert(std::pair<int, std::vector<int>>(it.first, *it.second));
518 void CMergeEditView::OnInitialUpdate()
521 CCrystalEditViewEx::OnInitialUpdate();
523 SetFont(dynamic_cast<CMainFrame*>(AfxGetMainWnd())->m_lfDiff);
524 SetAlternateDropTarget(new DropHandler(std::bind(&CMergeEditView::OnDropFiles, this, std::placeholders::_1)));
530 void CMergeEditView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
532 CCrystalEditViewEx::OnActivateView(bActivate, pActivateView, pDeactiveView);
534 CMergeDoc* pDoc = GetDocument();
535 pDoc->UpdateHeaderActivity(m_nThisPane, !!bActivate);
538 std::vector<CrystalLineParser::TEXTBLOCK> CMergeEditView::GetMarkerTextBlocks(int nLineIndex) const
542 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
543 return std::vector<CrystalLineParser::TEXTBLOCK>();
545 return CCrystalTextView::GetMarkerTextBlocks(nLineIndex);
548 std::vector<TEXTBLOCK> CMergeEditView::GetAdditionalTextBlocks (int nLineIndex)
550 static const std::vector<TEXTBLOCK> emptyBlocks;
553 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
557 DWORD dwLineFlags = GetLineFlags(nLineIndex);
558 if ((dwLineFlags & LF_SNP) == LF_SNP || (dwLineFlags & LF_DIFF) != LF_DIFF || (dwLineFlags & LF_MOVED) == LF_MOVED)
561 if (!GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT))
564 CMergeDoc *pDoc = GetDocument();
565 if (pDoc->IsEditedAfterRescan(m_nThisPane))
568 int nDiff = pDoc->m_diffList.LineToDiff(nLineIndex);
573 pDoc->m_diffList.GetDiff(nDiff, cd);
574 int unemptyLineCount = 0;
575 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
577 if (cd.begin[nPane] != cd.end[nPane] + 1)
580 if (unemptyLineCount < 2)
583 vector<WordDiff> worddiffs = pDoc->GetWordDiffArray(nLineIndex);
584 size_t nWordDiffs = worddiffs.size();
586 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
588 std::vector<TEXTBLOCK> blocks(nWordDiffs * 2 + 1);
589 blocks[0].m_nCharPos = 0;
590 blocks[0].m_nColorIndex = COLORINDEX_NONE;
591 blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
593 for (i = 0, j = 1; i < nWordDiffs; i++)
595 if (worddiffs[i].beginline[m_nThisPane] > nLineIndex || worddiffs[i].endline[m_nThisPane] < nLineIndex )
597 if (pDoc->m_nBuffers > 2)
599 if (m_nThisPane == 0 && worddiffs[i].op == OP_3RDONLY)
601 else if (m_nThisPane == 2 && worddiffs[i].op == OP_1STONLY)
604 int begin[3], end[3];
605 bool deleted = false;
606 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
608 begin[pane] = (worddiffs[i].beginline[pane] < nLineIndex) ? 0 : worddiffs[i].begin[pane];
609 end[pane] = (worddiffs[i].endline[pane] > nLineIndex) ? GetGroupView(pane)->GetLineLength(nLineIndex) : worddiffs[i].end[pane];
610 if (worddiffs[i].beginline[pane] == worddiffs[i].endline[pane] &&
611 worddiffs[i].begin[pane] == worddiffs[i].end[pane])
614 blocks[j].m_nCharPos = begin[m_nThisPane];
615 if (lineInCurrentDiff)
617 if (m_cachedColors.clrSelDiffText != CLR_NONE)
618 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT1 | COLORINDEX_APPLYFORCE;
620 blocks[j].m_nColorIndex = COLORINDEX_NONE;
621 blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE |
622 (deleted ? COLORINDEX_HIGHLIGHTBKGND4 : COLORINDEX_HIGHLIGHTBKGND1);
626 if (m_cachedColors.clrDiffText != CLR_NONE)
627 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT2 | COLORINDEX_APPLYFORCE;
629 blocks[j].m_nColorIndex = COLORINDEX_NONE;
630 blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE |
631 (deleted ? COLORINDEX_HIGHLIGHTBKGND3 : COLORINDEX_HIGHLIGHTBKGND2);
634 blocks[j].m_nCharPos = end[m_nThisPane];
635 blocks[j].m_nColorIndex = COLORINDEX_NONE;
636 blocks[j].m_nBgColorIndex = COLORINDEX_NONE;
645 COLORREF CMergeEditView::GetColor(int nColorIndex)
647 switch (nColorIndex & ~COLORINDEX_APPLYFORCE)
649 case COLORINDEX_HIGHLIGHTBKGND1:
650 return m_cachedColors.clrSelWordDiff;
651 case COLORINDEX_HIGHLIGHTTEXT1:
652 return m_cachedColors.clrSelWordDiffText;
653 case COLORINDEX_HIGHLIGHTBKGND2:
654 return m_cachedColors.clrWordDiff;
655 case COLORINDEX_HIGHLIGHTTEXT2:
656 return m_cachedColors.clrWordDiffText;
657 case COLORINDEX_HIGHLIGHTBKGND3:
658 return m_cachedColors.clrWordDiffDeleted;
659 case COLORINDEX_HIGHLIGHTBKGND4:
660 return m_cachedColors.clrSelWordDiffDeleted;
663 return CCrystalTextView::GetColor(nColorIndex);
668 * @brief Determine text and background color for line
669 * @param [in] nLineIndex Index of line in view (NOT line in file)
670 * @param [out] crBkgnd Backround color for line
671 * @param [out] crText Text color for line
673 void CMergeEditView::GetLineColors(int nLineIndex, COLORREF & crBkgnd,
674 COLORREF & crText, bool & bDrawWhitespace)
676 DWORD ignoreFlags = 0;
677 GetLineColors2(nLineIndex, ignoreFlags, crBkgnd, crText, bDrawWhitespace);
681 * @brief Determine text and background color for line
682 * @param [in] nLineIndex Index of line in view (NOT line in file)
683 * @param [in] ignoreFlags Flags that caller wishes ignored
684 * @param [out] crBkgnd Backround color for line
685 * @param [out] crText Text color for line
687 * This version allows caller to suppress particular flags
689 void CMergeEditView::GetLineColors2(int nLineIndex, DWORD ignoreFlags, COLORREF & crBkgnd,
690 COLORREF & crText, bool & bDrawWhitespace)
692 if (GetLineCount() <= nLineIndex)
695 DWORD dwLineFlags = GetLineFlags(nLineIndex);
697 if (dwLineFlags & ignoreFlags)
698 dwLineFlags &= (~ignoreFlags);
702 // Line with WinMerge flag,
703 // Lines with only the LF_DIFF/LF_TRIVIAL flags are not colored with Winmerge colors
704 if (dwLineFlags & (LF_WINMERGE_FLAGS & ~LF_DIFF & ~LF_TRIVIAL & ~LF_MOVED & ~LF_SNP))
706 crText = m_cachedColors.clrDiffText;
707 bDrawWhitespace = true;
709 if (dwLineFlags & LF_GHOST)
711 crBkgnd = m_cachedColors.clrDiffDeleted;
716 // If no syntax hilighting
717 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
719 crBkgnd = GetColor (COLORINDEX_BKGND);
720 crText = GetColor (COLORINDEX_NORMALTEXT);
721 bDrawWhitespace = false;
724 // Line not inside diff, get colors from CrystalEditor
725 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
726 crText, bDrawWhitespace);
728 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
730 crBkgnd = GetColor (COLORINDEX_WHITESPACE);
731 crText = GetColor (COLORINDEX_WHITESPACE);
732 bDrawWhitespace = false;
738 if (dwLineFlags & LF_WINMERGE_FLAGS)
740 crText = m_cachedColors.clrDiffText;
741 bDrawWhitespace = true;
742 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
744 if (dwLineFlags & LF_SNP)
746 if (lineInCurrentDiff)
748 if (dwLineFlags & LF_GHOST)
749 crBkgnd = m_cachedColors.clrSelSNPDeleted;
751 crBkgnd = m_cachedColors.clrSelSNP;
752 crText = m_cachedColors.clrSelSNPText;
756 if (dwLineFlags & LF_GHOST)
757 crBkgnd = m_cachedColors.clrSNPDeleted;
759 crBkgnd = m_cachedColors.clrSNP;
760 crText = m_cachedColors.clrSNPText;
764 else if (dwLineFlags & LF_DIFF)
766 if (lineInCurrentDiff)
768 if (dwLineFlags & LF_MOVED)
770 crBkgnd = m_cachedColors.clrSelMoved;
771 crText = m_cachedColors.clrSelMovedText;
775 crBkgnd = m_cachedColors.clrSelDiff;
776 crText = m_cachedColors.clrSelDiffText;
782 if (dwLineFlags & LF_MOVED)
784 crBkgnd = m_cachedColors.clrMoved;
785 crText = m_cachedColors.clrMovedText;
789 crBkgnd = m_cachedColors.clrDiff;
790 crText = m_cachedColors.clrDiffText;
795 else if (dwLineFlags & LF_TRIVIAL)
797 // trivial diff can not be selected
798 if (dwLineFlags & LF_GHOST)
799 // ghost lines in trivial diff has their own color
800 crBkgnd = m_cachedColors.clrTrivialDeleted;
802 crBkgnd = m_cachedColors.clrTrivial;
803 crText = m_cachedColors.clrTrivialText;
806 else if (dwLineFlags & LF_GHOST)
808 if (lineInCurrentDiff)
810 if (dwLineFlags & LF_MOVED)
811 crBkgnd = m_cachedColors.clrSelMovedDeleted;
813 crBkgnd = m_cachedColors.clrSelDiffDeleted;
817 if (dwLineFlags & LF_MOVED)
818 crBkgnd = m_cachedColors.clrMovedDeleted;
820 crBkgnd = m_cachedColors.clrDiffDeleted;
827 // Line not inside diff,
828 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
830 // If no syntax hilighting, get windows default colors
831 crBkgnd = GetColor (COLORINDEX_BKGND);
832 crText = GetColor (COLORINDEX_NORMALTEXT);
833 bDrawWhitespace = false;
836 // Syntax highlighting, get colors from CrystalEditor
837 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
838 crText, bDrawWhitespace);
843 * @brief Sync other pane position
845 void CMergeEditView::UpdateSiblingScrollPos (bool bHorz)
847 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
848 if (pSplitterWnd != nullptr)
850 // See CSplitterWnd::IdFromRowCol() implementation for details
851 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
852 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
853 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
854 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
856 // limit the TopLine : must be smaller than GetLineCount for all the panels
857 int newTopSubLine = m_nTopSubLine;
858 int nRows = pSplitterWnd->GetRowCount ();
859 int nCols = pSplitterWnd->GetColumnCount ();
861 // for (nRow = 0; nRow < nRows; nRow++)
863 // for (int nCol = 0; nCol < nCols; nCol++)
865 // CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
866 // if (pSiblingView != nullptr)
867 // if (pSiblingView->GetSubLineCount() <= newTopSubLine)
868 // newTopSubLine = pSiblingView->GetSubLineCount()-1;
871 if (m_nTopSubLine != newTopSubLine)
872 ScrollToSubLine(newTopSubLine);
874 for (nRow = 0; nRow < nRows; nRow++)
876 for (int nCol = 0; nCol < nCols; nCol++)
878 if (!(nRow == nCurrentRow && nCol == nCurrentCol)) // We don't need to update ourselves
880 CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
881 if (pSiblingView != nullptr && pSiblingView->m_nThisGroup == m_nThisGroup)
882 pSiblingView->OnUpdateSibling (this, bHorz);
890 * @brief Update other panes
892 void CMergeEditView::OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
894 if (pUpdateSource != this)
896 ASSERT (pUpdateSource != nullptr);
897 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
898 CMergeEditView *pSrcView = static_cast<CMergeEditView*>(pUpdateSource);
899 if (!bHorz) // changed this so bHorz works right
901 ASSERT (pSrcView->m_nTopSubLine >= 0);
903 // This ASSERT is wrong: panes have different files and
904 // different linecounts
905 // ASSERT (pSrcView->m_nTopLine < GetLineCount ());
906 if (pSrcView->m_nTopSubLine != m_nTopSubLine)
908 ScrollToSubLine (pSrcView->m_nTopSubLine, true, false);
910 RecalcVertScrollBar(true);
911 RecalcHorzScrollBar();
916 ASSERT (pSrcView->m_nOffsetChar >= 0);
918 // This ASSERT is wrong: panes have different files and
919 // different linelengths
920 // ASSERT (pSrcView->m_nOffsetChar < GetMaxLineLength ());
921 if (pSrcView->m_nOffsetChar != m_nOffsetChar)
923 ScrollToChar (pSrcView->m_nOffsetChar, true, false);
925 RecalcHorzScrollBar(true);
926 RecalcHorzScrollBar();
932 void CMergeEditView::OnDisplayDiff(int nDiff /*=0*/)
934 int newlineBegin, newlineEnd;
935 CMergeDoc *pd = GetDocument();
936 if (nDiff < 0 || nDiff >= pd->m_diffList.GetSize())
944 VERIFY(pd->m_diffList.GetDiff(nDiff, curDiff));
946 newlineBegin = curDiff.dbegin;
947 ASSERT (newlineBegin >= 0);
948 newlineEnd = curDiff.dend;
951 m_lineBegin = newlineBegin;
952 m_lineEnd = newlineEnd;
954 int nLineCount = GetLineCount();
955 if (m_lineBegin > nLineCount)
956 m_lineBegin = nLineCount - 1;
957 if (m_lineEnd > nLineCount)
958 m_lineEnd = nLineCount - 1;
960 if (m_nTopLine == newlineBegin)
963 // scroll to the first line of the diff
964 ScrollToLine(m_lineBegin);
966 // update the width of the horizontal scrollbar
967 RecalcHorzScrollBar();
971 * @brief Selects diff by number and syncs other file
972 * @param [in] nDiff Diff to select, must be >= 0
973 * @param [in] bScroll Scroll diff to view
974 * @param [in] bSelectText Select diff text
975 * @sa CMergeEditView::ShowDiff()
976 * @sa CMergeDoc::SetCurrentDiff()
977 * @todo Parameter bSelectText is never used?
979 void CMergeEditView::SelectDiff(int nDiff, bool bScroll /*= true*/, bool bSelectText /*= true*/)
981 CMergeDoc *pd = GetDocument();
983 // Check that nDiff is valid
985 _RPTF1(_CRT_ERROR, "Diffnumber negative (%d)", nDiff);
986 if (nDiff >= pd->m_diffList.GetSize())
987 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d >= %d)",
988 nDiff, pd->m_diffList.GetSize());
991 pd->SetCurrentDiff(nDiff);
992 ShowDiff(bScroll, bSelectText);
993 pd->UpdateAllViews(this);
994 UpdateSiblingScrollPos(false);
996 // notify either side, as it will notify the other one
997 pd->ForEachView ([&](auto& pView) { if (pView->m_bDetailView) pView->OnDisplayDiff(nDiff); });
1000 void CMergeEditView::DeselectDiffIfCursorNotInCurrentDiff()
1002 CMergeDoc *pd = GetDocument();
1003 // If we have a selected diff, deselect it
1004 int nCurrentDiff = pd->GetCurrentDiff();
1005 if (nCurrentDiff != -1)
1007 CPoint pos = GetCursorPos();
1008 if (!IsLineInCurrentDiff(pos.y))
1010 pd->SetCurrentDiff(-1);
1012 pd->UpdateAllViews(this);
1018 * @brief Called when user selects "Current Difference".
1019 * Goes to active diff. If no active diff, selects diff under cursor
1020 * @sa CMergeEditView::SelectDiff()
1021 * @sa CMergeDoc::GetCurrentDiff()
1022 * @sa CMergeDoc::LineToDiff()
1024 void CMergeEditView::OnCurdiff()
1026 CMergeDoc *pd = GetDocument();
1028 // If no diffs, nothing to select
1029 if (!pd->m_diffList.HasSignificantDiffs())
1032 // GetCurrentDiff() returns -1 if no diff selected
1033 int nDiff = pd->GetCurrentDiff();
1036 // Scroll to the first line of the currently selected diff
1037 SelectDiff(nDiff, true, false);
1041 // If cursor is inside diff, select that diff
1042 CPoint pos = GetCursorPos();
1043 nDiff = pd->m_diffList.LineToDiff(pos.y);
1044 if (nDiff != -1 && pd->m_diffList.IsDiffSignificant(nDiff))
1045 SelectDiff(nDiff, true, false);
1050 * @brief Called when "Current diff" item is updated
1052 void CMergeEditView::OnUpdateCurdiff(CCmdUI* pCmdUI)
1054 CMergeDoc *pd = GetDocument();
1055 CPoint pos = GetCursorPos();
1056 int nCurrentDiff = pd->GetCurrentDiff();
1057 if (nCurrentDiff == -1)
1059 int nNewDiff = pd->m_diffList.LineToDiff(pos.y);
1060 pCmdUI->Enable(nNewDiff != -1 && pd->m_diffList.IsDiffSignificant(nNewDiff));
1063 pCmdUI->Enable(true);
1067 * @brief Copy selected text to clipboard
1069 void CMergeEditView::OnEditCopy()
1071 CMergeDoc * pDoc = GetDocument();
1072 CPoint ptSelStart, ptSelEnd;
1073 GetSelection(ptSelStart, ptSelEnd);
1076 if (ptSelStart == ptSelEnd)
1081 if (!m_bRectangularSelection)
1083 CDiffTextBuffer * buffer = pDoc->m_ptBuf[m_nThisPane].get();
1085 buffer->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1086 ptSelEnd.y, ptSelEnd.x, text);
1089 GetTextWithoutEmptysInColumnSelection(text);
1091 PutToClipboard(text, text.GetLength(), m_bRectangularSelection);
1095 * @brief Called when "Copy" item is updated
1097 void CMergeEditView::OnUpdateEditCopy(CCmdUI* pCmdUI)
1099 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
1103 * @brief Cut current selection to clipboard
1105 void CMergeEditView::OnEditCut()
1107 if (!QueryEditable())
1110 CPoint ptSelStart, ptSelEnd;
1111 CMergeDoc * pDoc = GetDocument();
1112 GetSelection(ptSelStart, ptSelEnd);
1115 if (ptSelStart == ptSelEnd)
1119 if (!m_bRectangularSelection)
1120 pDoc->m_ptBuf[m_nThisPane]->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1121 ptSelEnd.y, ptSelEnd.x, text);
1123 GetTextWithoutEmptysInColumnSelection(text);
1125 PutToClipboard(text, text.GetLength(), m_bRectangularSelection);
1127 if (!m_bRectangularSelection)
1129 CPoint ptCursorPos = ptSelStart;
1130 ASSERT_VALIDTEXTPOS(ptCursorPos);
1131 SetAnchor(ptCursorPos);
1132 SetSelection(ptCursorPos, ptCursorPos);
1133 SetCursorPos(ptCursorPos);
1134 EnsureVisible(ptCursorPos);
1136 pDoc->m_ptBuf[m_nThisPane]->DeleteText(this, ptSelStart.y, ptSelStart.x, ptSelEnd.y,
1137 ptSelEnd.x, CE_ACTION_CUT);
1140 DeleteCurrentColumnSelection (CE_ACTION_CUT);
1142 m_pTextBuffer->SetModified(true);
1146 * @brief Called when "Cut" item is updated
1148 void CMergeEditView::OnUpdateEditCut(CCmdUI* pCmdUI)
1150 if (QueryEditable())
1151 CCrystalEditViewEx::OnUpdateEditCut(pCmdUI);
1153 pCmdUI->Enable(false);
1157 * @brief Paste text from clipboard
1159 void CMergeEditView::OnEditPaste()
1161 if (!QueryEditable())
1164 CCrystalEditViewEx::Paste();
1165 m_pTextBuffer->SetModified(true);
1169 * @brief Called when "Paste" item is updated
1171 void CMergeEditView::OnUpdateEditPaste(CCmdUI* pCmdUI)
1173 if (QueryEditable())
1174 CCrystalEditViewEx::OnUpdateEditPaste(pCmdUI);
1176 pCmdUI->Enable(false);
1180 * @brief Undo last action
1182 void CMergeEditView::OnEditUndo()
1184 CWaitCursor waitstatus;
1185 CMergeDoc* pDoc = GetDocument();
1186 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1189 if (!QueryEditable())
1192 GetParentFrame()->SetActiveView(this, true);
1193 if(CCrystalEditViewEx::DoEditUndo())
1196 pDoc->UpdateHeaderPath(m_nThisPane);
1197 pDoc->FlushAndRescan();
1200 m_pTextBuffer->GetRedoActionCode(nAction);
1201 if (nAction == CE_ACTION_MERGE)
1202 // select the diff so we may just merge it again
1208 tgt->SendMessage(WM_COMMAND, ID_EDIT_UNDO);
1210 if (!pDoc->CanUndo())
1211 pDoc->SetAutoMerged(false);
1215 * @brief Called when "Undo" item is updated
1217 void CMergeEditView::OnUpdateEditUndo(CCmdUI* pCmdUI)
1219 CMergeDoc* pDoc = GetDocument();
1220 if (pDoc->curUndo!=pDoc->undoTgt.begin())
1222 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1223 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
1226 pCmdUI->Enable(false);
1230 * @brief Go to first diff
1232 * Called when user selects "First Difference"
1233 * @sa CMergeEditView::SelectDiff()
1235 void CMergeEditView::OnFirstdiff()
1237 CMergeDoc *pd = GetDocument();
1238 if (pd->m_diffList.HasSignificantDiffs())
1240 int nDiff = pd->m_diffList.FirstSignificantDiff();
1241 SelectDiff(nDiff, true, false);
1246 * @brief Update "First diff" UI items
1248 void CMergeEditView::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1250 OnUpdatePrevdiff(pCmdUI);
1254 * @brief Go to last diff
1256 void CMergeEditView::OnLastdiff()
1258 CMergeDoc *pd = GetDocument();
1259 if (pd->m_diffList.HasSignificantDiffs())
1261 int nDiff = pd->m_diffList.LastSignificantDiff();
1262 SelectDiff(nDiff, true, false);
1267 * @brief Update "Last diff" UI items
1269 void CMergeEditView::OnUpdateLastdiff(CCmdUI* pCmdUI)
1271 OnUpdateNextdiff(pCmdUI);
1275 * @brief Go to next diff and select it.
1277 * Finds and selects next difference. There are several cases:
1278 * - if there is selected difference, and that difference is visible
1279 * on screen, next found difference is selected.
1280 * - if there is selected difference but it is not visible, next
1281 * difference from cursor position is selected. This is what user
1282 * expects to happen and is natural thing to do. Also reduces
1283 * needless scrolling.
1284 * - if there is no selected difference, next difference from cursor
1285 * position is selected.
1287 void CMergeEditView::OnNextdiff()
1289 CMergeDoc *pd = GetDocument();
1290 int cnt = pd->m_ptBuf[0]->GetLineCount();
1294 // Returns -1 if no diff selected
1296 int curDiff = pd->GetCurrentDiff();
1300 if (!IsDiffVisible(curDiff))
1302 // Selected difference not visible, select next from cursor
1303 int line = GetCursorPos().y;
1304 // Make sure we aren't in the first line of the diff
1306 if (!IsValidTextPosY(CPoint(0, line)))
1308 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1312 // Find out if there is a following significant diff
1313 if (curDiff < pd->m_diffList.GetSize() - 1)
1315 nextDiff = pd->m_diffList.NextSignificantDiff(curDiff);
1321 // We don't have a selected difference,
1322 // but cursor can be inside inactive diff
1323 int line = GetCursorPos().y;
1324 if (!IsValidTextPosY(CPoint(0, line)))
1326 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1329 int lastDiff = pd->m_diffList.LastSignificantDiff();
1330 if (nextDiff >= 0 && nextDiff <= lastDiff)
1331 SelectDiff(nextDiff, true, false);
1332 else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1334 if (pDirDoc->MoveableToNextDiff())
1335 pDirDoc->MoveToNextDiff(pd);
1340 * @brief Update "Next diff" UI items
1342 void CMergeEditView::OnUpdateNextdiff(CCmdUI* pCmdUI)
1344 CMergeDoc *pd = GetDocument();
1345 const DIFFRANGE * dfi = pd->m_diffList.LastSignificantDiffRange();
1350 // There aren't any significant differences
1355 // Enable if the beginning of the last significant difference is after caret
1356 enabled = (GetCursorPos().y < (long)dfi->dbegin);
1359 if (!enabled && pd->GetDirDoc())
1360 enabled = pd->GetDirDoc()->MoveableToNextDiff();
1362 pCmdUI->Enable(enabled);
1366 * @brief Go to previous diff and select it.
1368 * Finds and selects previous difference. There are several cases:
1369 * - if there is selected difference, and that difference is visible
1370 * on screen, previous found difference is selected.
1371 * - if there is selected difference but it is not visible, previous
1372 * difference from cursor position is selected. This is what user
1373 * expects to happen and is natural thing to do. Also reduces
1374 * needless scrolling.
1375 * - if there is no selected difference, previous difference from cursor
1376 * position is selected.
1378 void CMergeEditView::OnPrevdiff()
1380 CMergeDoc *pd = GetDocument();
1381 int cnt = pd->m_ptBuf[0]->GetLineCount();
1385 // GetCurrentDiff() returns -1 if no diff selected
1387 int curDiff = pd->GetCurrentDiff();
1391 if (!IsDiffVisible(curDiff))
1393 // Selected difference not visible, select previous from cursor
1394 int line = GetCursorPos().y;
1395 // Make sure we aren't in the last line of the diff
1397 if (!IsValidTextPosY(CPoint(0, line)))
1399 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1403 // Find out if there is a preceding significant diff
1406 prevDiff = pd->m_diffList.PrevSignificantDiff(curDiff);
1412 // We don't have a selected difference,
1413 // but cursor can be inside inactive diff
1414 int line = GetCursorPos().y;
1415 if (!IsValidTextPosY(CPoint(0, line)))
1417 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1420 int firstDiff = pd->m_diffList.FirstSignificantDiff();
1421 if (prevDiff >= 0 && prevDiff >= firstDiff)
1422 SelectDiff(prevDiff, true, false);
1423 else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1425 if (pDirDoc->MoveableToPrevDiff())
1426 pDirDoc->MoveToPrevDiff(pd);
1431 * @brief Update "Previous diff" UI items
1433 void CMergeEditView::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1435 CMergeDoc *pd = GetDocument();
1436 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificantDiffRange();
1441 // There aren't any significant differences
1446 // Enable if the end of the first significant difference is before caret
1447 enabled = (GetCursorPos().y > (long)dfi->dend);
1450 if (!enabled && pd->GetDirDoc())
1451 enabled = pd->GetDirDoc()->MoveableToPrevDiff();
1453 pCmdUI->Enable(enabled);
1456 void CMergeEditView::OnNextConflict()
1458 OnNext3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1462 * @brief Update "Next Conflict" UI items
1464 void CMergeEditView::OnUpdateNextConflict(CCmdUI* pCmdUI)
1466 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1469 void CMergeEditView::OnPrevConflict()
1471 OnPrev3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1475 * @brief Update "Prev Conflict" UI items
1477 void CMergeEditView::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1479 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1483 * @brief Go to next 3-way diff and select it.
1485 void CMergeEditView::OnNext3wayDiff(int nDiffType)
1487 CMergeDoc *pd = GetDocument();
1488 int cnt = pd->m_ptBuf[0]->GetLineCount();
1492 // Returns -1 if no diff selected
1493 int curDiff = pd->GetCurrentDiff();
1497 int nextDiff = curDiff;
1498 if (!IsDiffVisible(curDiff))
1500 // Selected difference not visible, select next from cursor
1501 int line = GetCursorPos().y;
1502 // Make sure we aren't in the first line of the diff
1504 if (!IsValidTextPosY(CPoint(0, line)))
1506 nextDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1510 // Find out if there is a following significant diff
1511 if (curDiff < pd->m_diffList.GetSize() - 1)
1513 nextDiff = pd->m_diffList.NextSignificant3wayDiff(curDiff, nDiffType);
1519 // nextDiff is the next one if there is one, else it is the one we're on
1520 SelectDiff(nextDiff, true, false);
1524 // We don't have a selected difference,
1525 // but cursor can be inside inactive diff
1526 int line = GetCursorPos().y;
1527 if (!IsValidTextPosY(CPoint(0, line)))
1529 curDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1531 SelectDiff(curDiff, true, false);
1536 * @brief Update "Next 3-way diff" UI items
1538 void CMergeEditView::OnUpdateNext3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1540 CMergeDoc *pd = GetDocument();
1542 if (pd->m_nBuffers < 3)
1544 pCmdUI->Enable(false);
1548 const DIFFRANGE * dfi = pd->m_diffList.LastSignificant3wayDiffRange(nDiffType);
1552 // There aren't any significant differences
1553 pCmdUI->Enable(false);
1557 // Enable if the beginning of the last significant difference is after caret
1558 CPoint pos = GetCursorPos();
1559 pCmdUI->Enable(pos.y < (long)dfi->dbegin);
1564 * @brief Go to previous 3-way diff and select it.
1566 void CMergeEditView::OnPrev3wayDiff(int nDiffType)
1568 CMergeDoc *pd = GetDocument();
1570 int cnt = pd->m_ptBuf[0]->GetLineCount();
1574 // GetCurrentDiff() returns -1 if no diff selected
1575 int curDiff = pd->GetCurrentDiff();
1579 int prevDiff = curDiff;
1580 if (!IsDiffVisible(curDiff))
1582 // Selected difference not visible, select previous from cursor
1583 int line = GetCursorPos().y;
1584 // Make sure we aren't in the last line of the diff
1586 if (!IsValidTextPosY(CPoint(0, line)))
1588 prevDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1592 // Find out if there is a preceding significant diff
1595 prevDiff = pd->m_diffList.PrevSignificant3wayDiff(curDiff, nDiffType);
1601 // prevDiff is the preceding one if there is one, else it is the one we're on
1602 SelectDiff(prevDiff, true, false);
1606 // We don't have a selected difference,
1607 // but cursor can be inside inactive diff
1608 int line = GetCursorPos().y;
1609 if (!IsValidTextPosY(CPoint(0, line)))
1611 curDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1613 SelectDiff(curDiff, true, false);
1618 * @brief Update "Previous diff X and Y" UI items
1620 void CMergeEditView::OnUpdatePrev3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1622 CMergeDoc *pd = GetDocument();
1624 if (pd->m_nBuffers < 3)
1626 pCmdUI->Enable(false);
1630 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificant3wayDiffRange(nDiffType);
1634 // There aren't any significant differences
1635 pCmdUI->Enable(false);
1639 // Enable if the end of the first significant difference is before caret
1640 CPoint pos = GetCursorPos();
1641 pCmdUI->Enable(pos.y > (long)dfi->dend);
1645 void CMergeEditView::OnNextdiffLM()
1647 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1650 void CMergeEditView::OnUpdateNextdiffLM(CCmdUI* pCmdUI)
1652 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1655 void CMergeEditView::OnNextdiffLR()
1657 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1660 void CMergeEditView::OnUpdateNextdiffLR(CCmdUI* pCmdUI)
1662 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1665 void CMergeEditView::OnNextdiffMR()
1667 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1670 void CMergeEditView::OnUpdateNextdiffMR(CCmdUI* pCmdUI)
1672 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1675 void CMergeEditView::OnNextdiffLO()
1677 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1680 void CMergeEditView::OnUpdateNextdiffLO(CCmdUI* pCmdUI)
1682 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1685 void CMergeEditView::OnNextdiffMO()
1687 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1690 void CMergeEditView::OnUpdateNextdiffMO(CCmdUI* pCmdUI)
1692 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1695 void CMergeEditView::OnNextdiffRO()
1697 OnNext3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1700 void CMergeEditView::OnUpdateNextdiffRO(CCmdUI* pCmdUI)
1702 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1705 void CMergeEditView::OnPrevdiffLM()
1707 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1710 void CMergeEditView::OnUpdatePrevdiffLM(CCmdUI* pCmdUI)
1712 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1715 void CMergeEditView::OnPrevdiffLR()
1717 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1720 void CMergeEditView::OnUpdatePrevdiffLR(CCmdUI* pCmdUI)
1722 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1725 void CMergeEditView::OnPrevdiffMR()
1727 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1730 void CMergeEditView::OnUpdatePrevdiffMR(CCmdUI* pCmdUI)
1732 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1735 void CMergeEditView::OnPrevdiffLO()
1737 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1740 void CMergeEditView::OnUpdatePrevdiffLO(CCmdUI* pCmdUI)
1742 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1745 void CMergeEditView::OnPrevdiffMO()
1747 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1750 void CMergeEditView::OnUpdatePrevdiffMO(CCmdUI* pCmdUI)
1752 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1755 void CMergeEditView::OnPrevdiffRO()
1757 OnPrev3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1760 void CMergeEditView::OnUpdatePrevdiffRO(CCmdUI* pCmdUI)
1762 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1766 * @brief Clear selection
1768 void CMergeEditView::SelectNone()
1770 SetSelection (GetCursorPos(), GetCursorPos());
1775 * @brief Check if line is inside currently selected diff
1776 * @param [in] nLine 0-based linenumber in view
1777 * @sa CMergeDoc::GetCurrentDiff()
1778 * @sa CMergeDoc::LineInDiff()
1780 bool CMergeEditView::IsLineInCurrentDiff(int nLine) const
1782 // Check validity of nLine
1785 _RPTF1(_CRT_ERROR, "Linenumber is negative (%d)!", nLine);
1786 int nLineCount = LocateTextBuffer()->GetLineCount();
1787 if (nLine >= nLineCount)
1788 _RPTF2(_CRT_ERROR, "Linenumber > linecount (%d>%d)!", nLine, nLineCount);
1791 const CMergeDoc *pd = GetDocument();
1792 int curDiff = pd->GetCurrentDiff();
1795 return pd->m_diffList.LineInDiff(nLine, curDiff);
1799 * @brief Called when mouse left-button double-clicked
1801 * Double-clicking mouse inside diff selects that diff
1803 void CMergeEditView::OnLButtonDblClk(UINT nFlags, CPoint point)
1805 CMergeDoc *pd = GetDocument();
1806 CPoint pos = GetCursorPos();
1808 int diff = pd->m_diffList.LineToDiff(pos.y);
1809 if (diff != -1 && pd->m_diffList.IsDiffSignificant(diff))
1810 SelectDiff(diff, false, false);
1812 CCrystalEditViewEx::OnLButtonDblClk(nFlags, point);
1816 * @brief Called when mouse left button is released.
1818 * If button is released outside diffs, current diff
1821 void CMergeEditView::OnLButtonUp(UINT nFlags, CPoint point)
1823 CCrystalEditViewEx::OnLButtonUp(nFlags, point);
1824 DeselectDiffIfCursorNotInCurrentDiff();
1828 * @brief Called when mouse right button is pressed.
1830 * If right button is pressed outside diffs, current diff
1833 void CMergeEditView::OnRButtonDown(UINT nFlags, CPoint point)
1835 CCrystalEditViewEx::OnRButtonDown(nFlags, point);
1836 DeselectDiffIfCursorNotInCurrentDiff();
1839 void CMergeEditView::OnX2Y(int srcPane, int dstPane)
1841 // Check that right side is not readonly
1842 if (IsReadOnly(dstPane))
1845 CMergeDoc *pDoc = GetDocument();
1846 int currentDiff = pDoc->GetCurrentDiff();
1848 if (currentDiff == -1)
1851 // If cursor is inside diff get number of that diff
1852 if (m_bCurrentLineIsDiff)
1854 CPoint pt = GetCursorPos();
1855 currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1859 CPoint ptStart, ptEnd;
1860 GetSelection(ptStart, ptEnd);
1861 if (IsSelection() || pDoc->EqualCurrentWordDiff(srcPane, ptStart, ptEnd))
1863 if (!m_bRectangularSelection)
1865 int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
1866 GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1867 if (firstDiff != -1 && lastDiff != -1)
1869 CWaitCursor waitstatus;
1870 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1875 CWaitCursor waitstatus;
1876 auto wordDiffs = GetColumnSelectedWordDiffIndice();
1878 std::for_each(wordDiffs.rbegin(), wordDiffs.rend(), [&](auto& it) {
1879 pDoc->WordListCopy(srcPane, dstPane, it.first, it.second[0], it.second[it.second.size() - 1], &it.second, i != 0, i == 0);
1884 else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1886 CWaitCursor waitstatus;
1887 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1891 void CMergeEditView::OnUpdateX2Y(int dstPane, CCmdUI* pCmdUI)
1893 // Check that right side is not readonly
1894 if (!IsReadOnly(dstPane))
1896 // If one or more diffs inside selection OR
1897 // there is an active diff OR
1898 // cursor is inside diff
1899 CPoint ptStart, ptEnd;
1900 GetSelection(ptStart, ptEnd);
1901 if (IsSelection() || GetDocument()->EqualCurrentWordDiff(m_nThisPane, ptStart, ptEnd))
1903 if (m_bCurrentLineIsDiff || (m_pTextBuffer->GetLineFlags(m_ptSelStart.y) & LF_NONTRIVIAL_DIFF) != 0)
1905 pCmdUI->Enable(true);
1909 int firstDiff, lastDiff;
1910 GetFullySelectedDiffs(firstDiff, lastDiff);
1912 pCmdUI->Enable(firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff));
1917 const int currDiff = GetDocument()->GetCurrentDiff();
1918 pCmdUI->Enable(m_bCurrentLineIsDiff || (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff)));
1922 pCmdUI->Enable(false);
1926 * @brief Copy diff from left pane to right pane
1928 * Difference is copied from left to right when
1929 * - difference is selected
1930 * - difference is inside selection (allows merging multiple differences).
1931 * - cursor is inside diff
1933 * If there is selected diff outside selection, we copy selected
1936 void CMergeEditView::OnL2r()
1938 int dstPane = (m_nThisPane < GetDocument()->m_nBuffers - 1) ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1939 int srcPane = dstPane - 1;
1940 OnX2Y(srcPane, dstPane);
1944 * @brief Called when "Copy to left" item is updated
1946 void CMergeEditView::OnUpdateL2r(CCmdUI* pCmdUI)
1948 OnUpdateX2Y(m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1, pCmdUI);
1952 * @brief Copy diff from right pane to left pane
1954 * Difference is copied from left to right when
1955 * - difference is selected
1956 * - difference is inside selection (allows merging multiple differences).
1957 * - cursor is inside diff
1959 * If there is selected diff outside selection, we copy selected
1962 void CMergeEditView::OnR2l()
1964 int dstPane = (m_nThisPane > 0) ? m_nThisPane - 1 : 0;
1965 int srcPane = dstPane + 1;
1966 OnX2Y(srcPane, dstPane);
1970 * @brief Called when "Copy to right" item is updated
1972 void CMergeEditView::OnUpdateR2l(CCmdUI* pCmdUI)
1974 OnUpdateX2Y(m_nThisPane > 0 ? m_nThisPane - 1 : 0, pCmdUI);
1977 void CMergeEditView::OnCopyFromLeft()
1979 int dstPane = m_nThisPane;
1980 int srcPane = dstPane - 1;
1983 OnX2Y(srcPane, dstPane);
1986 void CMergeEditView::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1988 int dstPane = m_nThisPane;
1989 int srcPane = dstPane - 1;
1991 pCmdUI->Enable(false);
1993 OnUpdateX2Y(dstPane, pCmdUI);
1996 void CMergeEditView::OnCopyFromRight()
1998 int dstPane = m_nThisPane;
1999 int srcPane = dstPane + 1;
2000 if (srcPane >= GetDocument()->m_nBuffers)
2002 OnX2Y(srcPane, dstPane);
2005 void CMergeEditView::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
2007 int dstPane = m_nThisPane;
2008 int srcPane = dstPane + 1;
2009 if (srcPane >= GetDocument()->m_nBuffers)
2010 pCmdUI->Enable(false);
2012 OnUpdateX2Y(dstPane, pCmdUI);
2016 * @brief Copy all diffs from right pane to left pane
2018 void CMergeEditView::OnAllLeft()
2020 // Check that left side is not readonly
2021 int srcPane = m_nThisPane > 0 ? m_nThisPane : 1;
2022 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
2023 if (IsReadOnly(dstPane))
2025 CWaitCursor waitstatus;
2027 GetDocument()->CopyAllList(srcPane, dstPane);
2031 * @brief Called when "Copy all to left" item is updated
2033 void CMergeEditView::OnUpdateAllLeft(CCmdUI* pCmdUI)
2035 // Check that left side is not readonly
2036 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
2037 if (!IsReadOnly(dstPane))
2038 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
2040 pCmdUI->Enable(false);
2044 * @brief Copy all diffs from left pane to right pane
2046 void CMergeEditView::OnAllRight()
2048 // Check that right side is not readonly
2049 int srcPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane : m_nThisPane - 1;
2050 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
2051 if (IsReadOnly(dstPane))
2054 CWaitCursor waitstatus;
2056 GetDocument()->CopyAllList(srcPane, dstPane);
2060 * @brief Called when "Copy all to right" item is updated
2062 void CMergeEditView::OnUpdateAllRight(CCmdUI* pCmdUI)
2064 // Check that right side is not readonly
2065 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
2066 if (!IsReadOnly(dstPane))
2067 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
2069 pCmdUI->Enable(false);
2073 * @brief Do Auto merge
2075 void CMergeEditView::OnAutoMerge()
2077 // Check current pane is not readonly
2078 if (GetDocument()->IsModified() || GetDocument()->GetAutoMerged() || !QueryEditable())
2081 CWaitCursor waitstatus;
2083 GetDocument()->DoAutoMerge(m_nThisPane);
2087 * @brief Called when "Auto Merge" item is updated
2089 void CMergeEditView::OnUpdateAutoMerge(CCmdUI* pCmdUI)
2091 pCmdUI->Enable(GetDocument()->m_nBuffers == 3 &&
2092 !GetDocument()->IsModified() &&
2093 !GetDocument()->GetAutoMerged() &&
2098 * @brief Add synchronization point
2100 void CMergeEditView::OnAddSyncPoint()
2102 GetDocument()->AddSyncPoint();
2106 * @brief Clear synchronization points
2108 void CMergeEditView::OnClearSyncPoints()
2110 GetDocument()->ClearSyncPoints();
2114 * @brief Called when "Clear Synchronization Points" item is updated
2116 void CMergeEditView::OnUpdateClearSyncPoints(CCmdUI* pCmdUI)
2118 pCmdUI->Enable(GetDocument()->HasSyncPoints());
2122 * @brief This function is called before other edit events.
2123 * @param [in] nAction Edit operation to do
2124 * @param [in] pszText Text to insert, delete etc
2125 * @sa CCrystalEditView::OnEditOperation()
2126 * @todo More edit-events for rescan delaying?
2128 void CMergeEditView::OnEditOperation(int nAction, LPCTSTR pszText, size_t cchText)
2130 if (!QueryEditable())
2132 // We must not arrive here, and assert helps detect troubles
2137 CMergeDoc* pDoc = GetDocument();
2138 pDoc->SetEditedAfterRescan(m_nThisPane);
2140 // simple hook for multiplex undo operations
2141 // deleted by jtuc 2003-06-28
2142 // now AddUndoRecords does it (so we don't create entry for OnEditOperation with no Undo data in m_pTextBuffer)
2143 /*if(dynamic_cast<CMergeDoc::CDiffTextBuffer*>(m_pTextBuffer)->curUndoGroup())
2145 pDoc->undoTgt.erase(pDoc->curUndo, pDoc->undoTgt.end());
2146 pDoc->undoTgt.push_back(this);
2147 pDoc->curUndo = pDoc->undoTgt.end();
2150 // perform original function
2151 CCrystalEditViewEx::OnEditOperation(nAction, pszText, cchText);
2153 // augment with additional operations
2155 // Change header to inform about changed doc
2156 pDoc->UpdateHeaderPath(m_nThisPane);
2158 // If automatic rescan enabled, rescan after edit events
2159 if (m_bAutomaticRescan)
2161 // keep document up to date
2162 // (Re)start timer to rescan only when user edits text
2163 // If timer starting fails, rescan immediately
2164 if (nAction == CE_ACTION_TYPING ||
2165 nAction == CE_ACTION_REPLACE ||
2166 nAction == CE_ACTION_BACKSPACE ||
2167 nAction == CE_ACTION_INDENT ||
2168 nAction == CE_ACTION_PASTE ||
2169 nAction == CE_ACTION_DELSEL ||
2170 nAction == CE_ACTION_DELETE ||
2171 nAction == CE_ACTION_CUT)
2173 if (!SetTimer(IDT_RESCAN, RESCAN_TIMEOUT, nullptr))
2174 pDoc->FlushAndRescan();
2177 pDoc->FlushAndRescan();
2183 // Update other pane for sync line.
2184 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
2186 if (nPane == m_nThisPane)
2188 CCrystalEditView *pView = GetGroupView(nPane);
2189 if (pView != nullptr)
2190 pView->Invalidate();
2197 * @brief Redo last action
2199 void CMergeEditView::OnEditRedo()
2201 CWaitCursor waitstatus;
2202 CMergeDoc* pDoc = GetDocument();
2203 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2206 if (!QueryEditable())
2209 GetParentFrame()->SetActiveView(this, true);
2210 if(CCrystalEditViewEx::DoEditRedo())
2213 pDoc->UpdateHeaderPath(m_nThisPane);
2214 pDoc->FlushAndRescan();
2219 tgt->SendMessage(WM_COMMAND, ID_EDIT_REDO);
2224 * @brief Called when "Redo" item is updated
2226 void CMergeEditView::OnUpdateEditRedo(CCmdUI* pCmdUI)
2228 CMergeDoc* pDoc = GetDocument();
2229 if (pDoc->curUndo!=pDoc->undoTgt.end())
2231 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2232 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
2235 pCmdUI->Enable(false);
2238 void CMergeEditView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
2240 CCrystalEditViewEx::OnUpdate(pSender, lHint, pHint);
2244 * @brief Scrolls to current diff and/or selects diff text
2245 * @param [in] bScroll If true scroll diff to view
2246 * @param [in] bSelectText If true select diff text
2247 * @note If bScroll and bSelectText are false, this does nothing!
2248 * @todo This shouldn't be called when no diff is selected, so
2249 * somebody could try to ASSERT(nDiff > -1)...
2251 void CMergeEditView::ShowDiff(bool bScroll, bool bSelectText)
2253 CMergeDoc *pd = GetDocument();
2254 const int nDiff = pd->GetCurrentDiff();
2256 // Try to trap some errors
2257 if (nDiff >= pd->m_diffList.GetSize())
2258 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d > %d)!",
2259 nDiff, pd->m_diffList.GetSize());
2261 if (nDiff >= 0 && nDiff < pd->m_diffList.GetSize())
2263 CPoint ptStart, ptEnd;
2265 pd->m_diffList.GetDiff(nDiff, curDiff);
2268 ptStart.y = curDiff.dbegin;
2270 ptEnd.y = curDiff.dend;
2272 if (bScroll && !m_bDetailView)
2274 if (!IsDiffVisible(curDiff, CONTEXT_LINES_BELOW))
2276 // Difference is not visible, scroll it so that max amount of
2277 // scrolling is done while keeping the diff in screen. So if
2278 // scrolling is downwards, scroll the diff to as up in screen
2279 // as possible. This usually brings next diff to the screen
2280 // and we don't need to scroll into it.
2281 int nLine = GetSubLineIndex(ptStart.y);
2282 if (nLine > CONTEXT_LINES_ABOVE)
2284 nLine -= CONTEXT_LINES_ABOVE;
2286 GetGroupView(m_nThisPane)->ScrollToSubLine(nLine);
2287 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2289 if (nPane != m_nThisPane)
2290 GetGroupView(nPane)->ScrollToSubLine(nLine);
2293 GetGroupView(m_nThisPane)->SetCursorPos(ptStart);
2294 GetGroupView(m_nThisPane)->SetAnchor(ptStart);
2295 GetGroupView(m_nThisPane)->SetSelection(ptStart, ptStart);
2296 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2298 if (nPane != m_nThisPane)
2300 GetGroupView(nPane)->SetCursorPos(ptStart);
2301 GetGroupView(nPane)->SetAnchor(ptStart);
2302 GetGroupView(nPane)->SetSelection(ptStart, ptStart);
2309 ptEnd.x = GetLineLength(ptEnd.y);
2310 SetSelection(ptStart, ptEnd);
2319 void CMergeEditView::OnTimer(UINT_PTR nIDEvent)
2321 // Maybe we want theApp::OnIdle to proceed before processing a timer message
2322 // ...but for this the queue must be empty
2323 // The timer message is a low priority message but the queue is maybe not yet empty
2324 // So we set a flag, wait for OnIdle to proceed, then come back here...
2325 // We come back here with a IDLE_TIMER OnTimer message (send with SendMessage
2326 // not with SetTimer so there is no delay)
2328 // IDT_RESCAN was posted because the app wanted to do a flushAndRescan with some delay
2330 // IDLE_TIMER is the false timer used to come back here after OnIdle
2331 // fTimerWaitingForIdle is a bool to store the commands waiting for idle
2332 // (one normal timer = one flag = one command)
2334 if (nIDEvent == IDT_RESCAN)
2336 KillTimer(IDT_RESCAN);
2337 fTimerWaitingForIdle |= FLAG_RESCAN_WAITS_FOR_IDLE;
2338 // notify the app to come back after OnIdle
2339 theApp.SetNeedIdleTimer();
2342 if (nIDEvent == IDLE_TIMER)
2344 // not a real timer, just come back after OnIdle
2345 // look to flags to know what to do
2346 if (fTimerWaitingForIdle & FLAG_RESCAN_WAITS_FOR_IDLE)
2347 GetDocument()->RescanIfNeeded(RESCAN_TIMEOUT/1000);
2348 fTimerWaitingForIdle = 0;
2351 CCrystalEditViewEx::OnTimer(nIDEvent);
2355 * @brief Returns if buffer is read-only
2356 * @note This has no any relation to file being read-only!
2358 bool CMergeEditView::IsReadOnly(int pane) const
2360 return m_bDetailView ? true : (GetDocument()->m_ptBuf[pane]->GetReadOnly() != false);
2364 * @brief Called when "Save left (as...)" item is updated
2366 void CMergeEditView::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
2368 CMergeDoc *pd = GetDocument();
2369 pCmdUI->Enable(!IsReadOnly(0) && pd->m_ptBuf[0]->IsModified());
2373 * @brief Called when "Save middle (as...)" item is updated
2375 void CMergeEditView::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
2377 CMergeDoc *pd = GetDocument();
2378 pCmdUI->Enable(pd->m_nBuffers == 3 && !IsReadOnly(1) && pd->m_ptBuf[1]->IsModified());
2382 * @brief Called when "Save right (as...)" item is updated
2384 void CMergeEditView::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
2386 CMergeDoc *pd = GetDocument();
2387 pCmdUI->Enable(!IsReadOnly(pd->m_nBuffers - 1) && pd->m_ptBuf[pd->m_nBuffers - 1]->IsModified());
2391 * @brief Refresh display using text-buffers
2392 * @note This DOES NOT reload files!
2394 void CMergeEditView::OnRefresh()
2396 CMergeDoc *pd = GetDocument();
2397 ASSERT(pd != nullptr);
2398 pd->FlushAndRescan(true);
2402 * @brief Handle some keys when in merging mode
2404 bool CMergeEditView::MergeModeKeyDown(MSG* pMsg)
2406 bool bHandled = false;
2408 // Allow default text selection when SHIFT pressed
2409 if (::GetAsyncKeyState(VK_SHIFT))
2412 // Allow default editor functions when CTRL pressed
2413 if (::GetAsyncKeyState(VK_CONTROL))
2416 // If we are in merging mode (merge with cursor keys)
2417 // handle some keys here
2418 switch (pMsg->wParam)
2445 * @brief Called before messages are translated.
2447 * Checks if ESC key was pressed, saves and closes doc.
2448 * Also if in merge mode traps cursor keys.
2450 BOOL CMergeEditView::PreTranslateMessage(MSG* pMsg)
2452 if (pMsg->message == WM_KEYDOWN)
2454 // If we are in merging mode (merge with cursor keys)
2455 // handle some keys here
2456 if (theApp.GetMergingMode())
2458 bool bHandled = MergeModeKeyDown(pMsg);
2463 // Close window if user has allowed it from options
2464 if (pMsg->wParam == VK_ESCAPE)
2466 int nCloseWithEsc = GetOptionsMgr()->GetInt(OPT_CLOSE_WITH_ESC);
2467 if (nCloseWithEsc != 0)
2468 GetParentFrame()->PostMessage(WM_CLOSE, 0, 0);
2473 return CCrystalEditViewEx::PreTranslateMessage(pMsg);
2477 * @brief Called when "Save" item is updated
2479 void CMergeEditView::OnUpdateFileSave(CCmdUI* pCmdUI)
2481 CMergeDoc *pd = GetDocument();
2483 bool bModified = false;
2484 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2486 if (pd->m_ptBuf[nPane]->IsModified())
2489 pCmdUI->Enable(bModified);
2493 * @brief Enable/disable left buffer read-only
2495 void CMergeEditView::OnLeftReadOnly()
2497 CMergeDoc *pd = GetDocument();
2498 bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2499 pd->m_ptBuf[0]->SetReadOnly(!bReadOnly);
2503 * @brief Called when "Left read-only" item is updated
2505 void CMergeEditView::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
2507 CMergeDoc *pd = GetDocument();
2508 bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2509 pCmdUI->Enable(true);
2510 pCmdUI->SetCheck(bReadOnly);
2514 * @brief Enable/disable middle buffer read-only
2516 void CMergeEditView::OnMiddleReadOnly()
2518 CMergeDoc *pd = GetDocument();
2519 if (pd->m_nBuffers == 3)
2521 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2522 pd->m_ptBuf[1]->SetReadOnly(!bReadOnly);
2527 * @brief Called when "Middle read-only" item is updated
2529 void CMergeEditView::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
2531 CMergeDoc *pd = GetDocument();
2532 if (pd->m_nBuffers < 3)
2534 pCmdUI->Enable(false);
2538 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2539 pCmdUI->Enable(true);
2540 pCmdUI->SetCheck(bReadOnly);
2545 * @brief Enable/disable right buffer read-only
2547 void CMergeEditView::OnRightReadOnly()
2549 CMergeDoc *pd = GetDocument();
2550 bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2551 pd->m_ptBuf[pd->m_nBuffers - 1]->SetReadOnly(!bReadOnly);
2555 * @brief Called when "Left read-only" item is updated
2557 void CMergeEditView::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
2559 CMergeDoc *pd = GetDocument();
2560 bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2561 pCmdUI->Enable(true);
2562 pCmdUI->SetCheck(bReadOnly);
2565 /// Store interface we use to display status line info
2566 void CMergeEditView::SetStatusInterface(IMergeEditStatus * piMergeEditStatus)
2568 ASSERT(m_piMergeEditStatus == nullptr);
2569 m_piMergeEditStatus = piMergeEditStatus;
2573 * @brief Update status bar contents.
2575 void CMergeEditView::UpdateStatusbar()
2581 * @brief Update statusbar info, Override from CCrystalTextView
2582 * @note we tab-expand column, but we don't tab-expand char count,
2583 * since we want to show how many chars there are and tab is just one
2584 * character although it expands to several spaces.
2586 void CMergeEditView::OnUpdateCaret()
2588 if (m_piMergeEditStatus == nullptr || !IsTextBufferInitialized())
2591 CPoint cursorPos = GetCursorPos();
2592 int nScreenLine = cursorPos.y;
2593 const int nRealLine = ComputeRealLine(nScreenLine);
2600 DWORD dwLineFlags = 0;
2602 dwLineFlags = m_pTextBuffer->GetLineFlags(nScreenLine);
2603 // Is this a ghost line ?
2604 if (dwLineFlags & LF_GHOST)
2606 // Ghost lines display eg "Line 12-13"
2607 sLine.Format(_T("%d-%d"), nRealLine, nRealLine+1);
2608 sEol = _T("hidden");
2612 // Regular lines display eg "Line 13 Characters: 25 EOL: CRLF"
2613 sLine.Format(_T("%d"), nRealLine+1);
2614 curChar = cursorPos.x + 1;
2615 chars = GetLineLength(nScreenLine);
2616 column = CalculateActualOffset(nScreenLine, cursorPos.x, true) + 1;
2617 columns = CalculateActualOffset(nScreenLine, chars, true) + 1;
2619 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2620 GetDocument()->IsMixedEOL(m_nThisPane))
2622 sEol = GetTextBufferEol(nScreenLine);
2625 sEol = _T("hidden");
2627 m_piMergeEditStatus->SetLineInfo(sLine, column, columns,
2628 curChar, chars, sEol, GetDocument()->m_ptBuf[m_nThisPane]->getCodepage(), GetDocument()->m_ptBuf[m_nThisPane]->getHasBom());
2630 // Is cursor inside difference?
2631 if (dwLineFlags & LF_NONTRIVIAL_DIFF)
2632 m_bCurrentLineIsDiff = true;
2634 m_bCurrentLineIsDiff = false;
2636 CWnd* pWnd = GetFocus();
2637 if (!m_bDetailView || (pWnd && pWnd->m_hWnd == this->m_hWnd))
2638 UpdateLocationViewPosition(m_nTopSubLine, m_nTopSubLine + GetScreenLines());
2641 * @brief Select linedifference in the current line.
2643 * Select line difference in current line. Selection type
2644 * is choosed by highlight type.
2646 template<bool reversed>
2647 void CMergeEditView::OnSelectLineDiff()
2649 // Pass this to the document, to compare this file to other
2650 GetDocument()->Showlinediff(this, reversed);
2653 /// Enable select difference menuitem if current line is inside difference.
2654 void CMergeEditView::OnUpdateSelectLineDiff(CCmdUI* pCmdUI)
2656 pCmdUI->Enable(!GetDocument()->IsEditedAfterRescan());
2660 * @brief Enable/disable Replace-menuitem
2662 void CMergeEditView::OnUpdateEditReplace(CCmdUI* pCmdUI)
2664 CMergeDoc *pd = GetDocument();
2665 bool bReadOnly = pd->m_ptBuf[m_nThisPane]->GetReadOnly();
2667 pCmdUI->Enable(!bReadOnly);
2671 * @brief Update readonly statusbaritem
2673 void CMergeEditView::OnUpdateStatusRO(CCmdUI* pCmdUI)
2675 bool bRO = GetDocument()->m_ptBuf[pCmdUI->m_nID - ID_STATUS_PANE0FILE_RO]->GetReadOnly();
2676 pCmdUI->Enable(bRO);
2680 * @brief Create the dynamic submenu for scripts
2682 HMENU CMergeEditView::createScriptsSubmenu(HMENU hMenu)
2685 std::vector<String> functionNamesList = FileTransform::GetFreeFunctionsInScripts(L"EDITOR_SCRIPT");
2688 size_t i = GetMenuItemCount(hMenu);
2690 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2692 if (functionNamesList.size() == 0)
2694 // no script : create a <empty> entry
2695 AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, _("< Empty >").c_str());
2699 // or fill in the submenu with the scripts names
2700 int ID = ID_SCRIPT_FIRST; // first ID in menu
2701 for (i = 0 ; i < functionNamesList.size() ; i++, ID++)
2702 AppendMenu(hMenu, MF_STRING, ID, functionNamesList[i].c_str());
2704 functionNamesList.clear();
2707 if (!plugin::IsWindowsScriptThere())
2708 AppendMenu(hMenu, MF_STRING, ID_NO_SCT_SCRIPTS, _("WSH not found - .sct scripts disabled").c_str());
2714 * @brief Create the dynamic submenu for prediffers
2716 * @note The plugins are grouped in (suggested) and (not suggested)
2717 * The IDs follow the order of GetAvailableScripts
2719 * suggested 0 ID_1ST + 0
2720 * suggested 1 ID_1ST + 2
2721 * suggested 2 ID_1ST + 5
2722 * not suggested 0 ID_1ST + 1
2723 * not suggested 1 ID_1ST + 3
2724 * not suggested 2 ID_1ST + 4
2726 HMENU CMergeEditView::createPrediffersSubmenu(HMENU hMenu)
2729 int i = GetMenuItemCount(hMenu);
2731 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2733 CMergeDoc *pd = GetDocument();
2734 ASSERT(pd != nullptr);
2737 AppendMenu(hMenu, MF_STRING, ID_NO_PREDIFFER, _("No prediffer (normal)").c_str());
2739 // get the scriptlet files
2740 PluginArray * piScriptArray =
2741 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
2742 PluginArray * piScriptArray2 =
2743 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
2745 // build the menu : first part, suggested plugins
2747 AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2748 AppendMenu(hMenu, MF_STRING, ID_SUGGESTED_PLUGINS, _("Suggested plugins").c_str());
2750 int ID = ID_PREDIFFERS_FIRST; // first ID in menu
2752 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2754 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2755 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2758 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2760 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2762 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2763 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2766 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2769 // build the menu : second part, others plugins
2771 AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2772 AppendMenu(hMenu, MF_STRING, ID_NOT_SUGGESTED_PLUGINS, _("Other plugins").c_str());
2774 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2775 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2777 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2778 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2781 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2783 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2785 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2786 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2789 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2792 // compute the m_CurrentPredifferID (to set the radio button)
2793 PrediffingInfo prediffer;
2794 pd->GetPrediffer(&prediffer);
2796 if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
2797 m_CurrentPredifferID = 0;
2798 else if (prediffer.m_PluginName.empty())
2799 m_CurrentPredifferID = ID_NO_PREDIFFER;
2802 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2803 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2805 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2806 if (prediffer.m_PluginName == plugin->m_name)
2807 m_CurrentPredifferID = ID;
2810 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2812 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2813 if (prediffer.m_PluginName == plugin->m_name)
2814 m_CurrentPredifferID = ID;
2822 * @brief Offer a context menu built with scriptlet/ActiveX functions
2824 void CMergeEditView::OnContextMenu(CWnd* pWnd, CPoint point)
2826 // Create the menu and populate it with the available functions
2828 VERIFY(menu.LoadMenu(IDR_POPUP_MERGEVIEW));
2830 // Remove copying item copying from active side
2831 if (m_nThisPane == 0) // left?
2833 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2834 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2836 if (m_nThisPane == GetDocument()->m_nBuffers - 1)
2838 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2839 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2842 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
2843 theApp.TranslateMenu(menu.m_hMenu);
2845 BCMenu *pSub = static_cast<BCMenu *>(menu.GetSubMenu(0));
2846 ASSERT(pSub != nullptr);
2848 // Context menu opened using keyboard has no coordinates
2849 if (point.x == -1 && point.y == -1)
2852 GetClientRect(rect);
2853 ClientToScreen(rect);
2855 point = rect.TopLeft();
2859 pSub->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
2860 point.x, point.y, AfxGetMainWnd());
2865 * @brief Update EOL mode in status bar
2867 void CMergeEditView::OnUpdateStatusEOL(CCmdUI* pCmdUI)
2869 GetGroupView(pCmdUI->m_nID - ID_STATUS_PANE0FILE_EOL)->OnUpdateIndicatorCRLF(pCmdUI);
2873 * @brief Change EOL mode and unify all the lines EOL to this new mode
2875 void CMergeEditView::OnConvertEolTo(UINT nID )
2877 CRLFSTYLE nStyle = CRLF_STYLE_AUTOMATIC;;
2881 nStyle = CRLF_STYLE_DOS;
2883 case ID_EOL_TO_UNIX:
2884 nStyle = CRLF_STYLE_UNIX;
2887 nStyle = CRLF_STYLE_MAC;
2891 _RPTF0(_CRT_ERROR, "Unhandled EOL type conversion!");
2894 m_pTextBuffer->SetCRLFMode(nStyle);
2896 // we don't need a derived applyEOLMode for ghost lines as they have no EOL char
2897 if (m_pTextBuffer->applyEOLMode())
2899 CMergeDoc *pd = GetDocument();
2900 ASSERT(pd != nullptr);
2901 pd->UpdateHeaderPath(m_nThisPane);
2902 pd->FlushAndRescan(true);
2907 * @brief allow convert to entries in file submenu
2909 void CMergeEditView::OnUpdateConvertEolTo(CCmdUI* pCmdUI)
2911 int nStyle = CRLF_STYLE_AUTOMATIC;
2912 switch (pCmdUI->m_nID)
2915 nStyle = CRLF_STYLE_DOS;
2917 case ID_EOL_TO_UNIX:
2918 nStyle = CRLF_STYLE_UNIX;
2921 nStyle = CRLF_STYLE_MAC;
2925 _RPTF0(_CRT_ERROR, "Missing menuitem handler for EOL convert menu!");
2929 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2930 GetDocument()->IsMixedEOL(m_nThisPane) ||
2931 nStyle != m_pTextBuffer->GetCRLFMode())
2933 pCmdUI->SetRadio(false);
2935 // Don't allow selecting other EOL style for protected pane
2936 if (!QueryEditable())
2937 pCmdUI->Enable(false);
2940 pCmdUI->SetRadio(true);
2944 * @brief Copy diff from left to right and advance to next diff
2946 void CMergeEditView::OnL2RNext()
2949 if (IsCursorInDiff()) // for 3-way file compare
2955 * @brief Update "Copy right and advance" UI item
2957 void CMergeEditView::OnUpdateL2RNext(CCmdUI* pCmdUI)
2959 OnUpdateL2r(pCmdUI);
2963 * @brief Copy diff from right to left and advance to next diff
2965 void CMergeEditView::OnR2LNext()
2968 if (IsCursorInDiff()) // for 3-way file compare
2974 * @brief Update "Copy left and advance" UI item
2976 void CMergeEditView::OnUpdateR2LNext(CCmdUI* pCmdUI)
2978 OnUpdateR2l(pCmdUI);
2982 * @brief Change active pane in MergeView.
2983 * Changes active pane and makes sure cursor position is kept in
2984 * screen. Currently we put cursor in same line than in original
2985 * active pane but we could be smarter too? Maybe update cursor
2986 * only when it is not visible in new pane?
2988 void CMergeEditView::OnChangePane()
2990 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
2991 CMergeEditView *pWnd = static_cast<CMergeEditView*>(pSplitterWnd->GetActivePane());
2992 CMergeDoc *pDoc = GetDocument();
2993 bool bFound = false;
2994 CMergeEditView *pNextActiveView = nullptr;
2995 std::vector<CMergeEditView *> list = pDoc->GetViewList();
2996 list.insert(list.end(), list.begin(), list.end());
2997 for (auto& pView : list)
2999 if (bFound && pView->m_bDetailView == pWnd->m_bDetailView)
3001 pNextActiveView = pView;
3007 GetParentFrame()->SetActiveView(pNextActiveView);
3008 CPoint ptCursor = pWnd->GetCursorPos();
3010 if (ptCursor.y >= pNextActiveView->GetLineCount())
3011 ptCursor.y = pNextActiveView->GetLineCount() - 1;
3012 pNextActiveView->SetCursorPos(ptCursor);
3013 pNextActiveView->SetAnchor(ptCursor);
3014 pNextActiveView->SetSelection(ptCursor, ptCursor);
3018 * @brief Show "Go To" dialog and scroll views to line or diff.
3020 * Before dialog is opened, current line and file is determined
3022 * @note Conversions needed between apparent and real lines
3024 void CMergeEditView::OnWMGoto()
3027 CMergeDoc *pDoc = GetDocument();
3028 CPoint pos = GetCursorPos();
3032 nRealLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(pos.y);
3033 int nLineCount = pDoc->m_ptBuf[m_nThisPane]->GetLineCount();
3034 nLastLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(nLineCount - 1);
3036 // Set active file and current line selected in dialog
3037 dlg.m_strParam = strutils::to_str(nRealLine + 1);
3038 dlg.m_nFile = (pDoc->m_nBuffers < 3) ? (m_nThisPane == 1 ? 2 : 0) : m_nThisPane;
3039 dlg.m_nGotoWhat = 0;
3041 if (dlg.DoModal() == IDOK)
3043 CMergeDoc * pDoc1 = GetDocument();
3044 CMergeEditView * pCurrentView = nullptr;
3047 pCurrentView = GetGroupView(m_nThisPane);
3050 try { num = std::stoi(dlg.m_strParam) - 1; } catch(...) {}
3052 if (dlg.m_nGotoWhat == 0)
3054 int nRealLine1 = num;
3057 if (nRealLine1 > nLastLine)
3058 nRealLine1 = nLastLine;
3060 GotoLine(nRealLine1, true, (pDoc1->m_nBuffers < 3) ? (dlg.m_nFile == 2 ? 1 : 0) : dlg.m_nFile);
3067 if (diff >= pDoc1->m_diffList.GetSize())
3068 diff = pDoc1->m_diffList.GetSize();
3070 pCurrentView->SelectDiff(diff, true, false);
3075 void CMergeEditView::OnShellMenu()
3077 CFrameWnd *pFrame = GetTopLevelFrame();
3078 ASSERT(pFrame != nullptr);
3079 BOOL bAutoMenuEnableOld = pFrame->m_bAutoMenuEnable;
3080 pFrame->m_bAutoMenuEnable = FALSE;
3082 String path = GetDocument()->m_filePaths[m_nThisPane];
3083 std::unique_ptr<CShellContextMenu> pContextMenu(new CShellContextMenu(0x9000, 0x9FFF));
3084 pContextMenu->Initialize();
3085 pContextMenu->AddItem(paths::GetParentPath(path), paths::FindFileName(path));
3086 pContextMenu->RequeryShellContextMenu();
3088 ::GetCursorPos(&point);
3089 HWND hWnd = GetSafeHwnd();
3090 BOOL nCmd = TrackPopupMenu(pContextMenu->GetHMENU(), TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, 0, hWnd, nullptr);
3092 pContextMenu->InvokeCommand(nCmd, hWnd);
3093 pContextMenu->ReleaseShellContextMenu();
3095 pFrame->m_bAutoMenuEnable = bAutoMenuEnableOld;
3098 void CMergeEditView::OnUpdateShellMenu(CCmdUI* pCmdUI)
3100 pCmdUI->Enable(!GetDocument()->m_filePaths[m_nThisPane].empty());
3104 * @brief Reload options.
3106 void CMergeEditView::RefreshOptions()
3108 RENDERING_MODE nRenderingMode = static_cast<RENDERING_MODE>(GetOptionsMgr()->GetInt(OPT_RENDERING_MODE));
3109 SetRenderingMode(nRenderingMode);
3111 m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
3113 if (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0)
3114 SetInsertTabs(true);
3116 SetInsertTabs(false);
3118 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3120 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
3121 SetTextType(CrystalLineParser::SRC_PLAIN);
3123 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3124 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3126 SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3127 SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE),
3128 GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3129 GetDocument()->IsMixedEOL(m_nThisPane));
3131 Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
3134 void CMergeEditView::OnScripts(UINT nID )
3136 // text is CHAR if compiled without UNICODE, WCHAR with UNICODE
3137 String text = GetSelectedText();
3139 // transform the text with a script/ActiveX function, event=EDITOR_SCRIPT
3140 bool bChanged = FileTransform::Interactive(text, L"EDITOR_SCRIPT", nID - ID_SCRIPT_FIRST);
3142 // now replace the text
3143 ReplaceSelection(text.c_str(), text.length(), 0);
3147 * @brief Called when an editor script item is updated
3149 void CMergeEditView::OnUpdateNoEditScripts(CCmdUI* pCmdUI)
3151 // append the scripts submenu
3152 HMENU scriptsSubmenu = dynamic_cast<CMainFrame*>(AfxGetMainWnd())->GetScriptsSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu);
3153 if (scriptsSubmenu != nullptr)
3154 createScriptsSubmenu(scriptsSubmenu);
3156 pCmdUI->Enable(true);
3160 * @brief Called when an editor script item is updated
3162 void CMergeEditView::OnUpdatePrediffer(CCmdUI* pCmdUI)
3164 pCmdUI->Enable(true);
3166 CMergeDoc *pd = GetDocument();
3167 ASSERT(pd != nullptr);
3168 PrediffingInfo prediffer;
3169 pd->GetPrediffer(&prediffer);
3171 if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
3173 pCmdUI->SetRadio(false);
3177 // Detect when CDiffWrapper::RunFileDiff has canceled a buggy prediffer
3178 if (prediffer.m_PluginName.empty())
3179 m_CurrentPredifferID = ID_NO_PREDIFFER;
3181 pCmdUI->SetRadio(pCmdUI->m_nID == static_cast<UINT>(m_CurrentPredifferID));
3185 * @brief Update "Prediffer" menuitem
3187 void CMergeEditView::OnUpdateNoPrediffer(CCmdUI* pCmdUI)
3189 // recreate the sub menu (to fill the "selected prediffers")
3190 GetMainFrame()->UpdatePrediffersMenu();
3194 void CMergeEditView::OnNoPrediffer()
3196 OnPrediffer(ID_NO_PREDIFFER);
3199 * @brief Handler for all prediffer choices, including ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, ID_NO_PREDIFFER, & specific prediffers
3201 void CMergeEditView::OnPrediffer(UINT nID )
3203 CMergeDoc *pd = GetDocument();
3204 ASSERT(pd != nullptr);
3206 SetPredifferByMenu(nID);
3207 pd->FlushAndRescan(true);
3211 * @brief Handler for all prediffer choices.
3212 * Prediffer choises include ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO,
3213 * ID_NO_PREDIFFER, & specific prediffers.
3215 void CMergeEditView::SetPredifferByMenu(UINT nID )
3217 CMergeDoc *pd = GetDocument();
3218 ASSERT(pd != nullptr);
3220 if (nID == ID_NO_PREDIFFER)
3222 m_CurrentPredifferID = nID;
3223 // All flags are set correctly during the construction
3224 PrediffingInfo *infoPrediffer = new PrediffingInfo;
3225 infoPrediffer->m_PluginOrPredifferMode = PLUGIN_MANUAL;
3226 infoPrediffer->m_PluginName.clear();
3227 pd->SetPrediffer(infoPrediffer);
3228 pd->FlushAndRescan(true);
3232 // get the scriptlet files
3233 PluginArray * piScriptArray =
3234 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3235 PluginArray * piScriptArray2 =
3236 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3238 // build a PrediffingInfo structure fom the ID
3239 PrediffingInfo prediffer;
3240 prediffer.m_PluginOrPredifferMode = PLUGIN_MANUAL;
3242 size_t pluginNumber = nID - ID_PREDIFFERS_FIRST;
3243 if (pluginNumber < piScriptArray->size())
3245 const PluginInfoPtr & plugin = piScriptArray->at(pluginNumber);
3246 prediffer.m_PluginName = plugin->m_name;
3250 pluginNumber -= piScriptArray->size();
3251 if (pluginNumber >= piScriptArray2->size())
3253 const PluginInfoPtr & plugin = piScriptArray2->at(pluginNumber);
3254 prediffer.m_PluginName = plugin->m_name;
3257 // update data for the radio button
3258 m_CurrentPredifferID = nID;
3260 // update the prediffer and rescan
3261 pd->SetPrediffer(&prediffer);
3265 * @brief Look through available prediffers, and return ID of requested one, if found
3267 int CMergeEditView::FindPrediffer(LPCTSTR prediffer) const
3270 int ID = ID_PREDIFFERS_FIRST;
3272 // Search file prediffers
3273 PluginArray * piScriptArray =
3274 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3275 for (i=0; i<piScriptArray->size(); ++i, ++ID)
3277 const PluginInfoPtr & plugin = piScriptArray->at(i);
3278 if (plugin->m_name == prediffer)
3282 // Search buffer prediffers
3283 PluginArray * piScriptArray2 =
3284 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3285 for (i=0; i<piScriptArray2->size(); ++i, ++ID)
3287 const PluginInfoPtr & plugin = piScriptArray2->at(i);
3288 if (plugin->m_name == prediffer)
3296 * @brief Look through available prediffers, and return ID of requested one, if found
3298 bool CMergeEditView::SetPredifferByName(const CString & prediffer)
3300 int id = FindPrediffer(prediffer);
3301 if (id<0) return false;
3302 SetPredifferByMenu(id);
3307 * @brief Goto given line.
3308 * @param [in] nLine Destination linenumber
3309 * @param [in] bRealLine if true linenumber is real line, otherwise
3310 * it is apparent line (including deleted lines)
3311 * @param [in] pane Pane index of goto target pane (0 = left, 1 = right).
3313 void CMergeEditView::GotoLine(UINT nLine, bool bRealLine, int pane)
3315 CMergeDoc *pDoc = GetDocument();
3316 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3317 CMergeEditView *pCurrentView = nullptr;
3318 if (pSplitterWnd != nullptr)
3319 pCurrentView = static_cast<CMergeEditView*>
3320 (pSplitterWnd->GetActivePane());
3322 int nRealLine = nLine;
3323 int nApparentLine = nLine;
3325 // Compute apparent (shown linenumber) line
3328 if (nRealLine > pDoc->m_ptBuf[pane]->GetLineCount() - 1)
3329 nRealLine = pDoc->m_ptBuf[pane]->GetLineCount() - 1;
3331 nApparentLine = pDoc->m_ptBuf[pane]->ComputeApparentLine(nRealLine);
3335 ptPos.y = nApparentLine;
3337 // Scroll line to center of view
3338 int nScrollLine = GetSubLineIndex(nApparentLine);
3339 nScrollLine -= GetScreenLines() / 2;
3340 if (nScrollLine < 0)
3343 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3345 CMergeEditView *pView = GetGroupView(nPane);
3346 pView->ScrollToSubLine(nScrollLine);
3347 if (ptPos.y < pView->GetLineCount())
3349 pView->SetCursorPos(ptPos);
3350 pView->SetAnchor(ptPos);
3354 CPoint ptPos1(0, pView->GetLineCount() - 1);
3355 pView->SetCursorPos(ptPos1);
3356 pView->SetAnchor(ptPos1);
3360 // If goto target is another view - activate another view.
3361 // This is done for user convenience as user probably wants to
3362 // work with goto target file.
3363 if (GetGroupView(pane) != pCurrentView)
3365 if (pSplitterWnd != nullptr)
3367 if (pSplitterWnd->GetColumnCount() > 1)
3368 pSplitterWnd->SetActivePane(0, pane);
3370 pSplitterWnd->SetActivePane(pane, 0);
3376 * @brief Check for horizontal scroll. Re-route to CSplitterEx if not from
3379 void CMergeEditView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3381 if (pScrollBar == nullptr)
3383 // Scroll did not come frome a scroll bar
3384 // Find the appropriate scroll bar
3385 // and send the message to the splitter window instead
3386 // The event should eventually come back here but with a valid scrollbar
3387 // Along the way it will be propagated to other windows that need it
3388 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3389 CScrollBar* curBar = this->GetScrollBarCtrl(SB_HORZ);
3390 pSplitterWnd->SendMessage(WM_HSCROLL,
3391 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3394 CCrystalTextView::OnHScroll (nSBCode, nPos, pScrollBar);
3398 * @brief When view is scrolled using scrollbars update location pane.
3400 void CMergeEditView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3402 if (pScrollBar == nullptr)
3404 // Scroll did not come frome a scroll bar
3405 // Find the appropriate scroll bar
3406 // and send the message to the splitter window instead
3407 // The event should eventually come back here but with a valid scrollbar
3408 // Along the way it will be propagated to other windows that need it
3409 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3410 CScrollBar* curBar = this->GetScrollBarCtrl(SB_VERT);
3411 pSplitterWnd->SendMessage(WM_VSCROLL,
3412 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3415 CCrystalTextView::OnVScroll (nSBCode, nPos, pScrollBar);
3417 if (nSBCode == SB_ENDSCROLL)
3420 // Note we cannot use nPos because of its 16-bit nature
3421 SCROLLINFO si = {0};
3422 si.cbSize = sizeof (si);
3423 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3424 VERIFY (GetScrollInfo (SB_VERT, &si));
3426 // Get the current position of scroll box.
3427 int nCurPos = si.nPos;
3429 UpdateLocationViewPosition(nCurPos, nCurPos + GetScreenLines());
3433 * @brief Copy selected lines adding linenumbers.
3435 void CMergeEditView::OnEditCopyLineNumbers()
3443 CMergeDoc *pDoc = GetDocument();
3444 GetSelection(ptStart, ptEnd);
3446 // Get last selected line (having widest linenumber)
3447 int line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(ptEnd.y);
3448 size_t nNumWidth = strutils::to_str(line + 1).length();
3450 for (int i = ptStart.y; i <= ptEnd.y; i++)
3452 if (GetLineFlags(i) & LF_GHOST || (GetEnableHideLines() && (GetLineFlags(i) & LF_INVISIBLE)))
3455 // We need to convert to real linenumbers
3456 line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(i);
3458 // Insert spaces to align different width linenumbers (99, 100)
3459 strLine = GetLineText(i);
3460 CString sSpaces(' ', static_cast<int>(nNumWidth - strutils::to_str(line + 1).length()));
3463 strNumLine.Format(_T("%d: %s"), line + 1, (LPCTSTR)strLine);
3464 strText += strNumLine;
3466 PutToClipboard(strText, strText.GetLength(), m_bRectangularSelection);
3469 void CMergeEditView::OnUpdateEditCopyLinenumbers(CCmdUI* pCmdUI)
3471 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
3475 * @brief Open active file with associated application.
3477 * First tries to open file using shell 'Edit' action, since that
3478 * action open scripts etc. to editor instead of running them. If
3479 * edit-action is not registered, 'Open' action is used.
3481 void CMergeEditView::OnOpenFile()
3483 CMergeDoc * pDoc = GetDocument();
3484 ASSERT(pDoc != nullptr);
3486 String sFileName = pDoc->m_filePaths[m_nThisPane];
3487 if (sFileName.empty())
3489 HINSTANCE rtn = ShellExecute(::GetDesktopWindow(), _T("edit"), sFileName.c_str(),
3490 0, 0, SW_SHOWNORMAL);
3491 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3492 rtn = ShellExecute(::GetDesktopWindow(), _T("open"), sFileName.c_str(),
3493 0, 0, SW_SHOWNORMAL);
3494 if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3499 * @brief Open active file with app selection dialog
3501 void CMergeEditView::OnOpenFileWith()
3503 CMergeDoc * pDoc = GetDocument();
3504 ASSERT(pDoc != nullptr);
3506 String sFileName = pDoc->m_filePaths[m_nThisPane];
3507 if (sFileName.empty())
3511 if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH))
3513 sysdir.ReleaseBuffer();
3514 CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + sFileName.c_str();
3515 ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg,
3516 sysdir, SW_SHOWNORMAL);
3520 * @brief Open active file with external editor
3522 void CMergeEditView::OnOpenFileWithEditor()
3524 CMergeDoc * pDoc = GetDocument();
3525 ASSERT(pDoc != nullptr);
3527 String sFileName = pDoc->m_filePaths[m_nThisPane];
3528 if (sFileName.empty())
3531 int nRealLine = ComputeRealLine(GetCursorPos().y) + 1;
3532 theApp.OpenFileToExternalEditor(sFileName, nRealLine);
3536 * @brief Force repaint of the location pane.
3538 void CMergeEditView::RepaintLocationPane()
3540 // Must force recalculation due to caching of data in location pane.
3541 CLocationView *pLocationView = GetDocument()->GetLocationView();
3542 if (pLocationView != nullptr)
3543 pLocationView->ForceRecalculate();
3547 * @brief Enables/disables linediff (different color for diffs)
3549 void CMergeEditView::OnViewLineDiffs()
3551 bool bWordDiffHighlight = GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
3552 GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, !bWordDiffHighlight);
3554 // Call CMergeDoc RefreshOptions() to refresh *both* views
3555 CMergeDoc *pDoc = GetDocument();
3556 pDoc->RefreshOptions();
3557 pDoc->FlushAndRescan(true);
3560 void CMergeEditView::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
3562 pCmdUI->Enable(true);
3563 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
3567 * @brief Enables/disables line number
3569 void CMergeEditView::OnViewLineNumbers()
3571 GetOptionsMgr()->SaveOption(OPT_VIEW_LINENUMBERS, !GetViewLineNumbers());
3573 // Call CMergeDoc RefreshOptions() to refresh *both* views
3574 CMergeDoc *pDoc = GetDocument();
3575 pDoc->RefreshOptions();
3578 void CMergeEditView::OnUpdateViewLineNumbers(CCmdUI* pCmdUI)
3580 pCmdUI->Enable(true);
3581 pCmdUI->SetCheck(GetViewLineNumbers());
3585 * @brief Enables/disables word wrap
3587 void CMergeEditView::OnViewWordWrap()
3589 GetOptionsMgr()->SaveOption(OPT_WORDWRAP, !m_bWordWrap);
3591 // Call CMergeDoc RefreshOptions() to refresh *both* views
3592 CMergeDoc *pDoc = GetDocument();
3593 pDoc->RefreshOptions();
3594 pDoc->UpdateAllViews(this);
3599 void CMergeEditView::OnUpdateViewWordWrap(CCmdUI* pCmdUI)
3601 pCmdUI->Enable(true);
3602 pCmdUI->SetCheck(m_bWordWrap);
3605 void CMergeEditView::OnViewWhitespace()
3607 GetOptionsMgr()->SaveOption(OPT_VIEW_WHITESPACE, !GetViewTabs());
3609 // Call CMergeDoc RefreshOptions() to refresh *both* views
3610 CMergeDoc *pDoc = GetDocument();
3611 pDoc->RefreshOptions();
3614 void CMergeEditView::OnUpdateViewWhitespace(CCmdUI* pCmdUI)
3616 pCmdUI->SetCheck(GetViewTabs());
3619 void CMergeEditView::OnSize(UINT nType, int cx, int cy)
3621 if (!IsInitialized())
3624 CMergeDoc * pDoc = GetDocument();
3625 if (m_nThisPane < pDoc->m_nBuffers - 1)
3627 // To calculate subline index correctly
3628 // we have to invalidate line cache in all pane before calling the function related the subline.
3629 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3631 CMergeEditView *pView = GetGroupView(nPane);
3632 if (pView != nullptr)
3633 pView->InvalidateScreenRect(false);
3638 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3640 CMergeEditView *pView = GetGroupView(nPane);
3641 if (pView != nullptr)
3642 pView->Invalidate();
3645 // recalculate m_nTopSubLine
3646 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
3650 RecalcVertScrollBar (false, false);
3651 RecalcHorzScrollBar (false, false);
3655 * @brief allocates GDI resources for printing
3656 * @param pDC [in] points to the printer device context
3657 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3659 void CMergeEditView::OnBeginPrinting(CDC * pDC, CPrintInfo * pInfo)
3661 GetParentFrame()->PostMessage(WM_TIMER);
3663 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3665 CMergeEditView *pView = GetDocument()->GetView(m_nThisGroup, pane);
3666 pView->m_bPrintHeader = true;
3667 pView->m_bPrintFooter = true;
3668 pView->CGhostTextView::OnBeginPrinting(pDC, pInfo);
3673 * @brief frees GDI resources for printing
3674 * @param pDC [in] points to the printer device context
3675 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3677 void CMergeEditView::OnEndPrinting(CDC * pDC, CPrintInfo * pInfo)
3679 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3680 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::OnEndPrinting(pDC, pInfo);
3682 GetParentFrame()->PostMessage(WM_TIMER);
3686 * @brief Gets header text to print
3687 * @param [in] nPageNum the page number to print
3688 * @param [out] header text to print
3690 void CMergeEditView::GetPrintHeaderText(int nPageNum, CString & text)
3692 text = GetDocument()->GetTitle();
3696 * @brief Prints header
3697 * @param [in] nPageNum the page number to print
3699 void CMergeEditView::PrintHeader(CDC * pdc, int nPageNum)
3701 if (m_nThisPane > 0)
3703 int oldRight = m_rcPrintArea.right;
3704 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3705 CGhostTextView::PrintHeader(pdc, nPageNum);
3706 m_rcPrintArea.right = oldRight;
3710 * @brief Prints footer
3711 * @param [in] nPageNum the page number to print
3713 void CMergeEditView::PrintFooter(CDC * pdc, int nPageNum)
3715 if (m_nThisPane > 0)
3717 int oldRight = m_rcPrintArea.right;
3718 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3719 CGhostTextView::PrintFooter(pdc, nPageNum);
3720 m_rcPrintArea.right = oldRight;
3723 void CMergeEditView::RecalcPageLayouts (CDC * pDC, CPrintInfo * pInfo)
3725 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3726 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::RecalcPageLayouts(pDC, pInfo);
3730 * @brief Prints or previews both panes.
3731 * @param pDC [in] points to the printer device context
3732 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3734 void CMergeEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
3736 CRect rDraw = pInfo->m_rectDraw;
3737 CSize sz = rDraw.Size();
3738 CMergeDoc *pDoc = GetDocument();
3740 SIZE szLeftTop, szRightBottom;
3741 GetPrintMargins(szLeftTop.cx, szLeftTop.cy, szRightBottom.cx, szRightBottom.cy);
3742 pDC->HIMETRICtoLP(&szLeftTop);
3743 pDC->HIMETRICtoLP(&szRightBottom);
3745 int midX = (sz.cx - szLeftTop.cx - szRightBottom.cx) / pDoc->m_nBuffers;
3748 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
3750 pInfo->m_rectDraw.left = rDraw.left + midX * pane;
3751 pInfo->m_rectDraw.right = pInfo->m_rectDraw.left + midX + szLeftTop.cx + szRightBottom.cx;
3752 CMergeEditView* pPane = pDoc->GetView(m_nThisGroup, pane);
3753 pPane->CGhostTextView::OnPrint(pDC, pInfo);
3757 bool CMergeEditView::IsInitialized() const
3759 CMergeEditView * pThis = const_cast<CMergeEditView *>(this);
3760 CDiffTextBuffer * pBuffer = dynamic_cast<CDiffTextBuffer *>(pThis->LocateTextBuffer());
3761 return pBuffer->IsInitialized();
3765 * @brief returns the number of empty lines which are added for synchronizing the line in two/three panes.
3767 int CMergeEditView::GetEmptySubLines( int nLineIndex )
3769 int nBreaks[3] = {0};
3770 int nMaxBreaks = -1;
3771 CMergeDoc * pDoc = GetDocument();
3772 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3774 CMergeEditView *pView = GetGroupView(nPane);
3775 if (pView != nullptr)
3777 if (nLineIndex >= pView->GetLineCount())
3779 pView->WrapLineCached( nLineIndex, pView->GetScreenChars(), nullptr, nBreaks[nPane] );
3781 nMaxBreaks = max(nMaxBreaks, nBreaks[nPane]);
3784 if (nBreaks[m_nThisPane] < nMaxBreaks)
3785 return nMaxBreaks - nBreaks[m_nThisPane];
3791 * @brief Invalidate sub line index cache from the specified index to the end of file.
3792 * @param [in] nLineIndex Index of the first line to invalidate
3794 void CMergeEditView::InvalidateSubLineIndexCache( int nLineIndex )
3796 CMergeDoc * pDoc = GetDocument();
3797 ASSERT(pDoc != nullptr);
3799 // We have to invalidate sub line index cache on both panes.
3800 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3802 CMergeEditView *pView = GetGroupView(nPane);
3803 if (pView != nullptr)
3804 pView->CCrystalTextView::InvalidateSubLineIndexCache( nLineIndex );
3808 void CMergeEditView::SetWordWrapping( bool bWordWrap )
3810 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3811 GetGroupView(pane)->m_bWordWrap = bWordWrap;
3812 CCrystalTextView::SetWordWrapping(bWordWrap);
3816 * @brief Swap the positions of the two panes
3818 void CMergeEditView::OnViewSwapPanes()
3820 GetDocument()->SwapFiles();
3824 * @brief Determine if difference is visible on screen.
3825 * @param [in] nDiff Number of diff to check.
3826 * @return true if difference is visible.
3828 bool CMergeEditView::IsDiffVisible(int nDiff)
3830 const CMergeDoc *pd = GetDocument();
3833 pd->m_diffList.GetDiff(nDiff, diff);
3835 return IsDiffVisible(diff);
3839 * @brief Determine if difference is visible on screen.
3840 * @param [in] diff diff to check.
3841 * @param [in] nLinesBelow Allow "minimizing" the number of visible lines.
3842 * @return true if difference is visible, false otherwise.
3844 bool CMergeEditView::IsDiffVisible(const DIFFRANGE& diff, int nLinesBelow /*=0*/)
3846 const int nDiffStart = GetSubLineIndex(diff.dbegin);
3847 const int nDiffEnd = GetSubLineIndex(diff.dend);
3848 // Diff's height is last line - first line + last line's line count
3849 const int nDiffHeight = nDiffEnd - nDiffStart + GetSubLines(diff.dend) + 1;
3851 // If diff first line outside current view - context OR
3852 // if diff last line outside current view - context OR
3853 // if diff is bigger than screen
3854 if ((nDiffStart < m_nTopSubLine) ||
3855 (nDiffEnd >= m_nTopSubLine + GetScreenLines() - nLinesBelow) ||
3856 (nDiffHeight >= GetScreenLines()))
3866 /** @brief Open help from mainframe when user presses F1*/
3867 void CMergeEditView::OnHelp()
3869 theApp.ShowHelp(MergeViewHelpLocation);
3873 * @brief Called after document is loaded.
3874 * This function is called from CMergeDoc::OpenDocs() after documents are
3875 * loaded. So this is good place to set View's options etc.
3877 void CMergeEditView::DocumentsLoaded()
3879 if (GetDocument()->m_ptBuf[m_nThisPane]->GetTableEditing())
3882 if (m_nThisPane == GetDocument()->m_nBuffers - 1 && !m_bDetailView)
3887 SetTopMargin(false);
3890 // Enable/disable automatic rescan (rescanning after edit)
3891 EnableRescan(GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN));
3893 // SetTextType will revert to language dependent defaults for tab
3894 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
3895 SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3896 const bool mixedEOLs = GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3897 GetDocument()->IsMixedEOL(m_nThisPane);
3898 SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE), mixedEOLs);
3899 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3900 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3901 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3903 // Enable Backspace at beginning of line
3904 SetDisableBSAtSOL(false);
3906 // Set tab type (tabs/spaces)
3907 bool bInsertTabs = (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0);
3908 SetInsertTabs(bInsertTabs);
3910 // Sometimes WinMerge doesn't update scrollbars correctly (they remain
3911 // disabled) after docs are open in screen. So lets make sure they are
3912 // really updated, even though this is unnecessary in most cases.
3913 RecalcHorzScrollBar();
3914 RecalcVertScrollBar();
3918 * @brief Update LocationView position.
3919 * This function updates LocationView position to given lines.
3920 * Usually we want to lines in file compare view and area in
3921 * LocationView to match. Be extra carefull to not call non-existing
3923 * @param [in] nTopLine Top line of current view.
3924 * @param [in] nBottomLine Bottom line of current view.
3926 void CMergeEditView::UpdateLocationViewPosition(int nTopLine /*=-1*/,
3927 int nBottomLine /*= -1*/)
3929 CMergeDoc *pDoc = GetDocument();
3930 if (pDoc == nullptr)
3933 CLocationView *pLocationView = pDoc->GetLocationView();
3935 if (pLocationView != nullptr && IsWindow(pLocationView->GetSafeHwnd()))
3937 pLocationView->UpdateVisiblePos(nTopLine, nBottomLine);
3942 * @brief Enable/Disable view's selection margins.
3943 * Selection margins show bookmarks and word-wrap symbols, so they are pretty
3944 * useful. But it appears many users don't use/need those features and for them
3945 * selection margins are just wasted screen estate.
3947 void CMergeEditView::OnViewMargin()
3949 bool bViewMargin = GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN);
3950 GetOptionsMgr()->SaveOption(OPT_VIEW_FILEMARGIN, !bViewMargin);
3952 SetSelectionMargin(!bViewMargin);
3953 CMergeDoc *pDoc = GetDocument();
3954 pDoc->RefreshOptions();
3955 pDoc->UpdateAllViews(this);
3959 * @brief Update GUI for Enable/Disable view's selection margin.
3960 * @param [in] pCmdUI Pointer to UI item to update.
3962 void CMergeEditView::OnUpdateViewMargin(CCmdUI* pCmdUI)
3964 pCmdUI->Enable(true);
3965 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3969 * @brief Create the "Change Scheme" sub menu.
3970 * @param [in] pCmdUI Pointer to UI item to update.
3972 void CMergeEditView::OnUpdateViewChangeScheme(CCmdUI *pCmdUI)
3974 // Delete the place holder menu.
3975 pCmdUI->m_pSubMenu->DeleteMenu(0, MF_BYPOSITION);
3977 const HMENU hSubMenu = pCmdUI->m_pSubMenu->m_hMenu;
3979 String name = theApp.LoadString(ID_COLORSCHEME_FIRST);
3980 AppendMenu(hSubMenu, MF_STRING, ID_COLORSCHEME_FIRST, name.c_str());
3981 AppendMenu(hSubMenu, MF_SEPARATOR, 0, nullptr);
3983 for (int i = ID_COLORSCHEME_FIRST + 1; i <= ID_COLORSCHEME_LAST; ++i)
3985 name = theApp.LoadString(i);
3986 AppendMenu(hSubMenu, MF_STRING, i, name.c_str());
3989 pCmdUI->Enable(true);
3993 * @brief Change the editor's syntax highlighting scheme.
3994 * @param [in] nID Selected color scheme sub menu id.
3996 void CMergeEditView::OnChangeScheme(UINT nID)
3998 CMergeDoc *pDoc = GetDocument();
3999 ASSERT(pDoc != nullptr);
4001 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
4003 CMergeEditView *pView = GetGroupView(nPane);
4004 ASSERT(pView != nullptr);
4006 if (pView != nullptr)
4008 pView->SetTextType(CrystalLineParser::TextType(nID - ID_COLORSCHEME_FIRST));
4009 pView->SetDisableBSAtSOL(false);
4013 pDoc->UpdateAllViews(nullptr);
4017 * @brief Enable all color schemes sub menu items.
4018 * @param [in] pCmdUI Pointer to UI item to update.
4020 void CMergeEditView::OnUpdateChangeScheme(CCmdUI* pCmdUI)
4022 const bool bIsCurrentScheme = (static_cast<UINT>(m_CurSourceDef->type) == (pCmdUI->m_nID - ID_COLORSCHEME_FIRST));
4023 pCmdUI->SetRadio(bIsCurrentScheme);
4024 pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT));
4028 * @brief Called when mouse's wheel is scrolled.
4030 BOOL CMergeEditView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
4032 if ( nFlags == MK_CONTROL )
4034 short amount = zDelta < 0 ? -1: 1;
4037 // no default CCrystalTextView
4038 return CView::OnMouseWheel(nFlags, zDelta, pt);
4041 if (nFlags == MK_SHIFT)
4043 SCROLLINFO si = { sizeof SCROLLINFO };
4044 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
4046 VERIFY(GetScrollInfo(SB_HORZ, &si));
4049 si.nPos -= zDelta / 40;
4050 if (si.nPos > si.nMax) si.nPos = si.nMax;
4051 if (si.nPos < si.nMin) si.nPos = si.nMin;
4053 SetScrollInfo(SB_HORZ, &si);
4056 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, si.nPos) , NULL );
4058 // no default CCrystalTextView
4059 return CView::OnMouseWheel(nFlags, zDelta, pt);
4062 return CGhostTextView::OnMouseWheel(nFlags, zDelta, pt);
4066 * @brief Called when mouse's horizontal wheel is scrolled.
4068 void CMergeEditView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
4070 SCROLLINFO si = { sizeof SCROLLINFO };
4071 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
4073 VERIFY(GetScrollInfo(SB_HORZ, &si));
4076 si.nPos += zDelta / 40;
4077 if (si.nPos > si.nMax) si.nPos = si.nMax;
4078 if (si.nPos < si.nMin) si.nPos = si.nMin;
4080 SetScrollInfo(SB_HORZ, &si);
4083 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, si.nPos) , NULL );
4085 // no default CCrystalTextView
4086 CView::OnMouseHWheel(nFlags, zDelta, pt);
4090 * @brief Change font size (zoom) in views.
4091 * @param [in] amount Amount of change/zoom, negative number makes
4092 * font smaller, positive number bigger and 0 reset the font size.
4094 void CMergeEditView::ZoomText(short amount)
4099 const int nLogPixelsY = CClientDC(this).GetDeviceCaps(LOGPIXELSY);
4100 int nPointSize = -MulDiv(lf.lfHeight, 72, nLogPixelsY);
4104 nPointSize = -MulDiv(GetOptionsMgr()->GetInt(OPT_FONT_FILECMP + OPT_FONT_HEIGHT), 72, nLogPixelsY);
4107 nPointSize += amount;
4111 lf.lfHeight = -MulDiv(nPointSize, nLogPixelsY, 72);
4113 CMergeDoc *pDoc = GetDocument();
4114 ASSERT(pDoc != nullptr);
4116 if (pDoc != nullptr)
4118 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
4120 CMergeEditView *pView = GetGroupView(nPane);
4121 ASSERT(pView != nullptr);
4123 if (pView != nullptr)
4131 bool CMergeEditView::QueryEditable()
4133 return m_bDetailView ? false : !GetDocument()->m_ptBuf[m_nThisPane]->GetReadOnly();
4137 * @brief Adjust the point to remain in the displayed diff
4139 * @return Tells if the point has been changed
4141 bool CMergeEditView::EnsureInDiff(CPoint& pt)
4143 int nLineCount = GetLineCount();
4144 if (m_lineBegin >= nLineCount)
4145 m_lineBegin = nLineCount - 1;
4146 if (m_lineEnd >= nLineCount)
4147 m_lineEnd = nLineCount - 1;
4149 int diffLength = m_lineEnd - m_lineBegin + 1;
4150 // first get the degenerate case out of the way
4152 if (diffLength == 0)
4154 if (pt.y == m_lineBegin && pt.x == 0)
4162 if (pt.y < m_lineBegin)
4168 // diff is defined and not below diff
4169 if (m_lineEnd > -1 && pt.y > m_lineEnd)
4172 pt.x = GetLineLength(pt.y);
4178 void CMergeEditView::EnsureVisible(CPoint pt)
4183 // ensure we remain in diff
4184 if (EnsureInDiff(ptNew))
4185 SetCursorPos(ptNew);
4187 CCrystalTextView::EnsureVisible(ptNew);
4190 void CMergeEditView::EnsureVisible(CPoint ptStart, CPoint ptEnd)
4192 CCrystalTextView::EnsureVisible(ptStart, ptEnd);
4195 void CMergeEditView::SetSelection(const CPoint& ptStart, const CPoint& ptEnd, bool bUpdateView)
4197 CPoint ptStartNew = ptStart;
4198 CPoint ptEndNew = ptEnd;
4201 // ensure we remain in diff
4202 EnsureInDiff(ptStartNew);
4203 EnsureInDiff(ptEndNew);
4205 CCrystalTextView::SetSelection(ptStartNew, ptEndNew, bUpdateView);
4208 void CMergeEditView::ScrollToSubLine(int nNewTopLine, bool bNoSmoothScroll /*= FALSE*/, bool bTrackScrollBar /*= TRUE*/)
4212 int nLineCount = GetLineCount();
4213 if (m_lineBegin >= nLineCount)
4214 m_lineBegin = nLineCount - 1;
4215 if (m_lineEnd >= nLineCount)
4216 m_lineEnd = nLineCount - 1;
4218 // ensure we remain in diff
4219 int sublineBegin = GetSubLineIndex(m_lineBegin);
4220 int sublineEnd = m_lineEnd < 0 ? -1 : GetSubLineIndex(m_lineEnd) + GetSubLines(m_lineEnd) - 1;
4221 int diffLength = sublineEnd - sublineBegin + 1;
4222 int displayLength = GetScreenLines();
4223 if (diffLength <= displayLength)
4224 nNewTopLine = sublineBegin;
4227 if (nNewTopLine < sublineBegin)
4228 nNewTopLine = sublineBegin;
4229 if (nNewTopLine + displayLength - 1 > sublineEnd)
4230 nNewTopLine = GetSubLineIndex(sublineEnd - displayLength + 1);
4233 CPoint pt = GetCursorPos();
4234 if (EnsureInDiff(pt))
4237 CPoint ptSelStart, ptSelEnd;
4238 GetSelection(ptSelStart, ptSelEnd);
4239 if (EnsureInDiff(ptSelStart) || EnsureInDiff(ptSelEnd))
4240 SetSelection(ptSelStart, ptSelEnd);
4242 CCrystalTextView::ScrollToSubLine(nNewTopLine, bNoSmoothScroll, bTrackScrollBar);
4246 * @brief Called when user selects View/Zoom In from menu.
4248 void CMergeEditView::OnViewZoomIn()
4254 * @brief Called when user selects View/Zoom Out from menu.
4256 void CMergeEditView::OnViewZoomOut()
4262 * @brief Called when user selects View/Zoom Normal from menu.
4264 void CMergeEditView::OnViewZoomNormal()
4269 void CMergeEditView::OnDropFiles(const std::vector<String>& tFiles)
4271 if (tFiles.size() > 1 || paths::IsDirectory(tFiles[0]))
4273 GetMainFrame()->GetDropHandler()->GetCallback()(tFiles);
4277 GetDocument()->ChangeFile(m_nThisPane, tFiles[0]);
4280 void CMergeEditView::OnWindowSplit()
4283 auto& wndSplitter = dynamic_cast<CMergeEditFrame *>(GetParentFrame())->GetSplitter();
4284 CMergeDoc *pDoc = GetDocument();
4285 CMergeEditView *pView = pDoc->GetView(0, m_nThisPane);
4286 auto* pwndSplitterChild = pView->GetParentSplitter(pView, false);
4287 int nBuffer = m_nThisPane;
4288 if (pDoc->m_nGroups <= 2)
4290 wndSplitter.SplitRow(1);
4291 wndSplitter.EqualizeRows();
4295 wndSplitter.SetActivePane(0, 0);
4296 wndSplitter.DeleteRow(1);
4297 if (pwndSplitterChild->GetColumnCount() > 1)
4298 pwndSplitterChild->SetActivePane(0, nBuffer);
4300 pwndSplitterChild->SetActivePane(nBuffer, 0);
4304 void CMergeEditView::OnUpdateWindowSplit(CCmdUI* pCmdUI)
4306 pCmdUI->Enable(!m_bDetailView);
4307 pCmdUI->SetCheck(GetDocument()->m_nGroups > 2);