1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /////////////////////////////////////////////////////////////////////////////
22 * @file MergeEditView.cpp
24 * @brief Implementation of the CMergeEditView class
26 // ID line follows -- this is updated by SVN
27 // $Id: MergeEditView.cpp 7142 2010-04-28 17:05:50Z kimmov $
33 #include "LocationView.h"
34 #include "MergeEditView.h"
35 #include "MergeDiffDetailView.h"
38 #include "OptionsMgr.h"
39 #include "WaitStatusCursor.h"
40 #include "FileTransform.h"
43 #include "WMGotoDlg.h"
44 #include "OptionsDef.h"
45 #include "SyntaxColors.h"
48 #include "MergeLineFlags.h"
49 #include "PluginsListDlg.h"
54 static char THIS_FILE[] = __FILE__;
59 /** @brief Timer ID for delayed rescan. */
60 const UINT IDT_RESCAN = 2;
61 /** @brief Timer timeout for delayed rescan. */
62 const UINT RESCAN_TIMEOUT = 1000;
64 /** @brief Location for file compare specific help to open. */
65 static TCHAR MergeViewHelpLocation[] = _T("::/htmlhelp/Compare_files.html");
67 /////////////////////////////////////////////////////////////////////////////
70 IMPLEMENT_DYNCREATE(CMergeEditView, CCrystalEditViewEx)
72 CMergeEditView::CMergeEditView()
73 : m_bCurrentLineIsDiff(false)
74 , m_pLocationView(NULL)
76 , m_piMergeEditStatus(0)
77 , m_bAutomaticRescan(false)
78 , fTimerWaitingForIdle(0)
80 SetParser(&m_xParser);
82 m_cachedColors.clrDiff = GetOptionsMgr()->GetInt(OPT_CLR_DIFF);
83 m_cachedColors.clrSelDiff = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_DIFF);
84 m_cachedColors.clrDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_DIFF_DELETED);
85 m_cachedColors.clrSelDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_DIFF_DELETED);
86 m_cachedColors.clrDiffText = GetOptionsMgr()->GetInt(OPT_CLR_DIFF_TEXT);
87 m_cachedColors.clrSelDiffText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_DIFF_TEXT);
88 m_cachedColors.clrTrivial = GetOptionsMgr()->GetInt(OPT_CLR_TRIVIAL_DIFF);
89 m_cachedColors.clrTrivialDeleted = GetOptionsMgr()->GetInt(OPT_CLR_TRIVIAL_DIFF_DELETED);
90 m_cachedColors.clrTrivialText = GetOptionsMgr()->GetInt(OPT_CLR_TRIVIAL_DIFF_TEXT);
91 m_cachedColors.clrMoved = GetOptionsMgr()->GetInt(OPT_CLR_MOVEDBLOCK);
92 m_cachedColors.clrMovedDeleted = GetOptionsMgr()->GetInt(OPT_CLR_MOVEDBLOCK_DELETED);
93 m_cachedColors.clrMovedText = GetOptionsMgr()->GetInt(OPT_CLR_MOVEDBLOCK_TEXT);
94 m_cachedColors.clrSelMoved = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_MOVEDBLOCK);
95 m_cachedColors.clrSelMovedDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_MOVEDBLOCK_DELETED);
96 m_cachedColors.clrSelMovedText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_MOVEDBLOCK_TEXT);
97 m_cachedColors.clrSNP = GetOptionsMgr()->GetInt(OPT_CLR_SNP);
98 m_cachedColors.clrSNPDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SNP_DELETED);
99 m_cachedColors.clrSNPText = GetOptionsMgr()->GetInt(OPT_CLR_SNP_TEXT);
100 m_cachedColors.clrSelSNP = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_SNP);
101 m_cachedColors.clrSelSNPDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_SNP_DELETED);
102 m_cachedColors.clrSelSNPText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_SNP_TEXT);
103 m_cachedColors.clrWordDiff = GetOptionsMgr()->GetInt(OPT_CLR_WORDDIFF);
104 m_cachedColors.clrWordDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_WORDDIFF_DELETED);
105 m_cachedColors.clrSelWordDiff = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_WORDDIFF);
106 m_cachedColors.clrSelWordDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_WORDDIFF_DELETED);
107 m_cachedColors.clrWordDiffText = GetOptionsMgr()->GetInt(OPT_CLR_WORDDIFF_TEXT);
108 m_cachedColors.clrSelWordDiffText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_WORDDIFF_TEXT);
111 CMergeEditView::~CMergeEditView()
116 BEGIN_MESSAGE_MAP(CMergeEditView, CCrystalEditViewEx)
117 //{{AFX_MSG_MAP(CMergeEditView)
118 ON_COMMAND(ID_CURDIFF, OnCurdiff)
119 ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
120 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
121 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
122 ON_COMMAND(ID_EDIT_CUT, OnEditCut)
123 ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
124 ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
125 ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
126 ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
127 ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
128 ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
129 ON_COMMAND(ID_LASTDIFF, OnLastdiff)
130 ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
131 ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
132 ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
133 ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
134 ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
135 ON_COMMAND(ID_NEXTDIFFLM, OnNextdiffLM)
136 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLM, OnUpdateNextdiffLM)
137 ON_COMMAND(ID_PREVDIFFLM, OnPrevdiffLM)
138 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLM, OnUpdatePrevdiffLM)
139 ON_COMMAND(ID_NEXTDIFFLR, OnNextdiffLR)
140 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLR, OnUpdateNextdiffLR)
141 ON_COMMAND(ID_PREVDIFFLR, OnPrevdiffLR)
142 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLR, OnUpdatePrevdiffLR)
143 ON_COMMAND(ID_NEXTDIFFMR, OnNextdiffMR)
144 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMR, OnUpdateNextdiffMR)
145 ON_COMMAND(ID_PREVDIFFMR, OnPrevdiffMR)
146 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMR, OnUpdatePrevdiffMR)
147 ON_COMMAND(ID_NEXTDIFFLO, OnNextdiffLO)
148 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLO, OnUpdateNextdiffLO)
149 ON_COMMAND(ID_PREVDIFFLO, OnPrevdiffLO)
150 ON_UPDATE_COMMAND_UI(ID_PREVDIFFLO, OnUpdatePrevdiffLO)
151 ON_COMMAND(ID_NEXTDIFFMO, OnNextdiffMO)
152 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMO, OnUpdateNextdiffMO)
153 ON_COMMAND(ID_PREVDIFFMO, OnPrevdiffMO)
154 ON_UPDATE_COMMAND_UI(ID_PREVDIFFMO, OnUpdatePrevdiffMO)
155 ON_COMMAND(ID_NEXTDIFFRO, OnNextdiffRO)
156 ON_UPDATE_COMMAND_UI(ID_NEXTDIFFRO, OnUpdateNextdiffRO)
157 ON_COMMAND(ID_PREVDIFFRO, OnPrevdiffRO)
158 ON_UPDATE_COMMAND_UI(ID_PREVDIFFRO, OnUpdatePrevdiffRO)
159 ON_WM_LBUTTONDBLCLK()
161 ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
162 ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
163 ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
164 ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
165 ON_COMMAND(ID_L2R, OnL2r)
166 ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
167 ON_COMMAND(ID_R2L, OnR2l)
168 ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
169 ON_COMMAND(ID_L2M, OnL2m)
170 ON_UPDATE_COMMAND_UI(ID_L2M, OnUpdateL2m)
171 ON_COMMAND(ID_R2M, OnR2m)
172 ON_UPDATE_COMMAND_UI(ID_R2M, OnUpdateR2m)
173 ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
174 ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
175 ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
177 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
178 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
179 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
180 ON_COMMAND(ID_REFRESH, OnRefresh)
181 ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
182 ON_COMMAND(ID_SELECTLINEDIFF, OnSelectLineDiff)
183 ON_UPDATE_COMMAND_UI(ID_SELECTLINEDIFF, OnUpdateSelectLineDiff)
185 ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateEditReplace)
186 ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
187 ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
188 ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
189 ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
190 ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
191 ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
192 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_RO, OnUpdateStatusRO)
193 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_RO, OnUpdateStatusRO)
194 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_RO, OnUpdateStatusRO)
195 ON_COMMAND_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnConvertEolTo)
196 ON_UPDATE_COMMAND_UI_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnUpdateConvertEolTo)
197 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_EOL, OnUpdateStatusEOL)
198 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_EOL, OnUpdateStatusEOL)
199 ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_EOL, OnUpdateStatusEOL)
200 ON_COMMAND(ID_L2RNEXT, OnL2RNext)
201 ON_UPDATE_COMMAND_UI(ID_L2RNEXT, OnUpdateL2RNext)
202 ON_COMMAND(ID_R2LNEXT, OnR2LNext)
203 ON_UPDATE_COMMAND_UI(ID_R2LNEXT, OnUpdateR2LNext)
204 ON_COMMAND(ID_WINDOW_CHANGE_PANE, OnChangePane)
205 ON_UPDATE_COMMAND_UI(ID_WINDOW_CHANGE_PANE, OnUpdateChangePane)
206 ON_COMMAND(ID_NEXT_PANE, OnChangePane)
207 ON_COMMAND(ID_EDIT_WMGOTO, OnWMGoto)
208 ON_UPDATE_COMMAND_UI(ID_EDIT_WMGOTO, OnUpdateWMGoto)
209 ON_COMMAND_RANGE(ID_SCRIPT_FIRST, ID_SCRIPT_LAST, OnScripts)
210 ON_UPDATE_COMMAND_UI_RANGE(ID_SCRIPT_FIRST, ID_SCRIPT_LAST, OnUpdateScripts)
211 ON_COMMAND(ID_NO_PREDIFFER, OnNoPrediffer)
212 ON_UPDATE_COMMAND_UI(ID_NO_PREDIFFER, OnUpdateNoPrediffer)
213 ON_COMMAND_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnPrediffer)
214 ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnUpdatePrediffer)
215 ON_COMMAND(ID_FILE_MERGINGMODE, OnMergingMode)
216 ON_UPDATE_COMMAND_UI(ID_FILE_MERGINGMODE, OnUpdateMergingMode)
217 ON_UPDATE_COMMAND_UI(ID_STATUS_MERGINGMODE, OnUpdateMergingStatus)
220 ON_COMMAND(ID_EDIT_COPY_LINENUMBERS, OnEditCopyLineNumbers)
221 ON_UPDATE_COMMAND_UI(ID_EDIT_COPY_LINENUMBERS, OnUpdateEditCopyLinenumbers)
222 ON_COMMAND(ID_VIEW_LINEDIFFS, OnViewLineDiffs)
223 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewLineDiffs)
224 ON_COMMAND(ID_VIEW_WORDWRAP, OnViewWordWrap)
225 ON_UPDATE_COMMAND_UI(ID_VIEW_WORDWRAP, OnUpdateViewWordWrap)
226 ON_COMMAND(ID_VIEW_LINENUMBERS, OnViewLineNumbers)
227 ON_UPDATE_COMMAND_UI(ID_VIEW_LINENUMBERS, OnUpdateViewLineNumbers)
228 ON_COMMAND(ID_FILE_OPEN_REGISTERED, OnOpenFile)
229 ON_COMMAND(ID_FILE_OPEN_WITHEDITOR, OnOpenFileWithEditor)
230 ON_COMMAND(ID_FILE_OPEN_WITH, OnOpenFileWith)
231 ON_COMMAND(ID_VIEW_SWAPPANES, OnViewSwapPanes)
232 ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewSwapPanes)
233 ON_UPDATE_COMMAND_UI(ID_NO_EDIT_SCRIPTS, OnUpdateNoEditScripts)
236 ON_COMMAND(ID_HELP, OnHelp)
237 ON_COMMAND(ID_VIEW_FILEMARGIN, OnViewMargin)
238 ON_UPDATE_COMMAND_UI(ID_VIEW_FILEMARGIN, OnUpdateViewMargin)
239 ON_UPDATE_COMMAND_UI(ID_VIEW_CHANGESCHEME, OnUpdateViewChangeScheme)
240 ON_COMMAND_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnChangeScheme)
241 ON_UPDATE_COMMAND_UI_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnUpdateChangeScheme)
243 ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
244 ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
245 ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
246 ON_COMMAND(ID_PLUGINS_LIST, OnPluginsList)
251 /////////////////////////////////////////////////////////////////////////////
252 // CMergeEditView diagnostics
255 CMergeDoc* CMergeEditView::GetDocument() // non-debug version is inline
257 ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMergeDoc)));
258 return (CMergeDoc*)m_pDocument;
262 /////////////////////////////////////////////////////////////////////////////
263 // CMergeEditView message handlers
266 * @brief Return text buffer for file in view
268 CCrystalTextBuffer *CMergeEditView::LocateTextBuffer()
270 return GetDocument()->m_ptBuf[m_nThisPane];
274 * @brief Update any resources necessary after a GUI language change
276 void CMergeEditView::UpdateResources()
280 void CMergeEditView::PrimeListWithFile()
282 // Set the tab size now, just in case the options change...
283 // We don't update it at the end of OnOptions,
284 // we can update it safely now
285 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
288 * @brief Return text from line given
290 CString CMergeEditView::GetLineText(int idx)
292 return GetLineChars(idx);
296 * @brief Return text from selection
298 CString CMergeEditView::GetSelectedText()
300 CPoint ptStart, ptEnd;
302 GetSelection(ptStart, ptEnd);
303 if (ptStart != ptEnd)
304 GetTextWithoutEmptys(ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, strText);
309 * @brief Get diffs inside selection.
310 * @param [out] firstDiff First diff inside selection
311 * @param [out] lastDiff Last diff inside selection
312 * @note -1 is returned in parameters if diffs cannot be determined
313 * @todo This shouldn't be called when there is no diffs, so replace
314 * first 'if' with ASSERT()?
316 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff)
321 CMergeDoc *pd = GetDocument();
322 const int nDiffs = pd->m_diffList.GetSignificantDiffs();
326 int firstLine, lastLine;
327 GetFullySelectedLines(firstLine, lastLine);
328 if (lastLine < firstLine)
331 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
332 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
333 if (firstDiff != -1 && lastDiff != -1)
337 // Check that first selected line is first diff's first line or above it
338 VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
339 if ((int)di.dbegin[0] < firstLine)
341 if (firstDiff < lastDiff)
345 // Check that last selected line is last diff's last line or below it
346 VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
347 if ((int)di.dend[0] > lastLine)
349 if (firstDiff < lastDiff)
353 // Special case: one-line diff is not selected if cursor is in it
354 if (firstLine == lastLine)
362 void CMergeEditView::OnInitialUpdate()
364 CCrystalEditViewEx::OnInitialUpdate();
365 SetFont(dynamic_cast<CMainFrame*>(AfxGetMainWnd())->m_lfDiff);
368 void CMergeEditView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
370 CCrystalEditViewEx::OnActivateView(bActivate, pActivateView, pDeactiveView);
372 CMergeDoc* pDoc = GetDocument();
373 pDoc->UpdateHeaderActivity(m_nThisPane, bActivate != FALSE);
376 int CMergeEditView::GetAdditionalTextBlocks (int nLineIndex, TEXTBLOCK *&pBuf)
380 DWORD dwLineFlags = GetLineFlags(nLineIndex);
381 if ((dwLineFlags & LF_SNP) == LF_SNP || (dwLineFlags & LF_DIFF) != LF_DIFF)
384 if (!GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT))
387 CMergeDoc *pDoc = GetDocument();
388 int unemptyLineCount = 0;
389 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
391 if ((pDoc->GetView(nPane)->GetLineCount() > nLineIndex) && !(pDoc->GetView(nPane)->GetLineFlags(nLineIndex) & LF_GHOST))
394 if (unemptyLineCount < 2)
397 vector<wdiff*> worddiffs;
398 pDoc->GetWordDiffArray(nLineIndex, &worddiffs);
400 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
401 int nWordDiffs = worddiffs.size();
403 pBuf = new TEXTBLOCK[nWordDiffs * 2 + 1];
404 pBuf[0].m_nCharPos = 0;
405 pBuf[0].m_nColorIndex = COLORINDEX_NONE;
406 pBuf[0].m_nBgColorIndex = COLORINDEX_NONE;
408 for (i = 0, j = 1; i < nWordDiffs; i++)
410 pBuf[j].m_nCharPos = worddiffs[i]->begin[m_nThisPane];
411 if (lineInCurrentDiff)
413 pBuf[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT1 | COLORINDEX_APPLYFORCE;
414 if (worddiffs[i]->begin[0] == worddiffs[i]->end[0] + 1 ||
415 worddiffs[i]->begin[1] == worddiffs[i]->end[1] + 1 ||
416 (pDoc->m_nBuffers == 3 && worddiffs[i]->begin[2] == worddiffs[i]->end[2] + 1))
417 pBuf[j].m_nBgColorIndex = COLORINDEX_HIGHLIGHTBKGND4 | COLORINDEX_APPLYFORCE;
419 pBuf[j].m_nBgColorIndex = COLORINDEX_HIGHLIGHTBKGND1 | COLORINDEX_APPLYFORCE;
423 pBuf[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT2 | COLORINDEX_APPLYFORCE;
424 if (worddiffs[i]->begin[0] == worddiffs[i]->end[0] + 1 ||
425 worddiffs[i]->begin[1] == worddiffs[i]->end[1] + 1 ||
426 (pDoc->m_nBuffers == 3 && worddiffs[i]->begin[2] == worddiffs[i]->end[2] + 1))
428 // Case on one side char/words are inserted or deleted
429 pBuf[j].m_nBgColorIndex = COLORINDEX_HIGHLIGHTBKGND3 | COLORINDEX_APPLYFORCE;
433 pBuf[j].m_nBgColorIndex = COLORINDEX_HIGHLIGHTBKGND2 | COLORINDEX_APPLYFORCE;
436 if (i + 1 == nWordDiffs || worddiffs[i]->end[m_nThisPane] + 1 < worddiffs[i + 1]->begin[m_nThisPane])
440 pBuf[j].m_nCharPos = worddiffs[i]->end[m_nThisPane] + 1;
442 LPCTSTR pLine = GetLineChars(nLineIndex);
443 BOOL bDBCS = (pLine && IsDBCSLeadByte(pLine[worddiffs[i]->end[m_nThisPane]]));
444 pBuf[j].m_nCharPos = worddiffs[i]->end[m_nThisPane] + (bDBCS ? 2 : 1);
446 pBuf[j].m_nColorIndex = COLORINDEX_NONE;
447 pBuf[j].m_nBgColorIndex = COLORINDEX_NONE;
452 while (!worddiffs.empty())
454 delete worddiffs.back();
455 worddiffs.pop_back();
461 COLORREF CMergeEditView::GetColor(int nColorIndex)
463 switch (nColorIndex & ~COLORINDEX_APPLYFORCE)
465 case COLORINDEX_HIGHLIGHTBKGND1:
466 return m_cachedColors.clrSelWordDiff;
467 case COLORINDEX_HIGHLIGHTTEXT1:
468 return m_cachedColors.clrSelWordDiffText;
469 case COLORINDEX_HIGHLIGHTBKGND2:
470 return m_cachedColors.clrWordDiff;
471 case COLORINDEX_HIGHLIGHTTEXT2:
472 return m_cachedColors.clrWordDiffText;
473 case COLORINDEX_HIGHLIGHTBKGND3:
474 return m_cachedColors.clrWordDiffDeleted;
475 case COLORINDEX_HIGHLIGHTBKGND4:
476 return m_cachedColors.clrSelWordDiffDeleted;
479 return CCrystalTextView::GetColor(nColorIndex);
484 * @brief Determine text and background color for line
485 * @param [in] nLineIndex Index of line in view (NOT line in file)
486 * @param [out] crBkgnd Backround color for line
487 * @param [out] crText Text color for line
489 void CMergeEditView::GetLineColors(int nLineIndex, COLORREF & crBkgnd,
490 COLORREF & crText, BOOL & bDrawWhitespace)
492 DWORD ignoreFlags = 0;
493 GetLineColors2(nLineIndex, ignoreFlags, crBkgnd, crText, bDrawWhitespace);
497 * @brief Determine text and background color for line
498 * @param [in] nLineIndex Index of line in view (NOT line in file)
499 * @param [in] ignoreFlags Flags that caller wishes ignored
500 * @param [out] crBkgnd Backround color for line
501 * @param [out] crText Text color for line
503 * This version allows caller to suppress particular flags
505 void CMergeEditView::GetLineColors2(int nLineIndex, DWORD ignoreFlags, COLORREF & crBkgnd,
506 COLORREF & crText, BOOL & bDrawWhitespace)
508 DWORD dwLineFlags = GetLineFlags(nLineIndex);
510 if (dwLineFlags & ignoreFlags)
511 dwLineFlags &= (~ignoreFlags);
514 if (dwLineFlags & LF_WINMERGE_FLAGS)
516 crText = m_cachedColors.clrDiffText;
517 bDrawWhitespace = true;
518 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
520 if (dwLineFlags & LF_SNP)
522 if (lineInCurrentDiff)
524 if (dwLineFlags & LF_GHOST)
525 crBkgnd = m_cachedColors.clrSelSNPDeleted;
527 crBkgnd = m_cachedColors.clrSelSNP;
528 crText = m_cachedColors.clrSelSNPText;
532 if (dwLineFlags & LF_GHOST)
533 crBkgnd = m_cachedColors.clrSNPDeleted;
535 crBkgnd = m_cachedColors.clrSNP;
536 crText = m_cachedColors.clrSNPText;
540 else if (dwLineFlags & LF_DIFF)
542 if (lineInCurrentDiff)
544 if (dwLineFlags & LF_MOVED)
546 if (dwLineFlags & LF_GHOST)
547 crBkgnd = m_cachedColors.clrSelMovedDeleted;
549 crBkgnd = m_cachedColors.clrSelMoved;
550 crText = m_cachedColors.clrSelMovedText;
554 crBkgnd = m_cachedColors.clrSelDiff;
555 crText = m_cachedColors.clrSelDiffText;
561 if (dwLineFlags & LF_MOVED)
563 if (dwLineFlags & LF_GHOST)
564 crBkgnd = m_cachedColors.clrMovedDeleted;
566 crBkgnd = m_cachedColors.clrMoved;
567 crText = m_cachedColors.clrMovedText;
571 crBkgnd = m_cachedColors.clrDiff;
572 crText = m_cachedColors.clrDiffText;
577 else if (dwLineFlags & LF_TRIVIAL)
579 // trivial diff can not be selected
580 if (dwLineFlags & LF_GHOST)
581 // ghost lines in trivial diff has their own color
582 crBkgnd = m_cachedColors.clrTrivialDeleted;
584 crBkgnd = m_cachedColors.clrTrivial;
585 crText = m_cachedColors.clrTrivialText;
588 else if (dwLineFlags & LF_GHOST)
590 if (lineInCurrentDiff)
591 crBkgnd = m_cachedColors.clrSelDiffDeleted;
593 crBkgnd = m_cachedColors.clrDiffDeleted;
599 // Line not inside diff,
600 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
602 // If no syntax hilighting, get windows default colors
603 crBkgnd = GetColor (COLORINDEX_BKGND);
604 crText = GetColor (COLORINDEX_NORMALTEXT);
605 bDrawWhitespace = false;
608 // Syntax highlighting, get colors from CrystalEditor
609 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
610 crText, bDrawWhitespace);
615 * @brief Sync other pane position
617 void CMergeEditView::UpdateSiblingScrollPos (BOOL bHorz)
619 CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
620 if (pSplitterWnd != NULL)
622 // See CSplitterWnd::IdFromRowCol() implementation for details
623 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
624 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
625 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
626 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
628 // limit the TopLine : must be smaller than GetLineCount for all the panels
629 int newTopSubLine = m_nTopSubLine;
630 int nRows = pSplitterWnd->GetRowCount ();
631 int nCols = pSplitterWnd->GetColumnCount ();
633 // for (nRow = 0; nRow < nRows; nRow++)
635 // for (int nCol = 0; nCol < nCols; nCol++)
637 // CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
638 // if (pSiblingView != NULL)
639 // if (pSiblingView->GetSubLineCount() <= newTopSubLine)
640 // newTopSubLine = pSiblingView->GetSubLineCount()-1;
643 if (m_nTopSubLine != newTopSubLine)
644 ScrollToSubLine(newTopSubLine);
646 for (nRow = 0; nRow < nRows; nRow++)
648 for (int nCol = 0; nCol < nCols; nCol++)
650 if (!(nRow == nCurrentRow && nCol == nCurrentCol)) // We don't need to update ourselves
652 CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
653 if (pSiblingView != NULL)
654 pSiblingView->OnUpdateSibling (this, bHorz);
662 * @brief Update other panes
664 void CMergeEditView::OnUpdateSibling (CCrystalTextView * pUpdateSource, BOOL bHorz)
666 if (pUpdateSource != this)
668 ASSERT (pUpdateSource != NULL);
669 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
670 CMergeEditView *pSrcView = static_cast<CMergeEditView*>(pUpdateSource);
671 if (!bHorz) // changed this so bHorz works right
673 ASSERT (pSrcView->m_nTopSubLine >= 0);
675 // This ASSERT is wrong: panes have different files and
676 // different linecounts
677 // ASSERT (pSrcView->m_nTopLine < GetLineCount ());
678 if (pSrcView->m_nTopSubLine != m_nTopSubLine)
680 ScrollToSubLine (pSrcView->m_nTopSubLine, true, false);
682 RecalcVertScrollBar(true);
687 ASSERT (pSrcView->m_nOffsetChar >= 0);
689 // This ASSERT is wrong: panes have different files and
690 // different linelengths
691 // ASSERT (pSrcView->m_nOffsetChar < GetMaxLineLength ());
692 if (pSrcView->m_nOffsetChar != m_nOffsetChar)
694 ScrollToChar (pSrcView->m_nOffsetChar, true, false);
696 RecalcHorzScrollBar(true);
703 * @brief Selects diff by number and syncs other file
704 * @param [in] nDiff Diff to select, must be >= 0
705 * @param [in] bScroll Scroll diff to view
706 * @param [in] bSelectText Select diff text
707 * @sa CMergeEditView::ShowDiff()
708 * @sa CMergeDoc::SetCurrentDiff()
709 * @todo Parameter bSelectText is never used?
711 void CMergeEditView::SelectDiff(int nDiff, bool bScroll /*=true*/, bool bSelectText /*=true*/)
713 CMergeDoc *pd = GetDocument();
715 // Check that nDiff is valid
717 _RPTF1(_CRT_ERROR, "Diffnumber negative (%d)", nDiff);
718 if (nDiff >= pd->m_diffList.GetSize())
719 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d >= %d)",
720 nDiff, pd->m_diffList.GetSize());
723 pd->SetCurrentDiff(nDiff);
724 ShowDiff(bScroll, bSelectText);
725 pd->UpdateAllViews(this);
726 UpdateSiblingScrollPos(false);
728 // notify either side, as it will notify the other one
729 pd->GetDetailView(0)->OnDisplayDiff(nDiff);
733 * @brief Called when user selects "Current Difference".
734 * Goes to active diff. If no active diff, selects diff under cursor
735 * @sa CMergeEditView::SelectDiff()
736 * @sa CMergeDoc::GetCurrentDiff()
737 * @sa CMergeDoc::LineToDiff()
739 void CMergeEditView::OnCurdiff()
741 CMergeDoc *pd = GetDocument();
743 // If no diffs, nothing to select
744 if (!pd->m_diffList.HasSignificantDiffs())
747 // GetCurrentDiff() returns -1 if no diff selected
748 int nDiff = pd->GetCurrentDiff();
751 // Scroll to the first line of the currently selected diff
752 SelectDiff(nDiff, true, false);
756 // If cursor is inside diff, select that diff
757 CPoint pos = GetCursorPos();
758 nDiff = pd->m_diffList.LineToDiff(pos.y);
759 if (nDiff != -1 && pd->m_diffList.IsDiffSignificant(nDiff))
760 SelectDiff(nDiff, true, false);
765 * @brief Called when "Current diff" item is updated
767 void CMergeEditView::OnUpdateCurdiff(CCmdUI* pCmdUI)
769 CMergeDoc *pd = GetDocument();
770 CPoint pos = GetCursorPos();
771 int nCurrentDiff = pd->GetCurrentDiff();
772 if (nCurrentDiff == -1)
774 int nNewDiff = pd->m_diffList.LineToDiff(pos.y);
775 if (nNewDiff != -1 && pd->m_diffList.IsDiffSignificant(nNewDiff))
776 pCmdUI->Enable(true);
778 pCmdUI->Enable(false);
781 pCmdUI->Enable(true);
785 * @brief Copy selected text to clipboard
787 void CMergeEditView::OnEditCopy()
789 CMergeDoc * pDoc = GetDocument();
790 CPoint ptSelStart, ptSelEnd;
791 GetSelection(ptSelStart, ptSelEnd);
794 if (ptSelStart == ptSelEnd)
799 if (!m_bColumnSelection)
801 CDiffTextBuffer * buffer = pDoc->m_ptBuf[m_nThisPane];
803 buffer->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
804 ptSelEnd.y, ptSelEnd.x, text);
807 GetTextWithoutEmptysInColumnSelection(text);
809 PutToClipboard(text, text.GetLength(), m_bColumnSelection);
813 * @brief Called when "Copy" item is updated
815 void CMergeEditView::OnUpdateEditCopy(CCmdUI* pCmdUI)
817 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
821 * @brief Cut current selection to clipboard
823 void CMergeEditView::OnEditCut()
825 if (IsReadOnly(m_nThisPane))
828 CPoint ptSelStart, ptSelEnd;
829 CMergeDoc * pDoc = GetDocument();
830 GetSelection(ptSelStart, ptSelEnd);
833 if (ptSelStart == ptSelEnd)
837 if (!m_bColumnSelection)
838 pDoc->m_ptBuf[m_nThisPane]->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
839 ptSelEnd.y, ptSelEnd.x, text);
841 GetTextWithoutEmptysInColumnSelection(text);
843 PutToClipboard(text, text.GetLength(), m_bColumnSelection);
845 if (!m_bColumnSelection)
847 CPoint ptCursorPos = ptSelStart;
848 ASSERT_VALIDTEXTPOS(ptCursorPos);
849 SetAnchor(ptCursorPos);
850 SetSelection(ptCursorPos, ptCursorPos);
851 SetCursorPos(ptCursorPos);
852 EnsureVisible(ptCursorPos);
854 pDoc->m_ptBuf[m_nThisPane]->DeleteText(this, ptSelStart.y, ptSelStart.x, ptSelEnd.y,
855 ptSelEnd.x, CE_ACTION_CUT);
858 DeleteCurrentColumnSelection (CE_ACTION_CUT);
860 m_pTextBuffer->SetModified(true);
864 * @brief Called when "Cut" item is updated
866 void CMergeEditView::OnUpdateEditCut(CCmdUI* pCmdUI)
868 if (!IsReadOnly(m_nThisPane))
869 CCrystalEditViewEx::OnUpdateEditCut(pCmdUI);
871 pCmdUI->Enable(false);
875 * @brief Paste text from clipboard
877 void CMergeEditView::OnEditPaste()
879 if (IsReadOnly(m_nThisPane))
882 CCrystalEditViewEx::Paste();
883 m_pTextBuffer->SetModified(true);
887 * @brief Called when "Paste" item is updated
889 void CMergeEditView::OnUpdateEditPaste(CCmdUI* pCmdUI)
891 if (!IsReadOnly(m_nThisPane))
892 CCrystalEditViewEx::OnUpdateEditPaste(pCmdUI);
894 pCmdUI->Enable(false);
898 * @brief Undo last action
900 void CMergeEditView::OnEditUndo()
902 WaitStatusCursor waitstatus(IDS_STATUS_UNDO);
903 CMergeDoc* pDoc = GetDocument();
904 CMergeEditView *tgt = *(pDoc->curUndo-1);
907 if (IsReadOnly(m_nThisPane))
910 GetParentFrame()->SetActiveView(this, true);
911 if(CCrystalEditViewEx::DoEditUndo())
914 pDoc->UpdateHeaderPath(m_nThisPane);
915 pDoc->FlushAndRescan();
918 m_pTextBuffer->GetRedoActionCode(nAction);
919 if (nAction == CE_ACTION_MERGE)
920 // select the diff so we may just merge it again
926 tgt->SendMessage(WM_COMMAND, ID_EDIT_UNDO);
931 * @brief Called when "Undo" item is updated
933 void CMergeEditView::OnUpdateEditUndo(CCmdUI* pCmdUI)
935 CMergeDoc* pDoc = GetDocument();
936 if (pDoc->curUndo!=pDoc->undoTgt.begin())
938 CMergeEditView *tgt = *(pDoc->curUndo-1);
939 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
942 pCmdUI->Enable(false);
946 * @brief Go to first diff
948 * Called when user selects "First Difference"
949 * @sa CMergeEditView::SelectDiff()
951 void CMergeEditView::OnFirstdiff()
953 CMergeDoc *pd = GetDocument();
954 if (pd->m_diffList.HasSignificantDiffs())
956 int nDiff = pd->m_diffList.FirstSignificantDiff();
957 SelectDiff(nDiff, true, false);
962 * @brief Update "First diff" UI items
964 void CMergeEditView::OnUpdateFirstdiff(CCmdUI* pCmdUI)
966 OnUpdatePrevdiff(pCmdUI);
970 * @brief Go to last diff
972 void CMergeEditView::OnLastdiff()
974 CMergeDoc *pd = GetDocument();
975 if (pd->m_diffList.HasSignificantDiffs())
977 int nDiff = pd->m_diffList.LastSignificantDiff();
978 SelectDiff(nDiff, true, false);
983 * @brief Update "Last diff" UI items
985 void CMergeEditView::OnUpdateLastdiff(CCmdUI* pCmdUI)
987 OnUpdateNextdiff(pCmdUI);
991 * @brief Go to next diff and select it.
993 * Finds and selects next difference. There are several cases:
994 * - if there is selected difference, and that difference is visible
995 * on screen, next found difference is selected.
996 * - if there is selected difference but it is not visible, next
997 * difference from cursor position is selected. This is what user
998 * expects to happen and is natural thing to do. Also reduces
999 * needless scrolling.
1000 * - if there is no selected difference, next difference from cursor
1001 * position is selected.
1003 void CMergeEditView::OnNextdiff()
1005 CMergeDoc *pd = GetDocument();
1006 int cnt = pd->m_ptBuf[0]->GetLineCount();
1010 // Returns -1 if no diff selected
1011 int curDiff = pd->GetCurrentDiff();
1015 int nextDiff = curDiff;
1016 if (!IsDiffVisible(curDiff))
1018 // Selected difference not visible, select next from cursor
1019 int line = GetCursorPos().y;
1020 // Make sure we aren't in the first line of the diff
1022 if (!IsValidTextPosY(CPoint(0, line)))
1024 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1028 // Find out if there is a following significant diff
1029 if (curDiff < pd->m_diffList.GetSize() - 1)
1031 nextDiff = pd->m_diffList.NextSignificantDiff(curDiff);
1037 // nextDiff is the next one if there is one, else it is the one we're on
1038 SelectDiff(nextDiff, true, false);
1042 // We don't have a selected difference,
1043 // but cursor can be inside inactive diff
1044 int line = GetCursorPos().y;
1045 if (!IsValidTextPosY(CPoint(0, line)))
1047 curDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1049 SelectDiff(curDiff, true, false);
1054 * @brief Update "Next diff" UI items
1056 void CMergeEditView::OnUpdateNextdiff(CCmdUI* pCmdUI)
1058 CMergeDoc *pd = GetDocument();
1059 const DIFFRANGE * dfi = pd->m_diffList.LastSignificantDiffRange();
1063 // There aren't any significant differences
1064 pCmdUI->Enable(false);
1068 // Enable if the beginning of the last significant difference is after caret
1069 CPoint pos = GetCursorPos();
1070 pCmdUI->Enable(pos.y < (long)dfi->dbegin[0]);
1075 * @brief Go to previous diff and select it.
1077 * Finds and selects previous difference. There are several cases:
1078 * - if there is selected difference, and that difference is visible
1079 * on screen, previous found difference is selected.
1080 * - if there is selected difference but it is not visible, previous
1081 * difference from cursor position is selected. This is what user
1082 * expects to happen and is natural thing to do. Also reduces
1083 * needless scrolling.
1084 * - if there is no selected difference, previous difference from cursor
1085 * position is selected.
1087 void CMergeEditView::OnPrevdiff()
1089 CMergeDoc *pd = GetDocument();
1090 int cnt = pd->m_ptBuf[0]->GetLineCount();
1094 // GetCurrentDiff() returns -1 if no diff selected
1095 int curDiff = pd->GetCurrentDiff();
1099 int prevDiff = curDiff;
1100 if (!IsDiffVisible(curDiff))
1102 // Selected difference not visible, select previous from cursor
1103 int line = GetCursorPos().y;
1104 // Make sure we aren't in the last line of the diff
1106 if (!IsValidTextPosY(CPoint(0, line)))
1108 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1112 // Find out if there is a preceding significant diff
1115 prevDiff = pd->m_diffList.PrevSignificantDiff(curDiff);
1121 // prevDiff is the preceding one if there is one, else it is the one we're on
1122 SelectDiff(prevDiff, true, false);
1126 // We don't have a selected difference,
1127 // but cursor can be inside inactive diff
1128 int line = GetCursorPos().y;
1129 if (!IsValidTextPosY(CPoint(0, line)))
1131 curDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1133 SelectDiff(curDiff, true, false);
1138 * @brief Update "Previous diff" UI items
1140 void CMergeEditView::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1142 CMergeDoc *pd = GetDocument();
1143 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificantDiffRange();
1147 // There aren't any significant differences
1148 pCmdUI->Enable(false);
1152 // Enable if the end of the first significant difference is before caret
1153 CPoint pos = GetCursorPos();
1154 pCmdUI->Enable(pos.y > (long)dfi->dend[0]);
1159 * @brief Go to next 3-way diff and select it.
1161 void CMergeEditView::OnNext3wayDiff(int nDiffType)
1163 CMergeDoc *pd = GetDocument();
1164 int cnt = pd->m_ptBuf[0]->GetLineCount();
1168 // Returns -1 if no diff selected
1169 int curDiff = pd->GetCurrentDiff();
1173 int nextDiff = curDiff;
1174 if (!IsDiffVisible(curDiff))
1176 // Selected difference not visible, select next from cursor
1177 int line = GetCursorPos().y;
1178 // Make sure we aren't in the first line of the diff
1180 if (!IsValidTextPosY(CPoint(0, line)))
1182 nextDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1186 // Find out if there is a following significant diff
1187 if (curDiff < pd->m_diffList.GetSize() - 1)
1189 nextDiff = pd->m_diffList.NextSignificant3wayDiff(curDiff, nDiffType);
1195 // nextDiff is the next one if there is one, else it is the one we're on
1196 SelectDiff(nextDiff, TRUE, FALSE);
1200 // We don't have a selected difference,
1201 // but cursor can be inside inactive diff
1202 int line = GetCursorPos().y;
1203 if (!IsValidTextPosY(CPoint(0, line)))
1205 curDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1207 SelectDiff(curDiff, TRUE, FALSE);
1212 * @brief Update "Next 3-way diff" UI items
1214 void CMergeEditView::OnUpdateNext3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1216 CMergeDoc *pd = GetDocument();
1218 if (pd->m_nBuffers < 3)
1220 pCmdUI->Enable(FALSE);
1224 const DIFFRANGE * dfi = pd->m_diffList.LastSignificant3wayDiffRange(nDiffType);
1228 // There aren't any significant differences
1229 pCmdUI->Enable(FALSE);
1233 // Enable if the beginning of the last significant difference is after caret
1234 CPoint pos = GetCursorPos();
1235 pCmdUI->Enable(pos.y < (long)dfi->dbegin[0]);
1240 * @brief Go to previous 3-way diff and select it.
1242 void CMergeEditView::OnPrev3wayDiff(int nDiffType)
1244 CMergeDoc *pd = GetDocument();
1246 int cnt = pd->m_ptBuf[0]->GetLineCount();
1250 // GetCurrentDiff() returns -1 if no diff selected
1251 int curDiff = pd->GetCurrentDiff();
1255 int prevDiff = curDiff;
1256 if (!IsDiffVisible(curDiff))
1258 // Selected difference not visible, select previous from cursor
1259 int line = GetCursorPos().y;
1260 // Make sure we aren't in the last line of the diff
1262 if (!IsValidTextPosY(CPoint(0, line)))
1264 prevDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1268 // Find out if there is a preceding significant diff
1271 prevDiff = pd->m_diffList.PrevSignificant3wayDiff(curDiff, nDiffType);
1277 // prevDiff is the preceding one if there is one, else it is the one we're on
1278 SelectDiff(prevDiff, TRUE, FALSE);
1282 // We don't have a selected difference,
1283 // but cursor can be inside inactive diff
1284 int line = GetCursorPos().y;
1285 if (!IsValidTextPosY(CPoint(0, line)))
1287 curDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1289 SelectDiff(curDiff, TRUE, FALSE);
1294 * @brief Update "Previous diff X and Y" UI items
1296 void CMergeEditView::OnUpdatePrev3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1298 CMergeDoc *pd = GetDocument();
1300 if (pd->m_nBuffers < 3)
1302 pCmdUI->Enable(FALSE);
1306 const DIFFRANGE * dfi = pd->m_diffList.FirstSignificant3wayDiffRange(nDiffType);
1310 // There aren't any significant differences
1311 pCmdUI->Enable(FALSE);
1315 // Enable if the end of the first significant difference is before caret
1316 CPoint pos = GetCursorPos();
1317 pCmdUI->Enable(pos.y > (long)dfi->dend[0]);
1321 void CMergeEditView::OnNextdiffLM()
1323 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1326 void CMergeEditView::OnUpdateNextdiffLM(CCmdUI* pCmdUI)
1328 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1331 void CMergeEditView::OnNextdiffLR()
1333 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1336 void CMergeEditView::OnUpdateNextdiffLR(CCmdUI* pCmdUI)
1338 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1341 void CMergeEditView::OnNextdiffMR()
1343 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1346 void CMergeEditView::OnUpdateNextdiffMR(CCmdUI* pCmdUI)
1348 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1351 void CMergeEditView::OnNextdiffLO()
1353 OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1356 void CMergeEditView::OnUpdateNextdiffLO(CCmdUI* pCmdUI)
1358 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1361 void CMergeEditView::OnNextdiffMO()
1363 OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1366 void CMergeEditView::OnUpdateNextdiffMO(CCmdUI* pCmdUI)
1368 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1371 void CMergeEditView::OnNextdiffRO()
1373 OnNext3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1376 void CMergeEditView::OnUpdateNextdiffRO(CCmdUI* pCmdUI)
1378 OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1381 void CMergeEditView::OnPrevdiffLM()
1383 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1386 void CMergeEditView::OnUpdatePrevdiffLM(CCmdUI* pCmdUI)
1388 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1391 void CMergeEditView::OnPrevdiffLR()
1393 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1396 void CMergeEditView::OnUpdatePrevdiffLR(CCmdUI* pCmdUI)
1398 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1401 void CMergeEditView::OnPrevdiffMR()
1403 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1406 void CMergeEditView::OnUpdatePrevdiffMR(CCmdUI* pCmdUI)
1408 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1411 void CMergeEditView::OnPrevdiffLO()
1413 OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1416 void CMergeEditView::OnUpdatePrevdiffLO(CCmdUI* pCmdUI)
1418 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1421 void CMergeEditView::OnPrevdiffMO()
1423 OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1426 void CMergeEditView::OnUpdatePrevdiffMO(CCmdUI* pCmdUI)
1428 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1431 void CMergeEditView::OnPrevdiffRO()
1433 OnPrev3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1436 void CMergeEditView::OnUpdatePrevdiffRO(CCmdUI* pCmdUI)
1438 OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1442 * @brief Clear selection
1444 void CMergeEditView::SelectNone()
1446 SetSelection (GetCursorPos(), GetCursorPos());
1451 * @brief Check if line is inside currently selected diff
1452 * @param [in] nLine 0-based linenumber in view
1453 * @sa CMergeDoc::GetCurrentDiff()
1454 * @sa CMergeDoc::LineInDiff()
1456 bool CMergeEditView::IsLineInCurrentDiff(int nLine)
1458 // Check validity of nLine
1461 _RPTF1(_CRT_ERROR, "Linenumber is negative (%d)!", nLine);
1462 int nLineCount = LocateTextBuffer()->GetLineCount();
1463 if (nLine >= nLineCount)
1464 _RPTF2(_CRT_ERROR, "Linenumber > linecount (%d>%d)!", nLine, nLineCount);
1467 CMergeDoc *pd = GetDocument();
1468 int curDiff = pd->GetCurrentDiff();
1471 return pd->m_diffList.LineInDiff(nLine, curDiff);
1475 * @brief Called when mouse left-button double-clicked
1477 * Double-clicking mouse inside diff selects that diff
1479 void CMergeEditView::OnLButtonDblClk(UINT nFlags, CPoint point)
1481 CMergeDoc *pd = GetDocument();
1482 CPoint pos = GetCursorPos();
1484 int diff = pd->m_diffList.LineToDiff(pos.y);
1485 if (diff != -1 && pd->m_diffList.IsDiffSignificant(diff))
1486 SelectDiff(diff, false, false);
1488 CCrystalEditViewEx::OnLButtonDblClk(nFlags, point);
1492 * @brief Called when mouse left button is released.
1494 * If button is released outside diffs, current diff
1497 void CMergeEditView::OnLButtonUp(UINT nFlags, CPoint point)
1499 CMergeDoc *pd = GetDocument();
1500 CCrystalEditViewEx::OnLButtonUp(nFlags, point);
1502 // If we have a selected diff, deselect it
1503 int nCurrentDiff = pd->GetCurrentDiff();
1504 if (nCurrentDiff != -1)
1506 CPoint pos = GetCursorPos();
1507 if (!IsLineInCurrentDiff(pos.y))
1509 pd->SetCurrentDiff(-1);
1511 pd->UpdateAllViews(this);
1517 * @brief Finds longest line (needed for scrolling etc).
1518 * @sa CCrystalTextView::GetMaxLineLength()
1520 void CMergeEditView::UpdateLineLengths()
1526 * @brief Copy diff from left pane to right pane
1528 * Difference is copied from left to right when
1529 * - difference is selected
1530 * - difference is inside selection (allows merging multiple differences).
1531 * - cursor is inside diff
1533 * If there is selected diff outside selection, we copy selected
1536 void CMergeEditView::OnL2r()
1538 int dstPane = (m_nThisPane < GetDocument()->m_nBuffers - 1) ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1539 int srcPane = dstPane - 1;
1541 // Check that right side is not readonly
1542 if (IsReadOnly(dstPane))
1545 CMergeDoc *pDoc = GetDocument();
1546 int currentDiff = pDoc->GetCurrentDiff();
1548 if (currentDiff == -1)
1551 // If cursor is inside diff get number of that diff
1552 if (m_bCurrentLineIsDiff)
1554 CPoint pt = GetCursorPos();
1555 currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1559 int firstDiff, lastDiff;
1560 GetFullySelectedDiffs(firstDiff, lastDiff);
1562 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1564 WaitStatusCursor waitstatus(IDS_STATUS_COPYL2R);
1565 if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff) && !IsSelection())
1566 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1568 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff);
1570 else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1572 WaitStatusCursor waitstatus(IDS_STATUS_COPYL2R);
1573 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1578 * @brief Called when "Copy to left" item is updated
1580 void CMergeEditView::OnUpdateL2r(CCmdUI* pCmdUI)
1582 // Check that right side is not readonly
1583 if (!IsReadOnly(m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1))
1585 int firstDiff, lastDiff;
1586 GetFullySelectedDiffs(firstDiff, lastDiff);
1588 // If one or more diffs inside selection OR
1589 // there is an active diff OR
1590 // cursor is inside diff
1591 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1592 pCmdUI->Enable(true);
1595 const int currDiff = GetDocument()->GetCurrentDiff();
1596 if (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff))
1597 pCmdUI->Enable(true);
1599 pCmdUI->Enable(m_bCurrentLineIsDiff);
1603 pCmdUI->Enable(false);
1607 * @brief Copy diff from right pane to left pane
1609 * Difference is copied from left to right when
1610 * - difference is selected
1611 * - difference is inside selection (allows merging multiple differences).
1612 * - cursor is inside diff
1614 * If there is selected diff outside selection, we copy selected
1617 void CMergeEditView::OnR2l()
1619 int dstPane = (m_nThisPane > 0) ? m_nThisPane - 1 : 0;
1620 int srcPane = dstPane + 1;
1622 // Check that left side is not readonly
1623 if (IsReadOnly(dstPane))
1626 CMergeDoc *pDoc = GetDocument();
1627 int currentDiff = pDoc->GetCurrentDiff();
1628 if (currentDiff == -1)
1631 // If cursor is inside diff get number of that diff
1632 if (m_bCurrentLineIsDiff)
1635 pt = GetCursorPos();
1636 currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1640 int firstDiff, lastDiff;
1641 GetFullySelectedDiffs(firstDiff, lastDiff);
1643 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1645 WaitStatusCursor waitstatus(IDS_STATUS_COPYR2L);
1646 if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff) && !IsSelection())
1647 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1649 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff);
1651 else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1653 WaitStatusCursor waitstatus(IDS_STATUS_COPYR2L);
1654 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1659 * @brief Called when "Copy to right" item is updated
1661 void CMergeEditView::OnUpdateR2l(CCmdUI* pCmdUI)
1663 // Check that left side is not readonly
1664 if (!IsReadOnly(m_nThisPane > 0 ? m_nThisPane - 1 : 0))
1666 int firstDiff, lastDiff;
1667 GetFullySelectedDiffs(firstDiff, lastDiff);
1669 // If one or more diffs inside selection OR
1670 // there is an active diff OR
1671 // cursor is inside diff
1672 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1673 pCmdUI->Enable(true);
1676 const int currDiff = GetDocument()->GetCurrentDiff();
1677 if (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff))
1678 pCmdUI->Enable(true);
1680 pCmdUI->Enable(m_bCurrentLineIsDiff);
1684 pCmdUI->Enable(false);
1688 * @brief Copy diff from left pane to middle pane
1690 * Difference is copied from left to middle when
1691 * - difference is selected
1692 * - difference is inside selection (allows merging multiple differences).
1693 * - cursor is inside diff
1695 * If there is selected diff outside selection, we copy selected
1698 void CMergeEditView::OnL2m()
1703 CMergeDoc *pDoc = GetDocument();
1705 // Check that middle side is not readonly
1706 if (pDoc->m_nBuffers < 3 || IsReadOnly(dstPane))
1709 int currentDiff = pDoc->GetCurrentDiff();
1711 if (currentDiff == -1)
1714 // If cursor is inside diff get number of that diff
1715 if (m_bCurrentLineIsDiff)
1717 CPoint pt = GetCursorPos();
1718 currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1722 int firstDiff, lastDiff;
1723 GetFullySelectedDiffs(firstDiff, lastDiff);
1725 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1727 WaitStatusCursor waitstatus(IDS_STATUS_COPYL2M);
1728 if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff) && !IsSelection())
1729 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1731 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff);
1733 else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1735 WaitStatusCursor waitstatus(IDS_STATUS_COPYL2M);
1736 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1741 * @brief Called when "Copy to middle" item is updated
1743 void CMergeEditView::OnUpdateL2m(CCmdUI* pCmdUI)
1745 CMergeDoc *pDoc = GetDocument();
1747 // Check that middle side is not readonly
1748 if (pDoc->m_nBuffers == 3 && !IsReadOnly(1))
1750 int firstDiff, lastDiff;
1751 GetFullySelectedDiffs(firstDiff, lastDiff);
1753 // If one or more diffs inside selection OR
1754 // there is an active diff OR
1755 // cursor is inside diff
1756 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1757 pCmdUI->Enable(true);
1760 const int currDiff = GetDocument()->GetCurrentDiff();
1761 if (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff))
1762 pCmdUI->Enable(true);
1764 pCmdUI->Enable(m_bCurrentLineIsDiff);
1768 pCmdUI->Enable(false);
1772 * @brief Copy diff from right pane to middle pane
1774 * Difference is copied from right to middle when
1775 * - difference is selected
1776 * - difference is inside selection (allows merging multiple differences).
1777 * - cursor is inside diff
1779 * If there is selected diff outside selection, we copy selected
1782 void CMergeEditView::OnR2m()
1787 CMergeDoc *pDoc = GetDocument();
1789 // Check that left side is not readonly
1790 if (pDoc->m_nBuffers < 3 || IsReadOnly(dstPane))
1793 int currentDiff = pDoc->GetCurrentDiff();
1794 if (currentDiff == -1)
1797 // If cursor is inside diff get number of that diff
1798 if (m_bCurrentLineIsDiff)
1801 pt = GetCursorPos();
1802 currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1806 int firstDiff, lastDiff;
1807 GetFullySelectedDiffs(firstDiff, lastDiff);
1809 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1811 WaitStatusCursor waitstatus(IDS_STATUS_COPYR2M);
1812 if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff) && !IsSelection())
1813 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1815 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff);
1817 else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1819 WaitStatusCursor waitstatus(IDS_STATUS_COPYR2M);
1820 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1825 * @brief Called when "Copy to middle" item is updated
1827 void CMergeEditView::OnUpdateR2m(CCmdUI* pCmdUI)
1829 CMergeDoc *pDoc = GetDocument();
1831 // Check that middle side is not readonly
1832 if (pDoc->m_nBuffers == 3 && !IsReadOnly(1))
1834 int firstDiff, lastDiff;
1835 GetFullySelectedDiffs(firstDiff, lastDiff);
1837 // If one or more diffs inside selection OR
1838 // there is an active diff OR
1839 // cursor is inside diff
1840 if (firstDiff != -1 && lastDiff != -1 && (lastDiff >= firstDiff))
1841 pCmdUI->Enable(true);
1844 const int currDiff = GetDocument()->GetCurrentDiff();
1845 if (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff))
1846 pCmdUI->Enable(true);
1848 pCmdUI->Enable(m_bCurrentLineIsDiff);
1852 pCmdUI->Enable(false);
1856 * @brief Copy all diffs from right pane to left pane
1858 void CMergeEditView::OnAllLeft()
1860 // Check that left side is not readonly
1861 int srcPane = m_nThisPane > 0 ? m_nThisPane : 1;
1862 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
1863 if (IsReadOnly(dstPane))
1865 WaitStatusCursor waitstatus(IDS_STATUS_COPYALL2L);
1867 GetDocument()->CopyAllList(srcPane, dstPane);
1871 * @brief Called when "Copy all to left" item is updated
1873 void CMergeEditView::OnUpdateAllLeft(CCmdUI* pCmdUI)
1875 // Check that left side is not readonly
1876 int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
1877 if (!IsReadOnly(dstPane))
1878 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
1880 pCmdUI->Enable(false);
1884 * @brief Copy all diffs from left pane to right pane
1886 void CMergeEditView::OnAllRight()
1888 // Check that right side is not readonly
1889 int srcPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane : m_nThisPane - 1;
1890 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1891 if (IsReadOnly(dstPane))
1894 WaitStatusCursor waitstatus(IDS_STATUS_COPYALL2R);
1896 GetDocument()->CopyAllList(srcPane, dstPane);
1900 * @brief Called when "Copy all to right" item is updated
1902 void CMergeEditView::OnUpdateAllRight(CCmdUI* pCmdUI)
1904 // Check that right side is not readonly
1905 int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1906 if (!IsReadOnly(dstPane))
1907 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
1909 pCmdUI->Enable(false);
1913 * @brief This function is called before other edit events.
1914 * @param [in] nAction Edit operation to do
1915 * @param [in] pszText Text to insert, delete etc
1916 * @sa CCrystalEditView::OnEditOperation()
1917 * @todo More edit-events for rescan delaying?
1919 void CMergeEditView::OnEditOperation(int nAction, LPCTSTR pszText, int cchText)
1921 if (IsReadOnly(m_nThisPane))
1923 // We must not arrive here, and assert helps detect troubles
1928 CMergeDoc* pDoc = GetDocument();
1929 pDoc->SetEditedAfterRescan(m_nThisPane);
1931 // simple hook for multiplex undo operations
1932 // deleted by jtuc 2003-06-28
1933 // now AddUndoRecords does it (so we don't create entry for OnEditOperation with no Undo data in m_pTextBuffer)
1934 /*if(dynamic_cast<CMergeDoc::CDiffTextBuffer*>(m_pTextBuffer)->curUndoGroup())
1936 pDoc->undoTgt.erase(pDoc->curUndo, pDoc->undoTgt.end());
1937 pDoc->undoTgt.push_back(this);
1938 pDoc->curUndo = pDoc->undoTgt.end();
1941 // perform original function
1942 CCrystalEditViewEx::OnEditOperation(nAction, pszText, cchText);
1944 // augment with additional operations
1946 // Change header to inform about changed doc
1947 pDoc->UpdateHeaderPath(m_nThisPane);
1949 // If automatic rescan enabled, rescan after edit events
1950 if (m_bAutomaticRescan)
1952 // keep document up to date
1953 // (Re)start timer to rescan only when user edits text
1954 // If timer starting fails, rescan immediately
1955 if (nAction == CE_ACTION_TYPING ||
1956 nAction == CE_ACTION_REPLACE ||
1957 nAction == CE_ACTION_BACKSPACE ||
1958 nAction == CE_ACTION_INDENT ||
1959 nAction == CE_ACTION_PASTE ||
1960 nAction == CE_ACTION_DELSEL ||
1961 nAction == CE_ACTION_DELETE ||
1962 nAction == CE_ACTION_CUT)
1964 if (!SetTimer(IDT_RESCAN, RESCAN_TIMEOUT, NULL))
1965 pDoc->FlushAndRescan();
1968 pDoc->FlushAndRescan();
1974 // Update other pane for sync line.
1975 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
1977 if (nPane == m_nThisPane)
1979 CCrystalEditView *pView = pDoc->GetView(nPane);
1981 pView->Invalidate();
1988 * @brief Redo last action
1990 void CMergeEditView::OnEditRedo()
1992 WaitStatusCursor waitstatus(IDS_STATUS_REDO);
1993 CMergeDoc* pDoc = GetDocument();
1994 CMergeEditView *tgt = *(pDoc->curUndo);
1997 if (IsReadOnly(m_nThisPane))
2000 GetParentFrame()->SetActiveView(this, true);
2001 if(CCrystalEditViewEx::DoEditRedo())
2004 pDoc->UpdateHeaderPath(m_nThisPane);
2005 pDoc->FlushAndRescan();
2010 tgt->SendMessage(WM_COMMAND, ID_EDIT_REDO);
2015 * @brief Called when "Redo" item is updated
2017 void CMergeEditView::OnUpdateEditRedo(CCmdUI* pCmdUI)
2019 CMergeDoc* pDoc = GetDocument();
2020 if (pDoc->curUndo!=pDoc->undoTgt.end())
2022 CMergeEditView *tgt = *(pDoc->curUndo);
2023 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
2026 pCmdUI->Enable(false);
2029 void CMergeEditView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
2031 CCrystalEditViewEx::OnUpdate(pSender, lHint, pHint);
2035 * @brief Scrolls to current diff and/or selects diff text
2036 * @param [in] bScroll If TRUE scroll diff to view
2037 * @param [in] bSelectText If TRUE select diff text
2038 * @note If bScroll and bSelectText are FALSE, this does nothing!
2039 * @todo This shouldn't be called when no diff is selected, so
2040 * somebody could try to ASSERT(nDiff > -1)...
2042 void CMergeEditView::ShowDiff(bool bScroll, bool bSelectText)
2044 CMergeDoc *pd = GetDocument();
2045 const int nDiff = pd->GetCurrentDiff();
2047 // Try to trap some errors
2048 if (nDiff >= pd->m_diffList.GetSize())
2049 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d > %d)!",
2050 nDiff, pd->m_diffList.GetSize());
2052 if (nDiff >= 0 && nDiff < pd->m_diffList.GetSize())
2054 CPoint ptStart, ptEnd;
2056 pd->m_diffList.GetDiff(nDiff, curDiff);
2059 ptStart.y = curDiff.dbegin[0];
2061 ptEnd.y = curDiff.dend[0];
2065 if (!IsDiffVisible(curDiff, CONTEXT_LINES_BELOW))
2067 // Difference is not visible, scroll it so that max amount of
2068 // scrolling is done while keeping the diff in screen. So if
2069 // scrolling is downwards, scroll the diff to as up in screen
2070 // as possible. This usually brings next diff to the screen
2071 // and we don't need to scroll into it.
2072 int nLine = GetSubLineIndex(ptStart.y);
2073 if (nLine > CONTEXT_LINES_ABOVE)
2075 nLine -= CONTEXT_LINES_ABOVE;
2077 pd->GetView(m_nThisPane)->ScrollToSubLine(nLine);
2078 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2080 if (nPane != m_nThisPane)
2081 pd->GetView(nPane)->ScrollToSubLine(nLine);
2084 pd->GetView(m_nThisPane)->SetCursorPos(ptStart);
2085 pd->GetView(m_nThisPane)->SetAnchor(ptStart);
2086 pd->GetView(m_nThisPane)->SetSelection(ptStart, ptStart);
2087 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2089 if (nPane != m_nThisPane)
2091 pd->GetView(nPane)->SetCursorPos(ptStart);
2092 pd->GetView(nPane)->SetAnchor(ptStart);
2093 pd->GetView(nPane)->SetSelection(ptStart, ptStart);
2100 ptEnd.x = GetLineLength(ptEnd.y);
2101 SetSelection(ptStart, ptEnd);
2110 void CMergeEditView::OnTimer(UINT_PTR nIDEvent)
2112 // Maybe we want theApp::OnIdle to proceed before processing a timer message
2113 // ...but for this the queue must be empty
2114 // The timer message is a low priority message but the queue is maybe not yet empty
2115 // So we set a flag, wait for OnIdle to proceed, then come back here...
2116 // We come back here with a IDLE_TIMER OnTimer message (send with SendMessage
2117 // not with SetTimer so there is no delay)
2119 // IDT_RESCAN was posted because the app wanted to do a flushAndRescan with some delay
2121 // IDLE_TIMER is the false timer used to come back here after OnIdle
2122 // fTimerWaitingForIdle is a bool to store the commands waiting for idle
2123 // (one normal timer = one flag = one command)
2125 if (nIDEvent == IDT_RESCAN)
2127 KillTimer(IDT_RESCAN);
2128 fTimerWaitingForIdle |= FLAG_RESCAN_WAITS_FOR_IDLE;
2129 // notify the app to come back after OnIdle
2130 theApp.SetNeedIdleTimer();
2133 if (nIDEvent == IDLE_TIMER)
2135 // not a real timer, just come back after OnIdle
2136 // look to flags to know what to do
2137 if (fTimerWaitingForIdle & FLAG_RESCAN_WAITS_FOR_IDLE)
2138 GetDocument()->RescanIfNeeded(RESCAN_TIMEOUT/1000);
2139 fTimerWaitingForIdle = 0;
2142 CCrystalEditViewEx::OnTimer(nIDEvent);
2146 * @brief Returns if buffer is read-only
2147 * @note This has no any relation to file being read-only!
2149 bool CMergeEditView::IsReadOnly(int pane)
2151 return GetDocument()->m_ptBuf[pane]->GetReadOnly() != FALSE;
2155 * @brief Called when "Save left (as...)" item is updated
2157 void CMergeEditView::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
2159 CMergeDoc *pd = GetDocument();
2161 if (!IsReadOnly(0) && pd->m_ptBuf[0]->IsModified())
2162 pCmdUI->Enable(true);
2164 pCmdUI->Enable(false);
2168 * @brief Called when "Save middle (as...)" item is updated
2170 void CMergeEditView::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
2172 CMergeDoc *pd = GetDocument();
2174 if (pd->m_nBuffers == 3 && !IsReadOnly(1) && pd->m_ptBuf[1]->IsModified())
2175 pCmdUI->Enable(TRUE);
2177 pCmdUI->Enable(FALSE);
2181 * @brief Called when "Save right (as...)" item is updated
2183 void CMergeEditView::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
2185 CMergeDoc *pd = GetDocument();
2187 if (!IsReadOnly(pd->m_nBuffers - 1) && pd->m_ptBuf[pd->m_nBuffers - 1]->IsModified())
2188 pCmdUI->Enable(true);
2190 pCmdUI->Enable(false);
2194 * @brief Refresh display using text-buffers
2195 * @note This DOES NOT reload files!
2197 void CMergeEditView::OnRefresh()
2199 CMergeDoc *pd = GetDocument();
2201 pd->FlushAndRescan(true);
2205 * @brief Enable/Disable automatic rescanning
2207 bool CMergeEditView::EnableRescan(bool bEnable)
2209 bool bOldValue = m_bAutomaticRescan;
2210 m_bAutomaticRescan = bEnable;
2215 * @brief Handle some keys when in merging mode
2217 bool CMergeEditView::MergeModeKeyDown(MSG* pMsg)
2219 bool bHandled = false;
2221 // Allow default text selection when SHIFT pressed
2222 if (::GetAsyncKeyState(VK_SHIFT))
2225 // Allow default editor functions when CTRL pressed
2226 if (::GetAsyncKeyState(VK_CONTROL))
2229 // If we are in merging mode (merge with cursor keys)
2230 // handle some keys here
2231 switch (pMsg->wParam)
2258 * @brief Called before messages are translated.
2260 * Checks if ESC key was pressed, saves and closes doc.
2261 * Also if in merge mode traps cursor keys.
2263 BOOL CMergeEditView::PreTranslateMessage(MSG* pMsg)
2265 if (pMsg->message == WM_KEYDOWN)
2267 bool bHandled = false;
2269 // If we are in merging mode (merge with cursor keys)
2270 // handle some keys here
2271 if (GetDocument()->GetMergingMode())
2273 bHandled = MergeModeKeyDown(pMsg);
2278 // Close window if user has allowed it from options
2279 bool bCloseWithEsc = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
2280 if (pMsg->wParam == VK_ESCAPE && bCloseWithEsc)
2282 GetParentFrame()->PostMessage(WM_CLOSE, 0, 0);
2286 return CCrystalEditViewEx::PreTranslateMessage(pMsg);
2290 * @brief Called when "Save" item is updated
2292 void CMergeEditView::OnUpdateFileSave(CCmdUI* pCmdUI)
2294 CMergeDoc *pd = GetDocument();
2296 BOOL bModified = FALSE;
2297 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2299 if (pd->m_ptBuf[nPane]->IsModified())
2303 pCmdUI->Enable(true);
2305 pCmdUI->Enable(false);
2309 * @brief Enable/disable left buffer read-only
2311 void CMergeEditView::OnLeftReadOnly()
2313 CMergeDoc *pd = GetDocument();
2314 BOOL bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2315 pd->m_ptBuf[0]->SetReadOnly(!bReadOnly);
2319 * @brief Called when "Left read-only" item is updated
2321 void CMergeEditView::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
2323 CMergeDoc *pd = GetDocument();
2324 BOOL bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2325 pCmdUI->Enable(true);
2326 pCmdUI->SetCheck(bReadOnly);
2330 * @brief Enable/disable middle buffer read-only
2332 void CMergeEditView::OnMiddleReadOnly()
2334 CMergeDoc *pd = GetDocument();
2335 if (pd->m_nBuffers == 3)
2337 BOOL bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2338 pd->m_ptBuf[1]->SetReadOnly(!bReadOnly);
2343 * @brief Called when "Middle read-only" item is updated
2345 void CMergeEditView::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
2347 CMergeDoc *pd = GetDocument();
2348 if (pd->m_nBuffers < 3)
2350 pCmdUI->Enable(FALSE);
2354 BOOL bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2355 pCmdUI->Enable(TRUE);
2356 pCmdUI->SetCheck(bReadOnly);
2361 * @brief Enable/disable right buffer read-only
2363 void CMergeEditView::OnRightReadOnly()
2365 CMergeDoc *pd = GetDocument();
2366 BOOL bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2367 pd->m_ptBuf[pd->m_nBuffers - 1]->SetReadOnly(!bReadOnly);
2371 * @brief Called when "Left read-only" item is updated
2373 void CMergeEditView::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
2375 CMergeDoc *pd = GetDocument();
2376 BOOL bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2377 pCmdUI->Enable(true);
2378 pCmdUI->SetCheck(bReadOnly);
2381 /// Store interface we use to display status line info
2382 void CMergeEditView::SetStatusInterface(IMergeEditStatus * piMergeEditStatus)
2384 ASSERT(!m_piMergeEditStatus);
2385 m_piMergeEditStatus = piMergeEditStatus;
2389 * @brief Update status bar contents.
2391 void CMergeEditView::UpdateStatusbar()
2397 * @brief Update statusbar info, Override from CCrystalTextView
2398 * @note we tab-expand column, but we don't tab-expand char count,
2399 * since we want to show how many chars there are and tab is just one
2400 * character although it expands to several spaces.
2402 void CMergeEditView::OnUpdateCaret()
2404 if (!m_piMergeEditStatus || !IsTextBufferInitialized())
2407 CPoint cursorPos = GetCursorPos();
2408 int nScreenLine = cursorPos.y;
2409 const int nRealLine = ComputeRealLine(nScreenLine);
2416 DWORD dwLineFlags = 0;
2418 dwLineFlags = m_pTextBuffer->GetLineFlags(nScreenLine);
2419 // Is this a ghost line ?
2420 if (dwLineFlags & LF_GHOST)
2422 // Ghost lines display eg "Line 12-13"
2423 sLine.Format(_T("%d-%d"), nRealLine, nRealLine+1);
2424 sEol = _T("hidden");
2428 // Regular lines display eg "Line 13 Characters: 25 EOL: CRLF"
2429 sLine.Format(_T("%d"), nRealLine+1);
2430 curChar = cursorPos.x + 1;
2431 chars = GetLineLength(nScreenLine);
2432 column = CalculateActualOffset(nScreenLine, cursorPos.x, true) + 1;
2433 columns = CalculateActualOffset(nScreenLine, chars, true) + 1;
2435 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2436 GetDocument()->IsMixedEOL(m_nThisPane))
2438 sEol = GetTextBufferEol(nScreenLine);
2441 sEol = _T("hidden");
2443 m_piMergeEditStatus->SetLineInfo(sLine, column, columns,
2444 curChar, chars, sEol, GetDocument()->m_ptBuf[m_nThisPane]->getCodepage());
2446 // Is cursor inside difference?
2447 if (dwLineFlags & LF_NONTRIVIAL_DIFF)
2448 m_bCurrentLineIsDiff = true;
2450 m_bCurrentLineIsDiff = false;
2452 UpdateLocationViewPosition(m_nTopSubLine, m_nTopSubLine + GetScreenLines());
2455 * @brief Select linedifference in the current line.
2457 * Select line difference in current line. Selection type
2458 * is choosed by highlight type.
2460 void CMergeEditView::OnSelectLineDiff()
2462 CMergeDoc::DIFFLEVEL level = CMergeDoc::BYTEDIFF;
2463 if (GetOptionsMgr()->GetBool(OPT_BREAK_ON_WORDS))
2464 level = CMergeDoc::WORDDIFF;
2466 // Pass this to the document, to compare this file to other
2467 GetDocument()->Showlinediff(this, level);
2470 /// Enable select difference menuitem if current line is inside difference.
2471 void CMergeEditView::OnUpdateSelectLineDiff(CCmdUI* pCmdUI)
2473 int line = GetCursorPos().y;
2474 bool enable = (GetLineFlags(line) & LF_DIFF) != 0;
2475 pCmdUI->Enable(enable);
2479 * @brief Enable/disable Replace-menuitem
2481 void CMergeEditView::OnUpdateEditReplace(CCmdUI* pCmdUI)
2483 CMergeDoc *pd = GetDocument();
2484 BOOL bReadOnly = pd->m_ptBuf[m_nThisPane]->GetReadOnly();
2486 pCmdUI->Enable(!bReadOnly);
2490 * @brief Update readonly statusbaritem
2492 void CMergeEditView::OnUpdateStatusRO(CCmdUI* pCmdUI)
2494 BOOL bRO = GetDocument()->m_ptBuf[pCmdUI->m_nID - ID_STATUS_PANE0FILE_RO]->GetReadOnly();
2495 pCmdUI->Enable(bRO);
2499 * @brief Call ::AppendMenu, and if it fails get error string into local variable
2501 * This only provides functionality for debugging.
2503 static bool DoAppendMenu(HMENU hMenu, UINT uFlags, UINT_PTR uIDNewItem, LPCTSTR lpNewItem)
2505 bool ok = ::AppendMenu(hMenu, uFlags, uIDNewItem, lpNewItem) != FALSE;
2508 int nerr = GetLastError();
2509 String syserr = GetSysError(nerr);
2515 * @brief Create the dynamic submenu for scripts
2517 HMENU CMergeEditView::createScriptsSubmenu(HMENU hMenu)
2520 CStringArray functionNamesList;
2521 GetFreeFunctionsInScripts(functionNamesList, L"EDITOR_SCRIPT");
2524 int i = GetMenuItemCount(hMenu);
2526 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2528 if (functionNamesList.GetSize() == 0)
2530 // no script : create a <empty> entry
2531 DoAppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, theApp.LoadString(ID_NO_EDIT_SCRIPTS).c_str());
2535 // or fill in the submenu with the scripts names
2536 int ID = ID_SCRIPT_FIRST; // first ID in menu
2537 for (i = 0 ; i < functionNamesList.GetSize() ; i++, ID++)
2538 DoAppendMenu(hMenu, MF_STRING, ID, functionNamesList[i]);
2540 functionNamesList.RemoveAll();
2543 if (IsWindowsScriptThere() == FALSE)
2544 DoAppendMenu(hMenu, MF_STRING, ID_NO_SCT_SCRIPTS, theApp.LoadString(ID_NO_SCT_SCRIPTS).c_str());
2550 * @brief Create the dynamic submenu for prediffers
2552 * @note The plugins are grouped in (suggested) and (not suggested)
2553 * The IDs follow the order of GetAvailableScripts
2555 * suggested 0 ID_1ST + 0
2556 * suggested 1 ID_1ST + 2
2557 * suggested 2 ID_1ST + 5
2558 * not suggested 0 ID_1ST + 1
2559 * not suggested 1 ID_1ST + 3
2560 * not suggested 2 ID_1ST + 4
2562 HMENU CMergeEditView::createPrediffersSubmenu(HMENU hMenu)
2565 int i = GetMenuItemCount(hMenu);
2567 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2569 CMergeDoc *pd = GetDocument();
2573 DoAppendMenu(hMenu, MF_STRING, ID_NO_PREDIFFER, theApp.LoadString(ID_NO_PREDIFFER).c_str());
2575 // get the scriptlet files
2576 PluginArray * piScriptArray =
2577 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
2578 PluginArray * piScriptArray2 =
2579 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
2581 // build the menu : first part, suggested plugins
2583 DoAppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
2584 DoAppendMenu(hMenu, MF_STRING, ID_SUGGESTED_PLUGINS, theApp.LoadString(ID_SUGGESTED_PLUGINS).c_str());
2586 int ID = ID_PREDIFFERS_FIRST; // first ID in menu
2588 for (iScript = 0 ; iScript < piScriptArray->GetSize() ; iScript++, ID ++)
2590 PluginInfo & plugin = piScriptArray->ElementAt(iScript);
2591 if (plugin.TestAgainstRegList(pd->m_strBothFilenames.c_str()) == FALSE)
2594 DoAppendMenu(hMenu, MF_STRING, ID, plugin.m_name.c_str());
2596 for (iScript = 0 ; iScript < piScriptArray2->GetSize() ; iScript++, ID ++)
2598 PluginInfo & plugin = piScriptArray2->ElementAt(iScript);
2599 if (plugin.TestAgainstRegList(pd->m_strBothFilenames.c_str()) == FALSE)
2602 DoAppendMenu(hMenu, MF_STRING, ID, plugin.m_name.c_str());
2605 // build the menu : second part, others plugins
2607 DoAppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
2608 DoAppendMenu(hMenu, MF_STRING, ID_NOT_SUGGESTED_PLUGINS, theApp.LoadString(ID_NOT_SUGGESTED_PLUGINS).c_str());
2610 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2611 for (iScript = 0 ; iScript < piScriptArray->GetSize() ; iScript++, ID ++)
2613 PluginInfo & plugin = piScriptArray->ElementAt(iScript);
2614 if (plugin.TestAgainstRegList(pd->m_strBothFilenames.c_str()) != FALSE)
2617 DoAppendMenu(hMenu, MF_STRING, ID, plugin.m_name.c_str());
2619 for (iScript = 0 ; iScript < piScriptArray2->GetSize() ; iScript++, ID ++)
2621 PluginInfo & plugin = piScriptArray2->ElementAt(iScript);
2622 if (plugin.TestAgainstRegList(pd->m_strBothFilenames.c_str()) != FALSE)
2625 DoAppendMenu(hMenu, MF_STRING, ID, plugin.m_name.c_str());
2628 // compute the m_CurrentPredifferID (to set the radio button)
2629 PrediffingInfo prediffer;
2630 pd->GetPrediffer(&prediffer);
2632 if (prediffer.bToBeScanned)
2633 m_CurrentPredifferID = 0;
2634 else if (prediffer.pluginName.empty())
2635 m_CurrentPredifferID = ID_NO_PREDIFFER;
2638 ID = ID_PREDIFFERS_FIRST; // first ID in menu
2639 for (iScript = 0 ; iScript < piScriptArray->GetSize() ; iScript++, ID ++)
2641 PluginInfo & plugin = piScriptArray->ElementAt(iScript);
2642 if (prediffer.pluginName == plugin.m_name)
2643 m_CurrentPredifferID = ID;
2646 for (iScript = 0 ; iScript < piScriptArray2->GetSize() ; iScript++, ID ++)
2648 PluginInfo & plugin = piScriptArray2->ElementAt(iScript);
2649 if (prediffer.pluginName == plugin.m_name)
2650 m_CurrentPredifferID = ID;
2658 * @brief Offer a context menu built with scriptlet/ActiveX functions
2660 void CMergeEditView::OnContextMenu(CWnd* pWnd, CPoint point)
2662 // Create the menu and populate it with the available functions
2664 VERIFY(menu.LoadMenu(IDR_POPUP_MERGEVIEW));
2666 // Remove copying item copying from active side
2667 if (m_nThisPane == 0) // left?
2668 menu.RemoveMenu(ID_R2L, MF_BYCOMMAND);
2670 menu.RemoveMenu(ID_L2R, MF_BYCOMMAND);
2672 VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
2673 theApp.TranslateMenu(menu.m_hMenu);
2675 BCMenu *pSub = (BCMenu *)menu.GetSubMenu(0);
2676 ASSERT(pSub != NULL);
2678 // Context menu opened using keyboard has no coordinates
2679 if (point.x == -1 && point.y == -1)
2682 GetClientRect(rect);
2683 ClientToScreen(rect);
2685 point = rect.TopLeft();
2689 pSub->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
2690 point.x, point.y, AfxGetMainWnd());
2695 * @brief Update EOL mode in status bar
2697 void CMergeEditView::OnUpdateStatusEOL(CCmdUI* pCmdUI)
2699 GetDocument()->GetView(pCmdUI->m_nID - ID_STATUS_PANE0FILE_EOL)->OnUpdateIndicatorCRLF(pCmdUI);
2703 * @brief Change EOL mode and unify all the lines EOL to this new mode
2705 void CMergeEditView::OnConvertEolTo(UINT nID )
2707 CRLFSTYLE nStyle = CRLF_STYLE_AUTOMATIC;;
2711 nStyle = CRLF_STYLE_DOS;
2713 case ID_EOL_TO_UNIX:
2714 nStyle = CRLF_STYLE_UNIX;
2717 nStyle = CRLF_STYLE_MAC;
2721 _RPTF0(_CRT_ERROR, "Unhandled EOL type conversion!");
2724 m_pTextBuffer->SetCRLFMode(nStyle);
2726 // we don't need a derived applyEOLMode for ghost lines as they have no EOL char
2727 if (m_pTextBuffer->applyEOLMode())
2729 CMergeDoc *pd = GetDocument();
2731 pd->UpdateHeaderPath(m_nThisPane);
2732 pd->FlushAndRescan(true);
2737 * @brief allow convert to entries in file submenu
2739 void CMergeEditView::OnUpdateConvertEolTo(CCmdUI* pCmdUI)
2741 int nStyle = CRLF_STYLE_AUTOMATIC;
2742 switch (pCmdUI->m_nID)
2745 nStyle = CRLF_STYLE_DOS;
2747 case ID_EOL_TO_UNIX:
2748 nStyle = CRLF_STYLE_UNIX;
2751 nStyle = CRLF_STYLE_MAC;
2755 _RPTF0(_CRT_ERROR, "Missing menuitem handler for EOL convert menu!");
2759 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2760 GetDocument()->IsMixedEOL(m_nThisPane) ||
2761 nStyle != m_pTextBuffer->GetCRLFMode())
2763 pCmdUI->SetRadio(false);
2765 // Don't allow selecting other EOL style for protected pane
2766 if (IsReadOnly(m_nThisPane))
2767 pCmdUI->Enable(false);
2770 pCmdUI->SetRadio(true);
2774 * @brief Copy diff from left to right and advance to next diff
2776 void CMergeEditView::OnL2RNext()
2778 // Check that diff is selected
2779 if (GetDocument()->GetCurrentDiff() == -1)
2783 if (this->IsCursorInDiff())
2789 * @brief Update "Copy right and advance" UI item
2791 void CMergeEditView::OnUpdateL2RNext(CCmdUI* pCmdUI)
2793 // Check that right side is not readonly
2794 if (!IsReadOnly(m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1))
2795 pCmdUI->Enable(GetDocument()->GetCurrentDiff()!=-1);
2797 pCmdUI->Enable(false);
2801 * @brief Copy diff from right to left and advance to next diff
2803 void CMergeEditView::OnR2LNext()
2805 // Check that diff is selected
2806 if (GetDocument()->GetCurrentDiff() == -1)
2810 if (this->IsCursorInDiff())
2816 * @brief Update "Copy left and advance" UI item
2818 void CMergeEditView::OnUpdateR2LNext(CCmdUI* pCmdUI)
2820 // Check that left side is not readonly
2821 if (!IsReadOnly(m_nThisPane > 0 ? m_nThisPane - 1 : 0))
2822 pCmdUI->Enable(GetDocument()->GetCurrentDiff()!=-1);
2824 pCmdUI->Enable(false);
2828 * @brief Change active pane in MergeView.
2829 * Changes active pane and makes sure cursor position is kept in
2830 * screen. Currently we put cursor in same line than in original
2831 * active pane but we could be smarter too? Maybe update cursor
2832 * only when it is not visible in new pane?
2834 void CMergeEditView::OnChangePane()
2836 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
2837 CMergeEditView *pWnd = static_cast<CMergeEditView*>(pSplitterWnd->GetActivePane());
2838 CPoint ptCursor = pWnd->GetCursorPos();
2839 pSplitterWnd->ActivateNext();
2840 pWnd = static_cast<CMergeEditView*>(pSplitterWnd->GetActivePane());
2842 if (ptCursor.y >= pWnd->GetLineCount())
2843 ptCursor.y = pWnd->GetLineCount() - 1;
2844 pWnd->SetCursorPos(ptCursor);
2845 pWnd->SetAnchor(ptCursor);
2846 pWnd->SetSelection(ptCursor, ptCursor);
2850 * @brief Enable "Change Pane" menuitem when mergeview is active
2852 void CMergeEditView::OnUpdateChangePane(CCmdUI* pCmdUI)
2854 pCmdUI->Enable(true);
2858 * @brief Show "Go To" dialog and scroll views to line or diff.
2860 * Before dialog is opened, current line and file is determined
2862 * @note Conversions needed between apparent and real lines
2864 void CMergeEditView::OnWMGoto()
2867 CMergeDoc *pDoc = GetDocument();
2868 CPoint pos = GetCursorPos();
2872 nRealLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(pos.y);
2873 int nLineCount = pDoc->m_ptBuf[m_nThisPane]->GetLineCount();
2874 nLastLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(nLineCount - 1);
2876 // Set active file and current line selected in dialog
2877 dlg.m_strParam.Format(_T("%d"), nRealLine + 1);
2878 dlg.m_nFile = (pDoc->m_nBuffers < 3) ? (m_nThisPane == 1 ? 2 : 0) : m_nThisPane;
2879 dlg.m_nGotoWhat = 0;
2881 if (dlg.DoModal() == IDOK)
2883 CMergeDoc * pDoc = GetDocument();
2884 CMergeEditView * pCurrentView = NULL;
2887 pCurrentView = pDoc->GetView(m_nThisPane);
2889 if (dlg.m_nGotoWhat == 0)
2891 int nRealLine = _ttoi(dlg.m_strParam) - 1;
2894 if (nRealLine > nLastLine)
2895 nRealLine = nLastLine;
2897 GotoLine(nRealLine, true, (pDoc->m_nBuffers < 3) ? (dlg.m_nFile == 2 ? 1 : 0) : dlg.m_nFile);
2901 int diff = _ttoi(dlg.m_strParam) - 1;
2904 if (diff >= pDoc->m_diffList.GetSize())
2905 diff = pDoc->m_diffList.GetSize();
2907 pCurrentView->SelectDiff(diff, true, false);
2913 * @brief Enable "Go To" menuitem when mergeview is active
2915 void CMergeEditView::OnUpdateWMGoto(CCmdUI* pCmdUI)
2917 pCmdUI->Enable(true);
2921 * @brief Reload options.
2923 void CMergeEditView::RefreshOptions()
2925 m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
2927 if (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0)
2928 SetInsertTabs(true);
2930 SetInsertTabs(false);
2932 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
2934 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
2935 SetTextType(CCrystalTextView::SRC_PLAIN);
2937 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
2938 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
2939 m_cachedColors.clrDiff = GetOptionsMgr()->GetInt(OPT_CLR_DIFF);
2940 m_cachedColors.clrSelDiff = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_DIFF);
2941 m_cachedColors.clrDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_DIFF_DELETED);
2942 m_cachedColors.clrSelDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_DIFF_DELETED);
2943 m_cachedColors.clrDiffText = GetOptionsMgr()->GetInt(OPT_CLR_DIFF_TEXT);
2944 m_cachedColors.clrSelDiffText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_DIFF_TEXT);
2945 m_cachedColors.clrTrivial = GetOptionsMgr()->GetInt(OPT_CLR_TRIVIAL_DIFF);
2946 m_cachedColors.clrTrivialDeleted = GetOptionsMgr()->GetInt(OPT_CLR_TRIVIAL_DIFF_DELETED);
2947 m_cachedColors.clrTrivialText = GetOptionsMgr()->GetInt(OPT_CLR_TRIVIAL_DIFF_TEXT);
2948 m_cachedColors.clrMoved = GetOptionsMgr()->GetInt(OPT_CLR_MOVEDBLOCK);
2949 m_cachedColors.clrMovedDeleted = GetOptionsMgr()->GetInt(OPT_CLR_MOVEDBLOCK_DELETED);
2950 m_cachedColors.clrMovedText = GetOptionsMgr()->GetInt(OPT_CLR_MOVEDBLOCK_TEXT);
2951 m_cachedColors.clrSelMoved = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_MOVEDBLOCK);
2952 m_cachedColors.clrSelMovedDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_MOVEDBLOCK_DELETED);
2953 m_cachedColors.clrSelMovedText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_MOVEDBLOCK_TEXT);
2954 m_cachedColors.clrSNP = GetOptionsMgr()->GetInt(OPT_CLR_SNP);
2955 m_cachedColors.clrSNPDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SNP_DELETED);
2956 m_cachedColors.clrSNPText = GetOptionsMgr()->GetInt(OPT_CLR_SNP_TEXT);
2957 m_cachedColors.clrSelSNP = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_SNP);
2958 m_cachedColors.clrSelSNPDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_SNP_DELETED);
2959 m_cachedColors.clrSelSNPText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_SNP_TEXT);
2960 m_cachedColors.clrWordDiff = GetOptionsMgr()->GetInt(OPT_CLR_WORDDIFF);
2961 m_cachedColors.clrWordDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_WORDDIFF_DELETED);
2962 m_cachedColors.clrSelWordDiff = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_WORDDIFF);
2963 m_cachedColors.clrSelWordDiffDeleted = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_WORDDIFF_DELETED);
2964 m_cachedColors.clrWordDiffText = GetOptionsMgr()->GetInt(OPT_CLR_WORDDIFF_TEXT);
2965 m_cachedColors.clrSelWordDiffText = GetOptionsMgr()->GetInt(OPT_CLR_SELECTED_WORDDIFF_TEXT);
2969 * @brief Called when an editor script item is updated
2971 void CMergeEditView::OnUpdateScripts(CCmdUI* pCmdUI)
2973 pCmdUI->Enable(true);
2976 void CMergeEditView::OnScripts(UINT nID )
2978 // text is CHAR if compiled without UNICODE, WCHAR with UNICODE
2979 CString text = GetSelectedText();
2981 // transform the text with a script/ActiveX function, event=EDITOR_SCRIPT
2982 BOOL bChanged = TextTransform_Interactive(text, L"EDITOR_SCRIPT", nID - ID_SCRIPT_FIRST);
2984 // now replace the text
2985 ReplaceSelection(text, text.GetLength(), 0);
2989 * @brief Called when an editor script item is updated
2991 void CMergeEditView::OnUpdateNoEditScripts(CCmdUI* pCmdUI)
2993 // append the scripts submenu
2994 HMENU scriptsSubmenu = dynamic_cast<CMainFrame*>(AfxGetMainWnd())->GetScriptsSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu);
2995 if (scriptsSubmenu != NULL)
2996 createScriptsSubmenu(scriptsSubmenu);
2998 pCmdUI->Enable(true);
3002 * @brief Called when an editor script item is updated
3004 void CMergeEditView::OnUpdatePrediffer(CCmdUI* pCmdUI)
3006 pCmdUI->Enable(true);
3008 CMergeDoc *pd = GetDocument();
3010 PrediffingInfo prediffer;
3011 pd->GetPrediffer(&prediffer);
3013 if (prediffer.bToBeScanned)
3015 pCmdUI->SetRadio(false);
3019 // Detect when CDiffWrapper::RunFileDiff has canceled a buggy prediffer
3020 if (prediffer.pluginName.empty())
3021 m_CurrentPredifferID = ID_NO_PREDIFFER;
3023 pCmdUI->SetRadio(pCmdUI->m_nID == m_CurrentPredifferID);
3027 * @brief Update "Prediffer" menuitem
3029 void CMergeEditView::OnUpdateNoPrediffer(CCmdUI* pCmdUI)
3031 // recreate the sub menu (to fill the "selected prediffers")
3032 GetMainFrame()->UpdatePrediffersMenu();
3033 OnUpdatePrediffer(pCmdUI);
3036 void CMergeEditView::OnNoPrediffer()
3038 OnPrediffer(ID_NO_PREDIFFER);
3041 * @brief Handler for all prediffer choices, including ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, ID_NO_PREDIFFER, & specific prediffers
3043 void CMergeEditView::OnPrediffer(UINT nID )
3045 CMergeDoc *pd = GetDocument();
3048 SetPredifferByMenu(nID);
3049 pd->FlushAndRescan(true);
3053 * @brief Handler for all prediffer choices.
3054 * Prediffer choises include ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO,
3055 * ID_NO_PREDIFFER, & specific prediffers.
3057 void CMergeEditView::SetPredifferByMenu(UINT nID )
3059 CMergeDoc *pd = GetDocument();
3062 if (nID == ID_NO_PREDIFFER)
3064 m_CurrentPredifferID = nID;
3065 // All flags are set correctly during the construction
3066 PrediffingInfo *infoPrediffer = new PrediffingInfo;
3067 infoPrediffer->bToBeScanned = 0;
3068 infoPrediffer->pluginName.clear();
3069 pd->SetPrediffer(infoPrediffer);
3070 pd->FlushAndRescan(true);
3074 // get the scriptlet files
3075 PluginArray * piScriptArray =
3076 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3077 PluginArray * piScriptArray2 =
3078 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3080 // build a PrediffingInfo structure fom the ID
3081 PrediffingInfo prediffer;
3082 prediffer.bToBeScanned = false;
3084 int pluginNumber = nID - ID_PREDIFFERS_FIRST;
3085 if (pluginNumber < piScriptArray->GetSize())
3087 prediffer.bWithFile = true;
3088 PluginInfo & plugin = piScriptArray->ElementAt(pluginNumber);
3089 prediffer.pluginName = plugin.m_name;
3093 pluginNumber -= piScriptArray->GetSize();
3094 if (pluginNumber >= piScriptArray2->GetSize())
3096 prediffer.bWithFile = false;
3097 PluginInfo & plugin = piScriptArray2->ElementAt(pluginNumber);
3098 prediffer.pluginName = plugin.m_name;
3101 // update data for the radio button
3102 m_CurrentPredifferID = nID;
3104 // update the prediffer and rescan
3105 pd->SetPrediffer(&prediffer);
3109 * @brief Look through available prediffers, and return ID of requested one, if found
3111 int CMergeEditView::FindPrediffer(LPCTSTR prediffer) const
3114 int ID = ID_PREDIFFERS_FIRST;
3116 // Search file prediffers
3117 PluginArray * piScriptArray =
3118 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3119 for (i=0; i<piScriptArray->GetSize(); ++i, ++ID)
3121 PluginInfo & plugin = piScriptArray->ElementAt(i);
3122 if (plugin.m_name == prediffer)
3126 // Search buffer prediffers
3127 PluginArray * piScriptArray2 =
3128 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3129 for (i=0; i<piScriptArray2->GetSize(); ++i, ++ID)
3131 PluginInfo & plugin = piScriptArray2->ElementAt(i);
3132 if (plugin.m_name == prediffer)
3140 * @brief Look through available prediffers, and return ID of requested one, if found
3142 bool CMergeEditView::SetPredifferByName(const CString & prediffer)
3144 int id = FindPrediffer(prediffer);
3145 if (id<0) return false;
3146 SetPredifferByMenu(id);
3151 * @brief Switch Merging/Editing mode and update
3152 * buffer read-only states accordingly
3154 void CMergeEditView::OnMergingMode()
3156 CMergeDoc *pDoc = GetDocument();
3157 bool bMergingMode = pDoc->GetMergingMode();
3160 LangMessageBox(IDS_MERGE_MODE, MB_ICONINFORMATION | MB_DONT_DISPLAY_AGAIN);
3161 pDoc->SetMergingMode(!bMergingMode);
3165 * @brief Update Menuitem for Merging Mode
3167 void CMergeEditView::OnUpdateMergingMode(CCmdUI* pCmdUI)
3169 pCmdUI->Enable(true);
3170 pCmdUI->SetCheck(GetDocument()->GetMergingMode());
3174 * @brief Update MergingMode UI in statusbar
3176 void CMergeEditView::OnUpdateMergingStatus(CCmdUI *pCmdUI)
3178 String text = theApp.LoadString(IDS_MERGEMODE_MERGING);
3179 pCmdUI->SetText(text.c_str());
3180 pCmdUI->Enable(GetDocument()->GetMergingMode());
3184 * @brief Goto given line.
3185 * @param [in] nLine Destination linenumber
3186 * @param [in] bRealLine if TRUE linenumber is real line, otherwise
3187 * it is apparent line (including deleted lines)
3188 * @param [in] pane Pane index of goto target pane (0 = left, 1 = right).
3190 void CMergeEditView::GotoLine(UINT nLine, bool bRealLine, int pane)
3192 CMergeDoc *pDoc = GetDocument();
3193 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3194 CMergeEditView *pCurrentView = static_cast<CMergeEditView*>
3195 (pSplitterWnd->GetActivePane());
3197 int nRealLine = nLine;
3198 int nApparentLine = nLine;
3200 // Compute apparent (shown linenumber) line
3203 if (nRealLine > pDoc->m_ptBuf[pane]->GetLineCount() - 1)
3204 nRealLine = pDoc->m_ptBuf[pane]->GetLineCount() - 1;
3206 nApparentLine = pDoc->m_ptBuf[pane]->ComputeApparentLine(nRealLine);
3210 ptPos.y = nApparentLine;
3212 // Scroll line to center of view
3213 int nScrollLine = GetSubLineIndex(nApparentLine);
3214 nScrollLine -= GetScreenLines() / 2;
3215 if (nScrollLine < 0)
3218 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3220 CMergeEditView *pView = pDoc->GetView(nPane);
3221 pView->ScrollToSubLine(nScrollLine);
3222 if (ptPos.y < pView->GetLineCount())
3224 pView->SetCursorPos(ptPos);
3225 pView->SetAnchor(ptPos);
3229 // If goto target is another view - activate another view.
3230 // This is done for user convenience as user probably wants to
3231 // work with goto target file.
3232 if (pDoc->GetView(pane) != pCurrentView)
3233 pSplitterWnd->SetActivePane(0, pane);
3237 * @brief Check for horizontal scroll. Re-route to CSplitterEx if not from
3240 void CMergeEditView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3242 if (pScrollBar == NULL)
3244 // Scroll did not come frome a scroll bar
3245 // Find the appropriate scroll bar
3246 // and send the message to the splitter window instead
3247 // The event should eventually come back here but with a valid scrollbar
3248 // Along the way it will be propagated to other windows that need it
3249 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3250 CScrollBar* curBar = this->GetScrollBarCtrl(SB_HORZ);
3251 pSplitterWnd->SendMessage(WM_HSCROLL,
3252 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3255 CCrystalTextView::OnHScroll (nSBCode, nPos, pScrollBar);
3259 * @brief When view is scrolled using scrollbars update location pane.
3261 void CMergeEditView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3263 if (pScrollBar == NULL)
3265 // Scroll did not come frome a scroll bar
3266 // Find the appropriate scroll bar
3267 // and send the message to the splitter window instead
3268 // The event should eventually come back here but with a valid scrollbar
3269 // Along the way it will be propagated to other windows that need it
3270 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3271 CScrollBar* curBar = this->GetScrollBarCtrl(SB_VERT);
3272 pSplitterWnd->SendMessage(WM_VSCROLL,
3273 MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3276 CCrystalTextView::OnVScroll (nSBCode, nPos, pScrollBar);
3278 if (nSBCode == SB_ENDSCROLL)
3281 // Note we cannot use nPos because of its 16-bit nature
3282 SCROLLINFO si = {0};
3283 si.cbSize = sizeof (si);
3284 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3285 VERIFY (GetScrollInfo (SB_VERT, &si));
3287 // Get the current position of scroll box.
3288 int nCurPos = si.nPos;
3290 UpdateLocationViewPosition(nCurPos, nCurPos + GetScreenLines());
3294 * @brief Copy selected lines adding linenumbers.
3296 void CMergeEditView::OnEditCopyLineNumbers()
3307 CMergeDoc *pDoc = GetDocument();
3308 GetSelection(ptStart, ptEnd);
3310 // Get last selected line (having widest linenumber)
3311 line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(ptEnd.y);
3312 strNum.Format(_T("%d"), line + 1);
3313 nNumWidth = strNum.GetLength();
3315 for (int i = ptStart.y; i <= ptEnd.y; i++)
3317 if (GetLineFlags(i) & LF_GHOST || (GetEnableHideLines() && (GetLineFlags(i) & LF_INVISIBLE)))
3320 // We need to convert to real linenumbers
3321 line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(i);
3323 // Insert spaces to align different width linenumbers (99, 100)
3324 strLine = GetLineText(i);
3325 strNum.Format(_T("%d"), line + 1);
3326 CString sSpaces(' ', nNumWidth - strNum.GetLength());
3329 strNumLine.Format(_T("%d: %s"), line + 1, strLine);
3330 strText += strNumLine;
3332 PutToClipboard(strText, strText.GetLength(), m_bColumnSelection);
3335 void CMergeEditView::OnUpdateEditCopyLinenumbers(CCmdUI* pCmdUI)
3337 CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
3341 * @brief Open active file with associated application.
3343 * First tries to open file using shell 'Edit' action, since that
3344 * action open scripts etc. to editor instead of running them. If
3345 * edit-action is not registered, 'Open' action is used.
3347 void CMergeEditView::OnOpenFile()
3349 CMergeDoc * pDoc = GetDocument();
3350 ASSERT(pDoc != NULL);
3352 String sFileName = pDoc->m_filePaths[m_nThisPane];
3353 if (sFileName.empty())
3355 int rtn = (int)ShellExecute(::GetDesktopWindow(), _T("edit"), sFileName.c_str(),
3356 0, 0, SW_SHOWNORMAL);
3357 if (rtn==SE_ERR_NOASSOC)
3358 rtn = (int)ShellExecute(::GetDesktopWindow(), _T("open"), sFileName.c_str(),
3359 0, 0, SW_SHOWNORMAL);
3360 if (rtn==SE_ERR_NOASSOC)
3365 * @brief Open active file with app selection dialog
3367 void CMergeEditView::OnOpenFileWith()
3369 CMergeDoc * pDoc = GetDocument();
3370 ASSERT(pDoc != NULL);
3372 String sFileName = pDoc->m_filePaths[m_nThisPane];
3373 if (sFileName.empty())
3377 if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH))
3379 sysdir.ReleaseBuffer();
3380 CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + sFileName.c_str();
3381 ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg,
3382 sysdir, SW_SHOWNORMAL);
3386 * @brief Open active file with external editor
3388 void CMergeEditView::OnOpenFileWithEditor()
3390 CMergeDoc * pDoc = GetDocument();
3391 ASSERT(pDoc != NULL);
3393 String sFileName = pDoc->m_filePaths[m_nThisPane];
3394 if (sFileName.empty())
3397 int nRealLine = ComputeRealLine(GetCursorPos().y) + 1;
3398 GetMainFrame()->OpenFileToExternalEditor(sFileName.c_str(), nRealLine);
3402 * @brief Force repaint of the location pane.
3404 void CMergeEditView::RepaintLocationPane()
3406 // Must force recalculation due to caching of data in location pane.
3407 if (m_pLocationView)
3408 m_pLocationView->ForceRecalculate();
3412 * @brief Enables/disables linediff (different color for diffs)
3414 void CMergeEditView::OnViewLineDiffs()
3416 bool bWordDiffHighlight = GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
3417 GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, !bWordDiffHighlight);
3419 // Call CMergeDoc RefreshOptions() to refresh *both* views
3420 CMergeDoc *pDoc = GetDocument();
3421 pDoc->RefreshOptions();
3422 pDoc->FlushAndRescan(true);
3425 void CMergeEditView::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
3427 pCmdUI->Enable(true);
3428 pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
3432 * @brief Enables/disables line number
3434 void CMergeEditView::OnViewLineNumbers()
3436 GetOptionsMgr()->SaveOption(OPT_VIEW_LINENUMBERS, !GetViewLineNumbers());
3438 // Call CMergeDoc RefreshOptions() to refresh *both* views
3439 CMergeDoc *pDoc = GetDocument();
3440 pDoc->RefreshOptions();
3443 void CMergeEditView::OnUpdateViewLineNumbers(CCmdUI* pCmdUI)
3445 pCmdUI->Enable(true);
3446 pCmdUI->SetCheck(GetViewLineNumbers());
3450 * @brief Enables/disables word wrap
3452 void CMergeEditView::OnViewWordWrap()
3454 GetOptionsMgr()->SaveOption(OPT_WORDWRAP, !m_bWordWrap);
3456 // Call CMergeDoc RefreshOptions() to refresh *both* views
3457 CMergeDoc *pDoc = GetDocument();
3458 pDoc->RefreshOptions();
3459 pDoc->UpdateAllViews(this);
3464 void CMergeEditView::OnUpdateViewWordWrap(CCmdUI* pCmdUI)
3466 pCmdUI->Enable(true);
3467 pCmdUI->SetCheck(m_bWordWrap);
3470 void CMergeEditView::OnSize(UINT nType, int cx, int cy)
3472 if (!IsInitialized())
3475 CMergeDoc * pDoc = GetDocument();
3476 if (m_nThisPane == 0)
3478 // To calculate subline index correctly
3479 // we have to invalidate line cache in all pane before calling the function related the subline.
3480 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3482 CMergeEditView *pView = pDoc->GetView(nPane);
3484 pView->InvalidateScreenRect(FALSE);
3487 else if (m_nThisPane == pDoc->m_nBuffers - 1)
3489 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3491 CMergeEditView *pView = pDoc->GetView(nPane);
3493 pView->Invalidate();
3496 // recalculate m_nTopSubLine
3497 m_nTopSubLine = GetSubLineIndex(m_nTopLine);
3501 RecalcVertScrollBar (FALSE, FALSE);
3502 RecalcHorzScrollBar (FALSE, FALSE);
3504 ((CChildFrame *)GetParentFrame())->PostMessage(WM_TIMER);
3508 * @brief allocates GDI resources for printing
3509 * @param pDC [in] points to the printer device context
3510 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3512 void CMergeEditView::OnBeginPrinting(CDC * pDC, CPrintInfo * pInfo)
3514 ((CChildFrame *)GetParentFrame())->PostMessage(WM_TIMER);
3516 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3518 CMergeEditView *pView = GetDocument()->GetView(pane);
3519 pView->m_bPrintHeader = true;
3520 pView->m_bPrintFooter = true;
3521 pView->CGhostTextView::OnBeginPrinting(pDC, pInfo);
3526 * @brief frees GDI resources for printing
3527 * @param pDC [in] points to the printer device context
3528 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3530 void CMergeEditView::OnEndPrinting(CDC * pDC, CPrintInfo * pInfo)
3532 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3533 GetDocument()->GetView(pane)->CGhostTextView::OnEndPrinting(pDC, pInfo);
3535 ((CChildFrame *)GetParentFrame())->PostMessage(WM_TIMER);
3539 * @brief Gets header text to print
3540 * @param [in] nPageNum the page number to print
3541 * @param [out] header text to print
3543 void CMergeEditView::GetPrintHeaderText(int nPageNum, CString & text)
3545 text = GetDocument()->GetTitle();
3549 * @brief Prints header
3550 * @param [in] nPageNum the page number to print
3552 void CMergeEditView::PrintHeader(CDC * pdc, int nPageNum)
3554 if (m_nThisPane > 0)
3556 int oldRight = m_rcPrintArea.right;
3557 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3558 CGhostTextView::PrintHeader(pdc, nPageNum);
3559 m_rcPrintArea.right = oldRight;
3563 * @brief Prints footer
3564 * @param [in] nPageNum the page number to print
3566 void CMergeEditView::PrintFooter(CDC * pdc, int nPageNum)
3568 if (m_nThisPane > 0)
3570 int oldRight = m_rcPrintArea.right;
3571 m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3572 CGhostTextView::PrintFooter(pdc, nPageNum);
3573 m_rcPrintArea.right = oldRight;
3576 void CMergeEditView::RecalcPageLayouts (CDC * pDC, CPrintInfo * pInfo)
3578 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3579 GetDocument()->GetView(pane)->CGhostTextView::RecalcPageLayouts(pDC, pInfo);
3583 * @brief Prints or previews both panes.
3584 * @param pDC [in] points to the printer device context
3585 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3587 void CMergeEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo)
3589 CRect rDraw = pInfo->m_rectDraw;
3590 CSize sz = rDraw.Size();
3591 CMergeDoc *pDoc = GetDocument();
3593 SIZE szLeftTop, szRightBottom;
3594 GetPrintMargins(szLeftTop.cx, szLeftTop.cy, szRightBottom.cx, szRightBottom.cy);
3595 pDC->HIMETRICtoLP(&szLeftTop);
3596 pDC->HIMETRICtoLP(&szRightBottom);
3598 int midX = (sz.cx - szLeftTop.cx - szRightBottom.cx) / pDoc->m_nBuffers;
3601 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
3603 pInfo->m_rectDraw.left = rDraw.left + midX * pane;
3604 pInfo->m_rectDraw.right = pInfo->m_rectDraw.left + midX + szLeftTop.cx + szRightBottom.cx;
3605 CMergeEditView* pPane = pDoc->GetView(pane);
3606 pPane->CGhostTextView::OnPrint(pDC, pInfo);
3610 bool CMergeEditView::IsInitialized() const
3612 CMergeEditView * pThis = const_cast<CMergeEditView *>(this);
3613 CDiffTextBuffer * pBuffer = dynamic_cast<CDiffTextBuffer *>(pThis->LocateTextBuffer());
3614 return pBuffer->IsInitialized();
3618 * @brief returns the number of empty lines which are added for synchronizing the line in two/three panes.
3620 int CMergeEditView::GetEmptySubLines( int nLineIndex )
3622 int nBreaks[3] = {0};
3623 int nMaxBreaks = -1;
3624 CMergeDoc * pDoc = GetDocument();
3625 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3627 CMergeEditView *pView = pDoc->GetView(nPane);
3630 if (nLineIndex >= pView->GetLineCount())
3632 pView->WrapLineCached( nLineIndex, pView->GetScreenChars(), NULL, nBreaks[nPane] );
3634 nMaxBreaks = max(nMaxBreaks, nBreaks[nPane]);
3637 if (nBreaks[m_nThisPane] < nMaxBreaks)
3638 return nMaxBreaks - nBreaks[m_nThisPane];
3644 * @brief Invalidate sub line index cache from the specified index to the end of file.
3645 * @param [in] nLineIndex Index of the first line to invalidate
3647 void CMergeEditView::InvalidateSubLineIndexCache( int nLineIndex )
3649 CMergeDoc * pDoc = GetDocument();
3650 ASSERT(pDoc != NULL);
3652 // We have to invalidate sub line index cache on both panes.
3653 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3655 CMergeEditView *pView = pDoc->GetView(nPane);
3657 pView->CCrystalTextView::InvalidateSubLineIndexCache( nLineIndex );
3661 void CMergeEditView::SetWordWrapping( BOOL bWordWrap )
3663 for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3664 GetDocument()->GetView(pane)->m_bWordWrap = bWordWrap;
3665 CCrystalTextView::SetWordWrapping(bWordWrap);
3669 * @brief Swap the positions of the two panes
3671 void CMergeEditView::OnViewSwapPanes()
3673 GetDocument()->SwapFiles();
3677 * @brief Enable Swap Panes -gui.
3679 void CMergeEditView::OnUpdateViewSwapPanes(CCmdUI* pCmdUI)
3681 pCmdUI->Enable(true);
3685 * @brief Check if cursor is inside difference.
3686 * @return TRUE if cursor is inside difference.
3688 bool CMergeEditView::IsCursorInDiff() const
3690 return m_bCurrentLineIsDiff;
3694 * @brief Determine if difference is visible on screen.
3695 * @param [in] nDiff Number of diff to check.
3696 * @return TRUE if difference is visible.
3698 bool CMergeEditView::IsDiffVisible(int nDiff)
3700 CMergeDoc *pd = GetDocument();
3703 pd->m_diffList.GetDiff(nDiff, diff);
3705 return IsDiffVisible(diff);
3709 * @brief Determine if difference is visible on screen.
3710 * @param [in] diff diff to check.
3711 * @param [in] nLinesBelow Allow "minimizing" the number of visible lines.
3712 * @return TRUE if difference is visible, FALSE otherwise.
3714 bool CMergeEditView::IsDiffVisible(const DIFFRANGE& diff, int nLinesBelow /*=0*/)
3716 const int nDiffStart = GetSubLineIndex(diff.dbegin[0]);
3717 const int nDiffEnd = GetSubLineIndex(diff.dend[0]);
3718 // Diff's height is last line - first line + last line's line count
3719 const int nDiffHeight = nDiffEnd - nDiffStart + GetSubLines(diff.dend[0]) + 1;
3721 // If diff first line outside current view - context OR
3722 // if diff last line outside current view - context OR
3723 // if diff is bigger than screen
3724 if ((nDiffStart < m_nTopSubLine) ||
3725 (nDiffEnd >= m_nTopSubLine + GetScreenLines() - nLinesBelow) ||
3726 (nDiffHeight >= GetScreenLines()))
3736 /** @brief Open help from mainframe when user presses F1*/
3737 void CMergeEditView::OnHelp()
3739 GetMainFrame()->ShowHelp(MergeViewHelpLocation);
3743 * @brief Called after document is loaded.
3744 * This function is called from CMergeDoc::OpenDocs() after documents are
3745 * loaded. So this is good place to set View's options etc.
3747 void CMergeEditView::DocumentsLoaded()
3749 // Enable/disable automatic rescan (rescanning after edit)
3750 EnableRescan(GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN));
3752 // SetTextType will revert to language dependent defaults for tab
3753 SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
3754 SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3755 const bool mixedEOLs = GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3756 GetDocument()->IsMixedEOL(m_nThisPane);
3757 SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE), mixedEOLs);
3758 SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3759 SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3760 SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3762 // Enable Backspace at beginning of line
3763 SetDisableBSAtSOL(false);
3765 // Set tab type (tabs/spaces)
3766 bool bInsertTabs = (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0);
3767 SetInsertTabs(bInsertTabs);
3769 // Sometimes WinMerge doesn't update scrollbars correctly (they remain
3770 // disabled) after docs are open in screen. So lets make sure they are
3771 // really updated, even though this is unnecessary in most cases.
3772 RecalcHorzScrollBar();
3773 RecalcVertScrollBar();
3777 * @brief Set LocationView pointer.
3778 * CLocationView calls this function to set pointer to itself,
3779 * so we can call locationview to update it.
3780 * @param [in] pView Pointer to CLocationView.
3782 void CMergeEditView::SetLocationView(const CLocationView * pView /*=NULL*/)
3784 m_pLocationView = const_cast<CLocationView *>(pView);
3788 * @brief Update LocationView position.
3789 * This function updates LocationView position to given lines.
3790 * Usually we want to lines in file compare view and area in
3791 * LocationView to match. Be extra carefull to not call non-existing
3793 * @param [in] nTopLine Top line of current view.
3794 * @param [in] nBottomLine Bottom line of current view.
3796 void CMergeEditView::UpdateLocationViewPosition(int nTopLine /*=-1*/,
3797 int nBottomLine /*= -1*/)
3799 if (m_pDocument == NULL)
3802 if (m_pLocationView != NULL && IsWindow(m_pLocationView->GetSafeHwnd()))
3804 m_pLocationView->UpdateVisiblePos(nTopLine, nBottomLine);
3809 * @brief Enable/Disable view's selection margins.
3810 * Selection margins show bookmarks and word-wrap symbols, so they are pretty
3811 * useful. But it appears many users don't use/need those features and for them
3812 * selection margins are just wasted screen estate.
3814 void CMergeEditView::OnViewMargin()
3816 bool bViewMargin = GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN);
3817 GetOptionsMgr()->SaveOption(OPT_VIEW_FILEMARGIN, !bViewMargin);
3819 SetSelectionMargin(!bViewMargin);
3820 CMergeDoc *pDoc = GetDocument();
3821 pDoc->RefreshOptions();
3822 pDoc->UpdateAllViews(this);
3826 * @brief Update GUI for Enable/Disable view's selection margin.
3827 * @param [in] pCmdUI Pointer to UI item to update.
3829 void CMergeEditView::OnUpdateViewMargin(CCmdUI* pCmdUI)
3831 bool bViewMargin = GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN);
3832 pCmdUI->Enable(true);
3833 pCmdUI->SetCheck(bViewMargin);
3837 * @brief Create the "Change Scheme" sub menu.
3838 * @param [in] pCmdUI Pointer to UI item to update.
3840 void CMergeEditView::OnUpdateViewChangeScheme(CCmdUI *pCmdUI)
3842 // Delete the place holder menu.
3843 pCmdUI->m_pSubMenu->DeleteMenu(0, MF_BYPOSITION);
3845 const HMENU hSubMenu = pCmdUI->m_pSubMenu->m_hMenu;
3847 String name = theApp.LoadString(ID_COLORSCHEME_FIRST);
3848 DoAppendMenu(hSubMenu, MF_STRING, ID_COLORSCHEME_FIRST, name.c_str());
3849 DoAppendMenu(hSubMenu, MF_SEPARATOR, 0, NULL);
3851 for (int i = ID_COLORSCHEME_FIRST + 1; i <= ID_COLORSCHEME_LAST; ++i)
3853 name = theApp.LoadString(i);
3854 DoAppendMenu(hSubMenu, MF_STRING, i, name.c_str());
3857 pCmdUI->Enable(true);
3861 * @brief Change the editor's syntax highlighting scheme.
3862 * @param [in] nID Selected color scheme sub menu id.
3864 void CMergeEditView::OnChangeScheme(UINT nID)
3866 CMergeDoc *pDoc = GetDocument();
3867 ASSERT(pDoc != NULL);
3869 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3871 CMergeEditView *pView = pDoc->GetView(nPane);
3872 ASSERT(pView != NULL);
3876 pView->SetTextType(CCrystalTextView::TextType(nID - ID_COLORSCHEME_FIRST));
3877 pView->SetDisableBSAtSOL(false);
3881 pDoc->UpdateAllViews(NULL);
3885 * @brief Enable all color schemes sub menu items.
3886 * @param [in] pCmdUI Pointer to UI item to update.
3888 void CMergeEditView::OnUpdateChangeScheme(CCmdUI* pCmdUI)
3890 const bool bIsCurrentScheme = (m_CurSourceDef->type == (pCmdUI->m_nID - ID_COLORSCHEME_FIRST));
3891 pCmdUI->SetRadio(bIsCurrentScheme);
3893 bool syntaxHLEnabled = GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT);
3894 if (syntaxHLEnabled)
3895 pCmdUI->Enable(true);
3897 pCmdUI->Enable(false);
3901 * @brief Called when mouse's wheel is scrolled.
3903 BOOL CMergeEditView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
3905 if ( nFlags == MK_CONTROL )
3907 short amount = zDelta < 0 ? -1: 1;
3910 // no default CCrystalTextView
3911 return CView::OnMouseWheel(nFlags, zDelta, pt);
3914 if (nFlags == MK_SHIFT)
3916 SCROLLINFO si = {0};
3917 si.cbSize = sizeof(si);
3918 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
3920 VERIFY(GetScrollInfo(SB_HORZ, &si));
3923 si.nPos -= zDelta / 40;
3924 if (si.nPos > si.nMax) si.nPos = si.nMax;
3925 if (si.nPos < si.nMin) si.nPos = si.nMin;
3927 SetScrollInfo(SB_HORZ, &si);
3930 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, si.nPos) , NULL );
3932 // no default CCrystalTextView
3933 return CView::OnMouseWheel(nFlags, zDelta, pt);
3936 return CGhostTextView::OnMouseWheel(nFlags, zDelta, pt);
3940 * @brief Change font size (zoom) in views.
3941 * @param [in] amount Amount of change/zoom, negative number makes
3942 * font smaller, positive number bigger and 0 reset the font size.
3944 void CMergeEditView::ZoomText(short amount)
3954 const int nLogPixelsY = pDC->GetDeviceCaps(LOGPIXELSY);
3956 int nPointSize = -MulDiv(lf.lfHeight, 72, nLogPixelsY);
3960 nPointSize = -MulDiv(GetOptionsMgr()->GetInt(OPT_FONT_FILECMP_HEIGHT), 72, nLogPixelsY);
3963 nPointSize += amount;
3967 lf.lfHeight = -MulDiv(nPointSize, nLogPixelsY, 72);
3969 CMergeDoc *pDoc = GetDocument();
3970 ASSERT(pDoc != NULL);
3974 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3976 CMergeEditView *pView = pDoc->GetView(nPane);
3977 ASSERT(pView != NULL);
3989 * @brief Called when user selects View/Zoom In from menu.
3991 void CMergeEditView::OnViewZoomIn()
3997 * @brief Called when user selects View/Zoom Out from menu.
3999 void CMergeEditView::OnViewZoomOut()
4005 * @brief Called when user selects View/Zoom Normal from menu.
4007 void CMergeEditView::OnViewZoomNormal()
4013 * @brief Show the plugins list dialog.
4015 void CMergeEditView::OnPluginsList()