OSDN Git Service

Cppcheck: The scope of the variable '...' can be reduced
[winmerge-jp/winmerge-jp.git] / Src / MergeEditView.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //
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.
10 //
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.
15 //
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.
19 //
20 /////////////////////////////////////////////////////////////////////////////
21 /**
22  * @file  MergeEditView.cpp
23  *
24  * @brief Implementation of the CMergeEditView class
25  */
26
27 #include "StdAfx.h"
28 #include "MergeEditView.h"
29 #include <vector>
30 #include "BCMenu.h"
31 #include "Merge.h"
32 #include "LocationView.h"
33 #include "MergeDoc.h"
34 #include "MainFrm.h"
35 #include "OptionsMgr.h"
36 #include "OptionsDiffColors.h"
37 #include "FileTransform.h"
38 #include "Plugins.h"
39 #include "WMGotoDlg.h"
40 #include "OptionsDef.h"
41 #include "SyntaxColors.h"
42 #include "MergeEditFrm.h"
43 #include "MergeLineFlags.h"
44 #include "paths.h"
45 #include "DropHandler.h"
46 #include "DirDoc.h"
47 #include "ShellContextMenu.h"
48
49 #ifdef _DEBUG
50 #define new DEBUG_NEW
51 #endif
52
53 using std::vector;
54 using CrystalLineParser::TEXTBLOCK;
55
56 /** @brief Timer ID for delayed rescan. */
57 const UINT IDT_RESCAN = 2;
58 /** @brief Timer timeout for delayed rescan. */
59 const UINT RESCAN_TIMEOUT = 1000;
60
61 /** @brief Location for file compare specific help to open. */
62 static TCHAR MergeViewHelpLocation[] = _T("::/htmlhelp/Compare_files.html");
63
64 /////////////////////////////////////////////////////////////////////////////
65 // CMergeEditView
66
67 IMPLEMENT_DYNCREATE(CMergeEditView, CCrystalEditViewEx)
68
69 CMergeEditView::CMergeEditView()
70 : m_bCurrentLineIsDiff(false)
71 , m_nThisPane(0)
72 , m_nThisGroup(0)
73 , m_bDetailView(false)
74 , m_piMergeEditStatus(nullptr)
75 , m_bAutomaticRescan(false)
76 , fTimerWaitingForIdle(0)
77 , m_lineBegin(0)
78 , m_lineEnd(-1)
79 , m_CurrentPredifferID(0)
80 {
81         SetParser(&m_xParser);
82         
83         Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
84 }
85
86 CMergeEditView::~CMergeEditView()
87 {
88 }
89
90
91 BEGIN_MESSAGE_MAP(CMergeEditView, CCrystalEditViewEx)
92         //{{AFX_MSG_MAP(CMergeEditView)
93         ON_COMMAND(ID_CURDIFF, OnCurdiff)
94         ON_UPDATE_COMMAND_UI(ID_CURDIFF, OnUpdateCurdiff)
95         ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
96         ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
97         ON_COMMAND(ID_EDIT_CUT, OnEditCut)
98         ON_UPDATE_COMMAND_UI(ID_EDIT_CUT, OnUpdateEditCut)
99         ON_COMMAND(ID_EDIT_PASTE, OnEditPaste)
100         ON_UPDATE_COMMAND_UI(ID_EDIT_PASTE, OnUpdateEditPaste)
101         ON_COMMAND(ID_EDIT_UNDO, OnEditUndo)
102         ON_COMMAND(ID_FIRSTDIFF, OnFirstdiff)
103         ON_UPDATE_COMMAND_UI(ID_FIRSTDIFF, OnUpdateFirstdiff)
104         ON_COMMAND(ID_LASTDIFF, OnLastdiff)
105         ON_UPDATE_COMMAND_UI(ID_LASTDIFF, OnUpdateLastdiff)
106         ON_COMMAND(ID_NEXTDIFF, OnNextdiff)
107         ON_UPDATE_COMMAND_UI(ID_NEXTDIFF, OnUpdateNextdiff)
108         ON_COMMAND(ID_PREVDIFF, OnPrevdiff)
109         ON_UPDATE_COMMAND_UI(ID_PREVDIFF, OnUpdatePrevdiff)
110         ON_COMMAND(ID_NEXTCONFLICT, OnNextConflict)
111         ON_UPDATE_COMMAND_UI(ID_NEXTCONFLICT, OnUpdateNextConflict)
112         ON_COMMAND(ID_PREVCONFLICT, OnPrevConflict)
113         ON_UPDATE_COMMAND_UI(ID_PREVCONFLICT, OnUpdatePrevConflict)
114         ON_COMMAND(ID_NEXTDIFFLM, OnNextdiffLM)
115         ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLM, OnUpdateNextdiffLM)
116         ON_COMMAND(ID_PREVDIFFLM, OnPrevdiffLM)
117         ON_UPDATE_COMMAND_UI(ID_PREVDIFFLM, OnUpdatePrevdiffLM)
118         ON_COMMAND(ID_NEXTDIFFLR, OnNextdiffLR)
119         ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLR, OnUpdateNextdiffLR)
120         ON_COMMAND(ID_PREVDIFFLR, OnPrevdiffLR)
121         ON_UPDATE_COMMAND_UI(ID_PREVDIFFLR, OnUpdatePrevdiffLR)
122         ON_COMMAND(ID_NEXTDIFFMR, OnNextdiffMR)
123         ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMR, OnUpdateNextdiffMR)
124         ON_COMMAND(ID_PREVDIFFMR, OnPrevdiffMR)
125         ON_UPDATE_COMMAND_UI(ID_PREVDIFFMR, OnUpdatePrevdiffMR)
126         ON_COMMAND(ID_NEXTDIFFLO, OnNextdiffLO)
127         ON_UPDATE_COMMAND_UI(ID_NEXTDIFFLO, OnUpdateNextdiffLO)
128         ON_COMMAND(ID_PREVDIFFLO, OnPrevdiffLO)
129         ON_UPDATE_COMMAND_UI(ID_PREVDIFFLO, OnUpdatePrevdiffLO)
130         ON_COMMAND(ID_NEXTDIFFMO, OnNextdiffMO)
131         ON_UPDATE_COMMAND_UI(ID_NEXTDIFFMO, OnUpdateNextdiffMO)
132         ON_COMMAND(ID_PREVDIFFMO, OnPrevdiffMO)
133         ON_UPDATE_COMMAND_UI(ID_PREVDIFFMO, OnUpdatePrevdiffMO)
134         ON_COMMAND(ID_NEXTDIFFRO, OnNextdiffRO)
135         ON_UPDATE_COMMAND_UI(ID_NEXTDIFFRO, OnUpdateNextdiffRO)
136         ON_COMMAND(ID_PREVDIFFRO, OnPrevdiffRO)
137         ON_UPDATE_COMMAND_UI(ID_PREVDIFFRO, OnUpdatePrevdiffRO)
138         ON_WM_LBUTTONDBLCLK()
139         ON_WM_LBUTTONUP()
140         ON_COMMAND(ID_ALL_LEFT, OnAllLeft)
141         ON_UPDATE_COMMAND_UI(ID_ALL_LEFT, OnUpdateAllLeft)
142         ON_COMMAND(ID_ALL_RIGHT, OnAllRight)
143         ON_UPDATE_COMMAND_UI(ID_ALL_RIGHT, OnUpdateAllRight)
144         ON_COMMAND(ID_AUTO_MERGE, OnAutoMerge)
145         ON_UPDATE_COMMAND_UI(ID_AUTO_MERGE, OnUpdateAutoMerge)
146         ON_COMMAND(ID_L2R, OnL2r)
147         ON_UPDATE_COMMAND_UI(ID_L2R, OnUpdateL2r)
148         ON_COMMAND(ID_R2L, OnR2l)
149         ON_UPDATE_COMMAND_UI(ID_R2L, OnUpdateR2l)
150         ON_COMMAND(ID_COPY_FROM_LEFT, OnCopyFromLeft)
151         ON_UPDATE_COMMAND_UI(ID_COPY_FROM_LEFT, OnUpdateCopyFromLeft)
152         ON_COMMAND(ID_COPY_FROM_RIGHT, OnCopyFromRight)
153         ON_UPDATE_COMMAND_UI(ID_COPY_FROM_RIGHT, OnUpdateCopyFromRight)
154         ON_COMMAND(ID_ADD_SYNCPOINT, OnAddSyncPoint)
155         ON_COMMAND(ID_CLEAR_SYNCPOINTS, OnClearSyncPoints)
156         ON_UPDATE_COMMAND_UI(ID_CLEAR_SYNCPOINTS, OnUpdateClearSyncPoints)
157         ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, OnUpdateEditUndo)
158         ON_COMMAND(ID_EDIT_REDO, OnEditRedo)
159         ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, OnUpdateEditRedo)
160         ON_WM_TIMER()
161         ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_LEFT, OnUpdateFileSaveLeft)
162         ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_MIDDLE, OnUpdateFileSaveMiddle)
163         ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_RIGHT, OnUpdateFileSaveRight)
164         ON_COMMAND(ID_REFRESH, OnRefresh)
165         ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
166         ON_COMMAND(ID_SELECTLINEDIFF, OnSelectLineDiff<false>)
167         ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
168         ON_COMMAND(ID_SELECTPREVLINEDIFF, OnSelectLineDiff<true>)
169         ON_UPDATE_COMMAND_UI(ID_SELECTPREVLINEDIFF, OnUpdateSelectLineDiff)
170         ON_WM_CONTEXTMENU()
171         ON_UPDATE_COMMAND_UI(ID_EDIT_REPLACE, OnUpdateEditReplace)
172         ON_COMMAND(ID_FILE_LEFT_READONLY, OnLeftReadOnly)
173         ON_UPDATE_COMMAND_UI(ID_FILE_LEFT_READONLY, OnUpdateLeftReadOnly)
174         ON_COMMAND(ID_FILE_MIDDLE_READONLY, OnMiddleReadOnly)
175         ON_UPDATE_COMMAND_UI(ID_FILE_MIDDLE_READONLY, OnUpdateMiddleReadOnly)
176         ON_COMMAND(ID_FILE_RIGHT_READONLY, OnRightReadOnly)
177         ON_UPDATE_COMMAND_UI(ID_FILE_RIGHT_READONLY, OnUpdateRightReadOnly)
178         ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_RO, OnUpdateStatusRO)
179         ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_RO, OnUpdateStatusRO)
180         ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_RO, OnUpdateStatusRO)
181         ON_COMMAND_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnConvertEolTo)
182         ON_UPDATE_COMMAND_UI_RANGE(ID_EOL_TO_DOS, ID_EOL_TO_MAC, OnUpdateConvertEolTo)
183         ON_UPDATE_COMMAND_UI(ID_STATUS_PANE0FILE_EOL, OnUpdateStatusEOL)
184         ON_UPDATE_COMMAND_UI(ID_STATUS_PANE1FILE_EOL, OnUpdateStatusEOL)
185         ON_UPDATE_COMMAND_UI(ID_STATUS_PANE2FILE_EOL, OnUpdateStatusEOL)
186         ON_COMMAND(ID_L2RNEXT, OnL2RNext)
187         ON_UPDATE_COMMAND_UI(ID_L2RNEXT, OnUpdateL2RNext)
188         ON_COMMAND(ID_R2LNEXT, OnR2LNext)
189         ON_UPDATE_COMMAND_UI(ID_R2LNEXT, OnUpdateR2LNext)
190         ON_COMMAND(ID_WINDOW_CHANGE_PANE, OnChangePane)
191         ON_COMMAND(ID_NEXT_PANE, OnChangePane)
192         ON_COMMAND(ID_EDIT_WMGOTO, OnWMGoto)
193         ON_COMMAND(ID_FILE_SHELLMENU, OnShellMenu)
194         ON_UPDATE_COMMAND_UI(ID_FILE_SHELLMENU, OnUpdateShellMenu)
195         ON_COMMAND_RANGE(ID_SCRIPT_FIRST, ID_SCRIPT_LAST, OnScripts)
196         ON_COMMAND(ID_NO_PREDIFFER, OnNoPrediffer)
197         ON_UPDATE_COMMAND_UI(ID_NO_PREDIFFER, OnUpdateNoPrediffer)
198         ON_COMMAND_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnPrediffer)
199         ON_UPDATE_COMMAND_UI_RANGE(ID_PREDIFFERS_FIRST, ID_PREDIFFERS_LAST, OnUpdatePrediffer)
200         ON_WM_VSCROLL ()
201         ON_WM_HSCROLL ()
202         ON_COMMAND(ID_EDIT_COPY_LINENUMBERS, OnEditCopyLineNumbers)
203         ON_UPDATE_COMMAND_UI(ID_EDIT_COPY_LINENUMBERS, OnUpdateEditCopyLinenumbers)
204         ON_COMMAND(ID_VIEW_LINEDIFFS, OnViewLineDiffs)
205         ON_UPDATE_COMMAND_UI(ID_VIEW_LINEDIFFS, OnUpdateViewLineDiffs)
206         ON_COMMAND(ID_VIEW_WORDWRAP, OnViewWordWrap)
207         ON_UPDATE_COMMAND_UI(ID_VIEW_WORDWRAP, OnUpdateViewWordWrap)
208         ON_COMMAND(ID_VIEW_LINENUMBERS, OnViewLineNumbers)
209         ON_UPDATE_COMMAND_UI(ID_VIEW_LINENUMBERS, OnUpdateViewLineNumbers)
210         ON_COMMAND(ID_VIEW_WHITESPACE, OnViewWhitespace)
211         ON_UPDATE_COMMAND_UI(ID_VIEW_WHITESPACE, OnUpdateViewWhitespace)
212         ON_COMMAND(ID_FILE_OPEN_REGISTERED, OnOpenFile)
213         ON_COMMAND(ID_FILE_OPEN_WITHEDITOR, OnOpenFileWithEditor)
214         ON_COMMAND(ID_FILE_OPEN_WITH, OnOpenFileWith)
215         ON_COMMAND(ID_VIEW_SWAPPANES, OnViewSwapPanes)
216         ON_UPDATE_COMMAND_UI(ID_NO_EDIT_SCRIPTS, OnUpdateNoEditScripts)
217         ON_WM_SIZE()
218         ON_WM_MOVE()
219         ON_COMMAND(ID_HELP, OnHelp)
220         ON_COMMAND(ID_VIEW_FILEMARGIN, OnViewMargin)
221         ON_UPDATE_COMMAND_UI(ID_VIEW_FILEMARGIN, OnUpdateViewMargin)
222         ON_UPDATE_COMMAND_UI(ID_VIEW_CHANGESCHEME, OnUpdateViewChangeScheme)
223         ON_COMMAND_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnChangeScheme)
224         ON_UPDATE_COMMAND_UI_RANGE(ID_COLORSCHEME_FIRST, ID_COLORSCHEME_LAST, OnUpdateChangeScheme)
225         ON_WM_MOUSEWHEEL()
226         ON_COMMAND(ID_VIEW_ZOOMIN, OnViewZoomIn)
227         ON_COMMAND(ID_VIEW_ZOOMOUT, OnViewZoomOut)
228         ON_COMMAND(ID_VIEW_ZOOMNORMAL, OnViewZoomNormal)
229         ON_COMMAND(ID_WINDOW_SPLIT, OnWindowSplit)
230         ON_UPDATE_COMMAND_UI(ID_WINDOW_SPLIT, OnUpdateWindowSplit)
231         //}}AFX_MSG_MAP
232 END_MESSAGE_MAP()
233
234
235 /////////////////////////////////////////////////////////////////////////////
236 // CMergeEditView diagnostics
237
238 #ifdef _DEBUG
239 CMergeDoc* CMergeEditView::GetDocument() // non-debug version is inline
240 {
241         ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMergeDoc)));
242         return (CMergeDoc*)m_pDocument;
243 }
244
245 #endif //_DEBUG
246
247 /////////////////////////////////////////////////////////////////////////////
248 // CMergeEditView message handlers
249
250 /**
251  * @brief Return text buffer for file in view
252  */
253 CCrystalTextBuffer *CMergeEditView::LocateTextBuffer()
254 {
255         return GetDocument()->m_ptBuf[m_nThisPane].get();
256 }
257
258 /**
259  * @brief Update any resources necessary after a GUI language change
260  */
261 void CMergeEditView::UpdateResources()
262 {
263 }
264
265 CMergeEditView *CMergeEditView::GetGroupView(int nBuffer) const
266 {
267         return GetDocument()->GetView(m_nThisGroup, nBuffer);
268 }
269
270 void CMergeEditView::PrimeListWithFile()
271 {
272         // Set the tab size now, just in case the options change...
273         // We don't update it at the end of OnOptions,
274         // we can update it safely now
275         SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
276 }
277 /**
278  * @brief Return text from line given
279  */
280 CString CMergeEditView::GetLineText(int idx)
281 {
282         return GetLineChars(idx);
283 }
284
285 /**
286  * @brief Return text from selection
287  */
288 CString CMergeEditView::GetSelectedText()
289 {
290         CPoint ptStart, ptEnd;
291         CString strText;
292         GetSelection(ptStart, ptEnd);
293         if (ptStart != ptEnd)
294                 GetTextWithoutEmptys(ptStart.y, ptStart.x, ptEnd.y, ptEnd.x, strText);
295         return strText;
296 }
297
298 /**
299  * @brief Get diffs inside selection.
300  * @param [out] firstDiff First diff inside selection
301  * @param [out] lastDiff Last diff inside selection
302  * @param [out] firstWordDiff First word level diff inside selection
303  * @param [out] lastWordDiff Last word level diff inside selection
304  * @note -1 is returned in parameters if diffs cannot be determined
305  * @todo This shouldn't be called when there is no diffs, so replace
306  * first 'if' with ASSERT()?
307  */
308 void CMergeEditView::GetFullySelectedDiffs(int & firstDiff, int & lastDiff, int & firstWordDiff, int & lastWordDiff, const CPoint *pptStart, const CPoint *pptEnd)
309 {
310         firstDiff = -1;
311         lastDiff = -1;
312         firstWordDiff = -1;
313         lastWordDiff = -1;
314
315         CMergeDoc *pd = GetDocument();
316         const int nDiffs = pd->m_diffList.GetSignificantDiffs();
317         if (nDiffs == 0)
318                 return;
319
320         int firstLine, lastLine;
321         CPoint ptStart, ptEnd;
322         GetSelection(ptStart, ptEnd);
323         if (pptStart != nullptr)
324                 ptStart = *pptStart;
325         if (pptEnd != nullptr)
326                 ptEnd = *pptEnd;
327         firstLine = ptStart.y;
328         lastLine = ptEnd.y;
329
330         firstDiff = pd->m_diffList.LineToDiff(firstLine);
331         bool firstLineIsNotInDiff = firstDiff == -1;
332         if (firstDiff == -1)
333         {
334                 firstDiff = pd->m_diffList.NextSignificantDiffFromLine(firstLine);
335                 if (firstDiff == -1)
336                         return;
337                 firstWordDiff = 0;
338         }
339         lastDiff = pd->m_diffList.LineToDiff(lastLine);
340         bool lastLineIsNotInDiff = lastDiff == -1;      
341         if (lastDiff == -1)
342                 lastDiff = pd->m_diffList.PrevSignificantDiffFromLine(lastLine);
343         if (lastDiff < firstDiff)
344         {
345                 firstDiff = -1;
346                 firstWordDiff = -1;
347                 return;
348         }
349
350         if (firstDiff != -1 && lastDiff != -1)
351         {
352                 DIFFRANGE di;
353                 
354                 if (ptStart != ptEnd)
355                 {
356                         VERIFY(pd->m_diffList.GetDiff(firstDiff, di));
357                         if (lastLineIsNotInDiff && (firstLineIsNotInDiff || (di.dbegin == firstLine && ptStart.x == 0)))
358                         {
359                                 firstWordDiff = -1;
360                                 return;
361                         }
362
363                         if (firstWordDiff == -1)
364                         {
365                                 vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(firstDiff);
366                                 for (size_t i = 0; i < worddiffs.size(); ++i)
367                                 {
368                                         if (worddiffs[i].endline[m_nThisPane] > firstLine ||
369                                                 (firstLine == worddiffs[i].endline[m_nThisPane] && worddiffs[i].end[m_nThisPane] - 1 >= ptStart.x))
370                                         {
371                                                 firstWordDiff = static_cast<int>(i);
372                                                 break;
373                                         }
374                                 }
375
376                                 if (firstLine >= di.dbegin && firstWordDiff == -1)
377                                 {
378                                         ++firstDiff;
379                                         firstWordDiff = 0;
380                                 }
381                         }
382
383                         VERIFY(pd->m_diffList.GetDiff(lastDiff, di));
384                         vector<WordDiff> worddiffs = pd->GetWordDiffArrayInDiffBlock(lastDiff);
385                         for (size_t i = worddiffs.size() - 1; i != (size_t)-1; --i)
386                         {
387                                 if (worddiffs[i].beginline[m_nThisPane] < lastLine ||
388                                     (lastLine == worddiffs[i].beginline[m_nThisPane] && worddiffs[i].begin[m_nThisPane] + 1 <= ptEnd.x))
389                                 {
390                                         lastWordDiff = static_cast<int>(i);
391                                         break;
392                                 }
393                         }
394
395                         if (lastLine <= di.dend && lastWordDiff == -1)
396                                 --lastDiff;
397
398                         if (firstDiff == lastDiff && (lastWordDiff != -1 && lastWordDiff < firstWordDiff))
399                         {
400                                 firstDiff = -1;
401                                 lastDiff = -1;
402                                 firstWordDiff = -1;
403                                 lastWordDiff = -1;
404                         }
405                         else if (lastDiff < firstDiff || (firstDiff == lastDiff && firstWordDiff == -1 && lastWordDiff == -1))
406                         {
407                                 firstDiff = -1;
408                                 lastDiff = -1;
409                                 firstWordDiff = -1;
410                                 lastWordDiff = -1;
411                         }
412                 }
413                 else
414                 {
415                         firstDiff = -1;
416                         lastDiff = -1;
417                         firstWordDiff = -1;
418                         lastWordDiff = -1;
419                 }
420         }
421
422         ASSERT(firstDiff == -1 ? (lastDiff  == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
423         ASSERT(lastDiff  == -1 ? (firstDiff == -1 && firstWordDiff == -1 && lastWordDiff == -1) : true);
424         ASSERT(firstDiff != -1 ? firstWordDiff != -1 : true);
425 }
426
427 std::map<int, std::vector<int>> CMergeEditView::GetColumnSelectedWordDiffIndice()
428 {
429         CMergeDoc *pDoc = GetDocument();
430         std::map<int, std::vector<int>> ret;
431         std::map<int, std::vector<int> *> list;
432         CPoint ptStart, ptEnd;
433         GetSelection(ptStart, ptEnd);
434         for (int nLine = ptStart.y; nLine <= ptEnd.y; ++nLine)
435         {
436                 if (pDoc->m_diffList.LineToDiff(nLine) != -1)
437                 {
438                         int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
439                         int nLeft, nRight;
440                         GetColumnSelection(nLine, nLeft, nRight);
441                         CPoint ptStart2, ptEnd2;
442                         ptStart2.x = nLeft;
443                         ptEnd2.x = nRight;
444                         ptStart2.y = ptEnd2.y = nLine;
445                         GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff, &ptStart2, &ptEnd2);
446                         if (firstDiff != -1 && lastDiff != -1)
447                         {
448                                 std::vector<int> *pWordDiffs;
449                                 if (list.find(firstDiff) == list.end())
450                                         list.insert(std::pair<int, std::vector<int> *>(firstDiff, new std::vector<int>()));
451                                 pWordDiffs = list[firstDiff];
452                                 for (int i = firstWordDiff; i <= lastWordDiff; ++i)
453                                 {
454                                         if (pWordDiffs->empty() || i != (*pWordDiffs)[pWordDiffs->size() - 1])
455                                                 pWordDiffs->push_back(i);
456                                 }
457                         }
458                 }
459         }
460         for (auto& it : list)
461                 ret.insert(std::pair<int, std::vector<int>>(it.first, *it.second));
462         return ret;
463 }
464
465 void CMergeEditView::OnInitialUpdate()
466 {
467         PushCursors();
468         CCrystalEditViewEx::OnInitialUpdate();
469         PopCursors();
470         SetFont(dynamic_cast<CMainFrame*>(AfxGetMainWnd())->m_lfDiff);
471         SetAlternateDropTarget(new DropHandler(std::bind(&CMergeEditView::OnDropFiles, this, std::placeholders::_1)));
472
473         m_lineBegin = 0;
474         m_lineEnd = -1;
475 }
476
477 void CMergeEditView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
478 {
479         CCrystalEditViewEx::OnActivateView(bActivate, pActivateView, pDeactiveView);
480
481         CMergeDoc* pDoc = GetDocument();
482         pDoc->UpdateHeaderActivity(m_nThisPane, !!bActivate);
483 }
484
485 std::vector<TEXTBLOCK> CMergeEditView::GetAdditionalTextBlocks (int nLineIndex)
486 {
487         static const std::vector<TEXTBLOCK> emptyBlocks;
488         if (m_bDetailView)
489         {
490                 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
491                         return emptyBlocks;
492         }
493
494         DWORD dwLineFlags = GetLineFlags(nLineIndex);
495         if ((dwLineFlags & LF_SNP) == LF_SNP || (dwLineFlags & LF_DIFF) != LF_DIFF || (dwLineFlags & LF_MOVED) == LF_MOVED)
496                 return emptyBlocks;
497
498         if (!GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT))
499                 return emptyBlocks;
500
501         CMergeDoc *pDoc = GetDocument();
502         if (pDoc->IsEditedAfterRescan(m_nThisPane))
503                 return emptyBlocks;
504         
505         int nDiff = pDoc->m_diffList.LineToDiff(nLineIndex);
506         if (nDiff == -1)
507                 return emptyBlocks;
508
509         DIFFRANGE cd;
510         pDoc->m_diffList.GetDiff(nDiff, cd);
511         int unemptyLineCount = 0;
512         for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
513         {
514                 if (cd.begin[nPane] != cd.end[nPane] + 1)
515                         unemptyLineCount++;
516         }
517         if (unemptyLineCount < 2)
518                 return emptyBlocks;
519
520         vector<WordDiff> worddiffs = pDoc->GetWordDiffArray(nLineIndex);
521         size_t nWordDiffs = worddiffs.size();
522
523         bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
524
525         std::vector<TEXTBLOCK> blocks(nWordDiffs * 2 + 1);
526         blocks[0].m_nCharPos = 0;
527         blocks[0].m_nColorIndex = COLORINDEX_NONE;
528         blocks[0].m_nBgColorIndex = COLORINDEX_NONE;
529         size_t i, j;
530         for (i = 0, j = 1; i < nWordDiffs; i++)
531         {
532                 if (worddiffs[i].beginline[m_nThisPane] > nLineIndex || worddiffs[i].endline[m_nThisPane] < nLineIndex )
533                         continue;
534                 if (pDoc->m_nBuffers > 2)
535                 {
536                         if (m_nThisPane == 0 && worddiffs[i].op == OP_3RDONLY)
537                                 continue;
538                         else if (m_nThisPane == 2 && worddiffs[i].op == OP_1STONLY)
539                                 continue;
540                 }
541                 int begin[3], end[3];
542                 bool deleted = false;
543                 for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
544                 {
545                         begin[pane] = (worddiffs[i].beginline[pane] < nLineIndex) ? 0 : worddiffs[i].begin[pane];
546                         end[pane]   = (worddiffs[i].endline[pane]   > nLineIndex) ? GetGroupView(pane)->GetLineLength(nLineIndex) : worddiffs[i].end[pane];
547                         if (worddiffs[i].beginline[pane] == worddiffs[i].endline[pane] &&
548                                 worddiffs[i].begin[pane] == worddiffs[i].end[pane])
549                                 deleted = true;
550                 }
551                 blocks[j].m_nCharPos = begin[m_nThisPane];
552                 if (lineInCurrentDiff)
553                 {
554                         if (m_cachedColors.clrSelDiffText != CLR_NONE)
555                                 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT1 | COLORINDEX_APPLYFORCE;
556                         else
557                                 blocks[j].m_nColorIndex = COLORINDEX_NONE;
558                         blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE | 
559                                 (deleted ? COLORINDEX_HIGHLIGHTBKGND4 : COLORINDEX_HIGHLIGHTBKGND1);
560                 }
561                 else
562                 {
563                         if (m_cachedColors.clrDiffText != CLR_NONE)
564                                 blocks[j].m_nColorIndex = COLORINDEX_HIGHLIGHTTEXT2 | COLORINDEX_APPLYFORCE;
565                         else
566                                 blocks[j].m_nColorIndex = COLORINDEX_NONE;
567                         blocks[j].m_nBgColorIndex = COLORINDEX_APPLYFORCE |
568                                 (deleted ? COLORINDEX_HIGHLIGHTBKGND3 : COLORINDEX_HIGHLIGHTBKGND2);
569                 }
570                 j++;
571                 blocks[j].m_nCharPos = end[m_nThisPane];
572                 blocks[j].m_nColorIndex = COLORINDEX_NONE;
573                 blocks[j].m_nBgColorIndex = COLORINDEX_NONE;
574                 j++;
575         }
576
577         blocks.resize(j);
578
579         return blocks;
580 }
581
582 COLORREF CMergeEditView::GetColor(int nColorIndex)
583 {
584         switch (nColorIndex & ~COLORINDEX_APPLYFORCE)
585         {
586         case COLORINDEX_HIGHLIGHTBKGND1:
587                 return m_cachedColors.clrSelWordDiff;
588         case COLORINDEX_HIGHLIGHTTEXT1:
589                 return m_cachedColors.clrSelWordDiffText;
590         case COLORINDEX_HIGHLIGHTBKGND2:
591                 return m_cachedColors.clrWordDiff;
592         case COLORINDEX_HIGHLIGHTTEXT2:
593                 return m_cachedColors.clrWordDiffText;
594         case COLORINDEX_HIGHLIGHTBKGND3:
595                 return m_cachedColors.clrWordDiffDeleted;
596         case COLORINDEX_HIGHLIGHTBKGND4:
597                 return m_cachedColors.clrSelWordDiffDeleted;
598
599         default:
600                 return CCrystalTextView::GetColor(nColorIndex);
601         }
602 }
603
604 /**
605  * @brief Determine text and background color for line
606  * @param [in] nLineIndex Index of line in view (NOT line in file)
607  * @param [out] crBkgnd Backround color for line
608  * @param [out] crText Text color for line
609  */
610 void CMergeEditView::GetLineColors(int nLineIndex, COLORREF & crBkgnd,
611                                 COLORREF & crText, bool & bDrawWhitespace)
612 {
613         DWORD ignoreFlags = 0;
614         GetLineColors2(nLineIndex, ignoreFlags, crBkgnd, crText, bDrawWhitespace);
615 }
616
617 /**
618  * @brief Determine text and background color for line
619  * @param [in] nLineIndex Index of line in view (NOT line in file)
620  * @param [in] ignoreFlags Flags that caller wishes ignored
621  * @param [out] crBkgnd Backround color for line
622  * @param [out] crText Text color for line
623  *
624  * This version allows caller to suppress particular flags
625  */
626 void CMergeEditView::GetLineColors2(int nLineIndex, DWORD ignoreFlags, COLORREF & crBkgnd,
627                                 COLORREF & crText, bool & bDrawWhitespace)
628 {
629         if (GetLineCount() <= nLineIndex)
630                 return;
631
632         DWORD dwLineFlags = GetLineFlags(nLineIndex);
633
634         if (dwLineFlags & ignoreFlags)
635                 dwLineFlags &= (~ignoreFlags);
636
637         if (m_bDetailView)
638         {
639                 // Line with WinMerge flag, 
640                 // Lines with only the LF_DIFF/LF_TRIVIAL flags are not colored with Winmerge colors
641                 if (dwLineFlags & (LF_WINMERGE_FLAGS & ~LF_DIFF & ~LF_TRIVIAL & ~LF_MOVED & ~LF_SNP))
642                 {
643                         crText = m_cachedColors.clrDiffText;
644                         bDrawWhitespace = true;
645
646                         if (dwLineFlags & LF_GHOST)
647                         {
648                                 crBkgnd = m_cachedColors.clrDiffDeleted;
649                         }
650                 }
651                 else
652                 {
653                         // If no syntax hilighting
654                         if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
655                         {
656                                 crBkgnd = GetColor (COLORINDEX_BKGND);
657                                 crText = GetColor (COLORINDEX_NORMALTEXT);
658                                 bDrawWhitespace = false;
659                         }
660                         else
661                                 // Line not inside diff, get colors from CrystalEditor
662                                 CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
663                                         crText, bDrawWhitespace);
664                 }
665                 if (nLineIndex < m_lineBegin || nLineIndex > m_lineEnd)
666                 {
667                         crBkgnd = GetColor (COLORINDEX_WHITESPACE);
668                         crText = GetColor (COLORINDEX_WHITESPACE);
669                         bDrawWhitespace = false;
670                 }
671                 return;
672         }
673
674         // Line inside diff
675         if (dwLineFlags & LF_WINMERGE_FLAGS)
676         {
677                 crText = m_cachedColors.clrDiffText;
678                 bDrawWhitespace = true;
679                 bool lineInCurrentDiff = IsLineInCurrentDiff(nLineIndex);
680
681                 if (dwLineFlags & LF_SNP)
682                 {
683                         if (lineInCurrentDiff)
684                         {
685                                 if (dwLineFlags & LF_GHOST)
686                                         crBkgnd = m_cachedColors.clrSelSNPDeleted;
687                                 else
688                                         crBkgnd = m_cachedColors.clrSelSNP;
689                                 crText = m_cachedColors.clrSelSNPText;
690                         }
691                         else
692                         {
693                                 if (dwLineFlags & LF_GHOST)
694                                         crBkgnd = m_cachedColors.clrSNPDeleted;
695                                 else
696                                         crBkgnd = m_cachedColors.clrSNP;
697                                 crText = m_cachedColors.clrSNPText;
698                         }
699                         return;
700                 }
701                 else if (dwLineFlags & LF_DIFF)
702                 {
703                         if (lineInCurrentDiff)
704                         {
705                                 if (dwLineFlags & LF_MOVED)
706                                 {
707                                         if (dwLineFlags & LF_GHOST)
708                                                 crBkgnd = m_cachedColors.clrSelMovedDeleted;
709                                         else
710                                                 crBkgnd = m_cachedColors.clrSelMoved;
711                                         crText = m_cachedColors.clrSelMovedText;
712                                 }
713                                 else
714                                 {
715                                         crBkgnd = m_cachedColors.clrSelDiff;
716                                         crText = m_cachedColors.clrSelDiffText;
717                                 }
718                         
719                         }
720                         else
721                         {
722                                 if (dwLineFlags & LF_MOVED)
723                                 {
724                                         if (dwLineFlags & LF_GHOST)
725                                                 crBkgnd = m_cachedColors.clrMovedDeleted;
726                                         else
727                                                 crBkgnd = m_cachedColors.clrMoved;
728                                         crText = m_cachedColors.clrMovedText;
729                                 }
730                                 else
731                                 {
732                                         crBkgnd = m_cachedColors.clrDiff;
733                                         crText = m_cachedColors.clrDiffText;
734                                 }
735                         }
736                         return;
737                 }
738                 else if (dwLineFlags & LF_TRIVIAL)
739                 {
740                         // trivial diff can not be selected
741                         if (dwLineFlags & LF_GHOST)
742                                 // ghost lines in trivial diff has their own color
743                                 crBkgnd = m_cachedColors.clrTrivialDeleted;
744                         else
745                                 crBkgnd = m_cachedColors.clrTrivial;
746                         crText = m_cachedColors.clrTrivialText;
747                         return;
748                 }
749                 else if (dwLineFlags & LF_GHOST)
750                 {
751                         if (lineInCurrentDiff)
752                                 crBkgnd = m_cachedColors.clrSelDiffDeleted;
753                         else
754                                 crBkgnd = m_cachedColors.clrDiffDeleted;
755                         return;
756                 }
757         }
758         else
759         {
760                 // Line not inside diff,
761                 if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
762                 {
763                         // If no syntax hilighting, get windows default colors
764                         crBkgnd = GetColor (COLORINDEX_BKGND);
765                         crText = GetColor (COLORINDEX_NORMALTEXT);
766                         bDrawWhitespace = false;
767                 }
768                 else
769                         // Syntax highlighting, get colors from CrystalEditor
770                         CCrystalEditViewEx::GetLineColors(nLineIndex, crBkgnd,
771                                 crText, bDrawWhitespace);
772         }
773 }
774
775 /**
776  * @brief Sync other pane position
777  */
778 void CMergeEditView::UpdateSiblingScrollPos (bool bHorz)
779 {
780         CSplitterWnd *pSplitterWnd = GetParentSplitter (this, false);
781         if (pSplitterWnd != nullptr)
782         {
783                 //  See CSplitterWnd::IdFromRowCol() implementation for details
784                 int nCurrentRow = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) / 16;
785                 int nCurrentCol = (GetDlgCtrlID () - AFX_IDW_PANE_FIRST) % 16;
786                 ASSERT (nCurrentRow >= 0 && nCurrentRow < pSplitterWnd->GetRowCount ());
787                 ASSERT (nCurrentCol >= 0 && nCurrentCol < pSplitterWnd->GetColumnCount ());
788
789                 // limit the TopLine : must be smaller than GetLineCount for all the panels
790                 int newTopSubLine = m_nTopSubLine;
791                 int nRows = pSplitterWnd->GetRowCount ();
792                 int nCols = pSplitterWnd->GetColumnCount ();
793                 int nRow=0;
794 //              for (nRow = 0; nRow < nRows; nRow++)
795 //              {
796 //                      for (int nCol = 0; nCol < nCols; nCol++)
797 //                      {
798 //                              CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
799 //                              if (pSiblingView != nullptr)
800 //                                      if (pSiblingView->GetSubLineCount() <= newTopSubLine)
801 //                                              newTopSubLine = pSiblingView->GetSubLineCount()-1;
802 //                      }
803 //              }
804                 if (m_nTopSubLine != newTopSubLine)
805                         ScrollToSubLine(newTopSubLine);
806
807                 for (nRow = 0; nRow < nRows; nRow++)
808                 {
809                         for (int nCol = 0; nCol < nCols; nCol++)
810                         {
811                                 if (!(nRow == nCurrentRow && nCol == nCurrentCol))  //  We don't need to update ourselves
812                                 {
813                                         CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
814                                         if (pSiblingView != nullptr && pSiblingView->m_nThisGroup == m_nThisGroup)
815                                                 pSiblingView->OnUpdateSibling (this, bHorz);
816                                 }
817                         }
818                 }
819         }
820 }
821
822 /**
823  * @brief Update other panes
824  */
825 void CMergeEditView::OnUpdateSibling (CCrystalTextView * pUpdateSource, bool bHorz)
826 {
827         if (pUpdateSource != this)
828         {
829                 ASSERT (pUpdateSource != nullptr);
830                 ASSERT_KINDOF (CCrystalTextView, pUpdateSource);
831                 CMergeEditView *pSrcView = static_cast<CMergeEditView*>(pUpdateSource);
832                 if (!bHorz)  // changed this so bHorz works right
833                 {
834                         ASSERT (pSrcView->m_nTopSubLine >= 0);
835
836                         // This ASSERT is wrong: panes have different files and
837                         // different linecounts
838                         // ASSERT (pSrcView->m_nTopLine < GetLineCount ());
839                         if (pSrcView->m_nTopSubLine != m_nTopSubLine)
840                         {
841                                 ScrollToSubLine (pSrcView->m_nTopSubLine, true, false);
842                                 UpdateCaret ();
843                                 RecalcVertScrollBar(true);
844                                 RecalcHorzScrollBar();
845                         }
846                 }
847                 else
848                 {
849                         ASSERT (pSrcView->m_nOffsetChar >= 0);
850
851                         // This ASSERT is wrong: panes have different files and
852                         // different linelengths
853                         // ASSERT (pSrcView->m_nOffsetChar < GetMaxLineLength ());
854                         if (pSrcView->m_nOffsetChar != m_nOffsetChar)
855                         {
856                                 ScrollToChar (pSrcView->m_nOffsetChar, true, false);
857                                 UpdateCaret ();
858                                 RecalcHorzScrollBar(true);
859                                 RecalcHorzScrollBar();
860                         }
861                 }
862         }
863 }
864
865 void CMergeEditView::OnDisplayDiff(int nDiff /*=0*/)
866 {
867         int newlineBegin, newlineEnd;
868         CMergeDoc *pd = GetDocument();
869         if (nDiff < 0 || nDiff >= pd->m_diffList.GetSize())
870         {
871                 newlineBegin = 0;
872                 newlineEnd = -1;
873         }
874         else
875         {
876                 DIFFRANGE curDiff;
877                 VERIFY(pd->m_diffList.GetDiff(nDiff, curDiff));
878
879                 newlineBegin = curDiff.dbegin;
880                 ASSERT (newlineBegin >= 0);
881                 newlineEnd = curDiff.dend;
882         }
883
884         if (newlineBegin == m_lineBegin && newlineEnd == m_lineEnd)
885                 return;
886         m_lineBegin = newlineBegin;
887         m_lineEnd = newlineEnd;
888
889         // scroll to the first line of the diff
890         ScrollToLine(m_lineBegin);
891
892         // tell the others views about this diff (no need to call UpdateSiblingScrollPos)
893         CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
894
895         // pSplitterWnd is `nullptr` if WinMerge started minimized.
896         if (pSplitterWnd != nullptr)
897         {
898                 int nRows = pSplitterWnd->GetRowCount ();
899                 int nCols = pSplitterWnd->GetColumnCount ();
900                 for (int nRow = 0; nRow < nRows; nRow++)
901                 {
902                         for (int nCol = 0; nCol < nCols; nCol++)
903                         {
904                                 CMergeEditView *pSiblingView = static_cast<CMergeEditView*>(GetSiblingView (nRow, nCol));
905                                 if (pSiblingView != nullptr)
906                                         pSiblingView->OnDisplayDiff(nDiff);
907                         }
908                 }
909         }
910
911         // update the width of the horizontal scrollbar
912         RecalcHorzScrollBar();
913 }
914
915 /**
916  * @brief Selects diff by number and syncs other file
917  * @param [in] nDiff Diff to select, must be >= 0
918  * @param [in] bScroll Scroll diff to view
919  * @param [in] bSelectText Select diff text
920  * @sa CMergeEditView::ShowDiff()
921  * @sa CMergeDoc::SetCurrentDiff()
922  * @todo Parameter bSelectText is never used?
923  */
924 void CMergeEditView::SelectDiff(int nDiff, bool bScroll /*= true*/, bool bSelectText /*= true*/)
925 {
926         CMergeDoc *pd = GetDocument();
927
928         // Check that nDiff is valid
929         if (nDiff < 0)
930                 _RPTF1(_CRT_ERROR, "Diffnumber negative (%d)", nDiff);
931         if (nDiff >= pd->m_diffList.GetSize())
932                 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d >= %d)",
933                         nDiff, pd->m_diffList.GetSize());
934
935         SelectNone();
936         pd->SetCurrentDiff(nDiff);
937         ShowDiff(bScroll, bSelectText);
938         pd->UpdateAllViews(this);
939         UpdateSiblingScrollPos(false);
940
941         // notify either side, as it will notify the other one
942         pd->ForEachView (0, [&](auto& pView) { if (pView->m_bDetailView) pView->OnDisplayDiff(nDiff); });
943 }
944
945 /**
946  * @brief Called when user selects "Current Difference".
947  * Goes to active diff. If no active diff, selects diff under cursor
948  * @sa CMergeEditView::SelectDiff()
949  * @sa CMergeDoc::GetCurrentDiff()
950  * @sa CMergeDoc::LineToDiff()
951  */
952 void CMergeEditView::OnCurdiff()
953 {
954         CMergeDoc *pd = GetDocument();
955
956         // If no diffs, nothing to select
957         if (!pd->m_diffList.HasSignificantDiffs())
958                 return;
959
960         // GetCurrentDiff() returns -1 if no diff selected
961         int nDiff = pd->GetCurrentDiff();
962         if (nDiff != -1)
963         {
964                 // Scroll to the first line of the currently selected diff
965                 SelectDiff(nDiff, true, false);
966         }
967         else
968         {
969                 // If cursor is inside diff, select that diff
970                 CPoint pos = GetCursorPos();
971                 nDiff = pd->m_diffList.LineToDiff(pos.y);
972                 if (nDiff != -1 && pd->m_diffList.IsDiffSignificant(nDiff))
973                         SelectDiff(nDiff, true, false);
974         }
975 }
976
977 /**
978  * @brief Called when "Current diff" item is updated
979  */
980 void CMergeEditView::OnUpdateCurdiff(CCmdUI* pCmdUI)
981 {
982         CMergeDoc *pd = GetDocument();
983         CPoint pos = GetCursorPos();
984         int nCurrentDiff = pd->GetCurrentDiff();
985         if (nCurrentDiff == -1)
986         {
987                 int nNewDiff = pd->m_diffList.LineToDiff(pos.y);
988                 pCmdUI->Enable(nNewDiff != -1 && pd->m_diffList.IsDiffSignificant(nNewDiff));
989         }
990         else
991                 pCmdUI->Enable(true);
992 }
993
994 /**
995  * @brief Copy selected text to clipboard
996  */
997 void CMergeEditView::OnEditCopy()
998 {
999         CMergeDoc * pDoc = GetDocument();
1000         CPoint ptSelStart, ptSelEnd;
1001         GetSelection(ptSelStart, ptSelEnd);
1002
1003         // Nothing selected
1004         if (ptSelStart == ptSelEnd)
1005                 return;
1006
1007         CString text;
1008
1009         if (!m_bColumnSelection)
1010         {
1011                 CDiffTextBuffer * buffer = pDoc->m_ptBuf[m_nThisPane].get();
1012
1013                 buffer->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1014                         ptSelEnd.y, ptSelEnd.x, text);
1015         }
1016         else
1017                 GetTextWithoutEmptysInColumnSelection(text);
1018
1019         PutToClipboard(text, text.GetLength(), m_bColumnSelection);
1020 }
1021
1022 /**
1023  * @brief Called when "Copy" item is updated
1024  */
1025 void CMergeEditView::OnUpdateEditCopy(CCmdUI* pCmdUI)
1026 {
1027         CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
1028 }
1029
1030 /**
1031  * @brief Cut current selection to clipboard
1032  */
1033 void CMergeEditView::OnEditCut()
1034 {
1035         if (IsReadOnly(m_nThisPane))
1036                 return;
1037
1038         CPoint ptSelStart, ptSelEnd;
1039         CMergeDoc * pDoc = GetDocument();
1040         GetSelection(ptSelStart, ptSelEnd);
1041
1042         // Nothing selected
1043         if (ptSelStart == ptSelEnd)
1044                 return;
1045
1046         CString text;
1047         if (!m_bColumnSelection)
1048                 pDoc->m_ptBuf[m_nThisPane]->GetTextWithoutEmptys(ptSelStart.y, ptSelStart.x,
1049                         ptSelEnd.y, ptSelEnd.x, text);
1050         else
1051                 GetTextWithoutEmptysInColumnSelection(text);
1052
1053         PutToClipboard(text, text.GetLength(), m_bColumnSelection);
1054
1055         if (!m_bColumnSelection)
1056         {
1057                 CPoint ptCursorPos = ptSelStart;
1058                 ASSERT_VALIDTEXTPOS(ptCursorPos);
1059                 SetAnchor(ptCursorPos);
1060                 SetSelection(ptCursorPos, ptCursorPos);
1061                 SetCursorPos(ptCursorPos);
1062                 EnsureVisible(ptCursorPos);
1063
1064                 pDoc->m_ptBuf[m_nThisPane]->DeleteText(this, ptSelStart.y, ptSelStart.x, ptSelEnd.y,
1065                         ptSelEnd.x, CE_ACTION_CUT);
1066         }
1067         else
1068                 DeleteCurrentColumnSelection (CE_ACTION_CUT);
1069
1070         m_pTextBuffer->SetModified(true);
1071 }
1072
1073 /**
1074  * @brief Called when "Cut" item is updated
1075  */
1076 void CMergeEditView::OnUpdateEditCut(CCmdUI* pCmdUI)
1077 {
1078         if (!IsReadOnly(m_nThisPane))
1079                 CCrystalEditViewEx::OnUpdateEditCut(pCmdUI);
1080         else
1081                 pCmdUI->Enable(false);
1082 }
1083
1084 /**
1085  * @brief Paste text from clipboard
1086  */
1087 void CMergeEditView::OnEditPaste()
1088 {
1089         if (IsReadOnly(m_nThisPane))
1090                 return;
1091
1092         CCrystalEditViewEx::Paste();
1093         m_pTextBuffer->SetModified(true);
1094 }
1095
1096 /**
1097  * @brief Called when "Paste" item is updated
1098  */
1099 void CMergeEditView::OnUpdateEditPaste(CCmdUI* pCmdUI)
1100 {
1101         if (!IsReadOnly(m_nThisPane))
1102                 CCrystalEditViewEx::OnUpdateEditPaste(pCmdUI);
1103         else
1104                 pCmdUI->Enable(false);
1105 }
1106
1107 /**
1108  * @brief Undo last action
1109  */
1110 void CMergeEditView::OnEditUndo()
1111 {
1112         CWaitCursor waitstatus;
1113         CMergeDoc* pDoc = GetDocument();
1114         CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1115         if(tgt==this)
1116         {
1117                 if (IsReadOnly(m_nThisPane))
1118                         return;
1119
1120                 GetParentFrame()->SetActiveView(this, true);
1121                 if(CCrystalEditViewEx::DoEditUndo())
1122                 {
1123                         --pDoc->curUndo;
1124                         pDoc->UpdateHeaderPath(m_nThisPane);
1125                         pDoc->FlushAndRescan();
1126
1127                         int nAction;
1128                         m_pTextBuffer->GetRedoActionCode(nAction);
1129                         if (nAction == CE_ACTION_MERGE)
1130                                 // select the diff so we may just merge it again
1131                                 OnCurdiff();
1132                 }
1133         }
1134         else
1135         {
1136                 tgt->SendMessage(WM_COMMAND, ID_EDIT_UNDO);
1137         }
1138         if (!pDoc->CanUndo())
1139                 pDoc->SetAutoMerged(false);
1140 }
1141
1142 /**
1143  * @brief Called when "Undo" item is updated
1144  */
1145 void CMergeEditView::OnUpdateEditUndo(CCmdUI* pCmdUI)
1146 {
1147         CMergeDoc* pDoc = GetDocument();
1148         if (pDoc->curUndo!=pDoc->undoTgt.begin())
1149         {
1150                 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo-1));
1151                 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
1152         }
1153         else
1154                 pCmdUI->Enable(false);
1155 }
1156
1157 /**
1158  * @brief Go to first diff
1159  *
1160  * Called when user selects "First Difference"
1161  * @sa CMergeEditView::SelectDiff()
1162  */
1163 void CMergeEditView::OnFirstdiff()
1164 {
1165         CMergeDoc *pd = GetDocument();
1166         if (pd->m_diffList.HasSignificantDiffs())
1167         {
1168                 int nDiff = pd->m_diffList.FirstSignificantDiff();
1169                 SelectDiff(nDiff, true, false);
1170         }
1171 }
1172
1173 /**
1174  * @brief Update "First diff" UI items
1175  */
1176 void CMergeEditView::OnUpdateFirstdiff(CCmdUI* pCmdUI)
1177 {
1178         OnUpdatePrevdiff(pCmdUI);
1179 }
1180
1181 /**
1182  * @brief Go to last diff
1183  */
1184 void CMergeEditView::OnLastdiff()
1185 {
1186         CMergeDoc *pd = GetDocument();
1187         if (pd->m_diffList.HasSignificantDiffs())
1188         {
1189                 int nDiff = pd->m_diffList.LastSignificantDiff();
1190                 SelectDiff(nDiff, true, false);
1191         }
1192 }
1193
1194 /**
1195  * @brief Update "Last diff" UI items
1196  */
1197 void CMergeEditView::OnUpdateLastdiff(CCmdUI* pCmdUI)
1198 {
1199         OnUpdateNextdiff(pCmdUI);
1200 }
1201
1202 /**
1203  * @brief Go to next diff and select it.
1204  *
1205  * Finds and selects next difference. There are several cases:
1206  * - if there is selected difference, and that difference is visible
1207  * on screen, next found difference is selected.
1208  * - if there is selected difference but it is not visible, next
1209  * difference from cursor position is selected. This is what user
1210  * expects to happen and is natural thing to do. Also reduces
1211  * needless scrolling.
1212  * - if there is no selected difference, next difference from cursor
1213  * position is selected.
1214  */
1215 void CMergeEditView::OnNextdiff()
1216 {
1217         CMergeDoc *pd = GetDocument();
1218         int cnt = pd->m_ptBuf[0]->GetLineCount();
1219         if (cnt <= 0)
1220                 return;
1221
1222         // Returns -1 if no diff selected
1223         int nextDiff = -1;
1224         int curDiff = pd->GetCurrentDiff();
1225         if (curDiff != -1)
1226         {
1227                 // We're on a diff
1228                 if (!IsDiffVisible(curDiff))
1229                 {
1230                         // Selected difference not visible, select next from cursor
1231                         int line = GetCursorPos().y;
1232                         // Make sure we aren't in the first line of the diff
1233                         ++line;
1234                         if (!IsValidTextPosY(CPoint(0, line)))
1235                                 line = m_nTopLine;
1236                         nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1237                 }
1238                 else
1239                 {
1240                         // Find out if there is a following significant diff
1241                         if (curDiff < pd->m_diffList.GetSize() - 1)
1242                         {
1243                                 nextDiff = pd->m_diffList.NextSignificantDiff(curDiff);
1244                         }
1245                 }
1246         }
1247         else
1248         {
1249                 // We don't have a selected difference,
1250                 // but cursor can be inside inactive diff
1251                 int line = GetCursorPos().y;
1252                 if (!IsValidTextPosY(CPoint(0, line)))
1253                         line = m_nTopLine;
1254                 nextDiff = pd->m_diffList.NextSignificantDiffFromLine(line);
1255         }
1256
1257         int lastDiff = pd->m_diffList.LastSignificantDiff();
1258         if (nextDiff >= 0 && nextDiff <= lastDiff)
1259                 SelectDiff(nextDiff, true, false);
1260         else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1261         {
1262                 if (pDirDoc->MoveableToNextDiff())
1263                         pDirDoc->MoveToNextDiff(pd);
1264         }
1265 }
1266
1267 /**
1268  * @brief Update "Next diff" UI items
1269  */
1270 void CMergeEditView::OnUpdateNextdiff(CCmdUI* pCmdUI)
1271 {
1272         CMergeDoc *pd = GetDocument();
1273         const DIFFRANGE * dfi = pd->m_diffList.LastSignificantDiffRange();
1274         bool enabled;
1275
1276         if (dfi == nullptr)
1277         {
1278                 // There aren't any significant differences
1279                 enabled = false;
1280         }
1281         else
1282         {
1283                 // Enable if the beginning of the last significant difference is after caret
1284                 enabled = (GetCursorPos().y < (long)dfi->dbegin);
1285         }
1286
1287         if (!enabled && pd->GetDirDoc())
1288                 enabled = pd->GetDirDoc()->MoveableToNextDiff();
1289
1290         pCmdUI->Enable(enabled);
1291 }
1292
1293 /**
1294  * @brief Go to previous diff and select it.
1295  *
1296  * Finds and selects previous difference. There are several cases:
1297  * - if there is selected difference, and that difference is visible
1298  * on screen, previous found difference is selected.
1299  * - if there is selected difference but it is not visible, previous
1300  * difference from cursor position is selected. This is what user
1301  * expects to happen and is natural thing to do. Also reduces
1302  * needless scrolling.
1303  * - if there is no selected difference, previous difference from cursor
1304  * position is selected.
1305  */
1306 void CMergeEditView::OnPrevdiff()
1307 {
1308         CMergeDoc *pd = GetDocument();
1309         int cnt = pd->m_ptBuf[0]->GetLineCount();
1310         if (cnt <= 0)
1311                 return;
1312
1313         // GetCurrentDiff() returns -1 if no diff selected
1314         int prevDiff = -1;
1315         int curDiff = pd->GetCurrentDiff();
1316         if (curDiff != -1)
1317         {
1318                 // We're on a diff
1319                 if (!IsDiffVisible(curDiff))
1320                 {
1321                         // Selected difference not visible, select previous from cursor
1322                         int line = GetCursorPos().y;
1323                         // Make sure we aren't in the last line of the diff
1324                         --line;
1325                         if (!IsValidTextPosY(CPoint(0, line)))
1326                                 line = m_nTopLine;
1327                         prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1328                 }
1329                 else
1330                 {
1331                         // Find out if there is a preceding significant diff
1332                         if (curDiff > 0)
1333                         {
1334                                 prevDiff = pd->m_diffList.PrevSignificantDiff(curDiff);
1335                         }
1336                 }
1337         }
1338         else
1339         {
1340                 // We don't have a selected difference,
1341                 // but cursor can be inside inactive diff
1342                 int line = GetCursorPos().y;
1343                 if (!IsValidTextPosY(CPoint(0, line)))
1344                         line = m_nTopLine;
1345                 prevDiff = pd->m_diffList.PrevSignificantDiffFromLine(line);
1346         }
1347
1348         int firstDiff = pd->m_diffList.FirstSignificantDiff();
1349         if (prevDiff >= 0 && prevDiff >= firstDiff)
1350                 SelectDiff(prevDiff, true, false);
1351         else if (CDirDoc *pDirDoc = pd->GetDirDoc())
1352         {
1353                 if (pDirDoc->MoveableToPrevDiff())
1354                         pDirDoc->MoveToPrevDiff(pd);
1355         }
1356 }
1357
1358 /**
1359  * @brief Update "Previous diff" UI items
1360  */
1361 void CMergeEditView::OnUpdatePrevdiff(CCmdUI* pCmdUI)
1362 {
1363         CMergeDoc *pd = GetDocument();
1364         const DIFFRANGE * dfi = pd->m_diffList.FirstSignificantDiffRange();
1365         bool enabled;
1366
1367         if (dfi == nullptr)
1368         {
1369                 // There aren't any significant differences
1370                 enabled = false;
1371         }
1372         else
1373         {
1374                 // Enable if the end of the first significant difference is before caret
1375                 enabled = (GetCursorPos().y > (long)dfi->dend);
1376         }
1377
1378         if (!enabled && pd->GetDirDoc())
1379                 enabled = pd->GetDirDoc()->MoveableToPrevDiff();
1380
1381         pCmdUI->Enable(enabled);
1382 }
1383
1384 void CMergeEditView::OnNextConflict()
1385 {
1386         OnNext3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1387 }
1388
1389 /**
1390  * @brief Update "Next Conflict" UI items
1391  */
1392 void CMergeEditView::OnUpdateNextConflict(CCmdUI* pCmdUI)
1393 {
1394         OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1395 }
1396
1397 void CMergeEditView::OnPrevConflict()
1398 {
1399         OnPrev3wayDiff(THREEWAYDIFFTYPE_CONFLICT);
1400 }
1401
1402 /**
1403  * @brief Update "Prev Conflict" UI items
1404  */
1405 void CMergeEditView::OnUpdatePrevConflict(CCmdUI* pCmdUI)
1406 {
1407         OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_CONFLICT);
1408 }
1409
1410 /**
1411  * @brief Go to next 3-way diff and select it.
1412  */
1413 void CMergeEditView::OnNext3wayDiff(int nDiffType)
1414 {
1415         CMergeDoc *pd = GetDocument();
1416         int cnt = pd->m_ptBuf[0]->GetLineCount();
1417         if (cnt <= 0)
1418                 return;
1419
1420         // Returns -1 if no diff selected
1421         int curDiff = pd->GetCurrentDiff();
1422         if (curDiff != -1)
1423         {
1424                 // We're on a diff
1425                 int nextDiff = curDiff;
1426                 if (!IsDiffVisible(curDiff))
1427                 {
1428                         // Selected difference not visible, select next from cursor
1429                         int line = GetCursorPos().y;
1430                         // Make sure we aren't in the first line of the diff
1431                         ++line;
1432                         if (!IsValidTextPosY(CPoint(0, line)))
1433                                 line = m_nTopLine;
1434                         nextDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1435                 }
1436                 else
1437                 {
1438                         // Find out if there is a following significant diff
1439                         if (curDiff < pd->m_diffList.GetSize() - 1)
1440                         {
1441                                 nextDiff = pd->m_diffList.NextSignificant3wayDiff(curDiff, nDiffType);
1442                         }
1443                 }
1444                 if (nextDiff == -1)
1445                         nextDiff = curDiff;
1446
1447                 // nextDiff is the next one if there is one, else it is the one we're on
1448                 SelectDiff(nextDiff, true, false);
1449         }
1450         else
1451         {
1452                 // We don't have a selected difference,
1453                 // but cursor can be inside inactive diff
1454                 int line = GetCursorPos().y;
1455                 if (!IsValidTextPosY(CPoint(0, line)))
1456                         line = m_nTopLine;
1457                 curDiff = pd->m_diffList.NextSignificant3wayDiffFromLine(line, nDiffType);
1458                 if (curDiff >= 0)
1459                         SelectDiff(curDiff, true, false);
1460         }
1461 }
1462
1463 /**
1464  * @brief Update "Next 3-way diff" UI items
1465  */
1466 void CMergeEditView::OnUpdateNext3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1467 {
1468         CMergeDoc *pd = GetDocument();
1469
1470         if (pd->m_nBuffers < 3)
1471         {
1472                 pCmdUI->Enable(false);
1473                 return;
1474         }
1475
1476         const DIFFRANGE * dfi = pd->m_diffList.LastSignificant3wayDiffRange(nDiffType);
1477
1478         if (dfi == nullptr)
1479         {
1480                 // There aren't any significant differences
1481                 pCmdUI->Enable(false);
1482         }
1483         else
1484         {
1485                 // Enable if the beginning of the last significant difference is after caret
1486                 CPoint pos = GetCursorPos();
1487                 pCmdUI->Enable(pos.y < (long)dfi->dbegin);
1488         }
1489 }
1490
1491 /**
1492  * @brief Go to previous 3-way diff and select it.
1493  */
1494 void CMergeEditView::OnPrev3wayDiff(int nDiffType)
1495 {
1496         CMergeDoc *pd = GetDocument();
1497
1498         int cnt = pd->m_ptBuf[0]->GetLineCount();
1499         if (cnt <= 0)
1500                 return;
1501
1502         // GetCurrentDiff() returns -1 if no diff selected
1503         int curDiff = pd->GetCurrentDiff();
1504         if (curDiff != -1)
1505         {
1506                 // We're on a diff
1507                 int prevDiff = curDiff;
1508                 if (!IsDiffVisible(curDiff))
1509                 {
1510                         // Selected difference not visible, select previous from cursor
1511                         int line = GetCursorPos().y;
1512                         // Make sure we aren't in the last line of the diff
1513                         --line;
1514                         if (!IsValidTextPosY(CPoint(0, line)))
1515                                 line = m_nTopLine;
1516                         prevDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1517                 }
1518                 else
1519                 {
1520                         // Find out if there is a preceding significant diff
1521                         if (curDiff > 0)
1522                         {
1523                                 prevDiff = pd->m_diffList.PrevSignificant3wayDiff(curDiff, nDiffType);
1524                         }
1525                 }
1526                 if (prevDiff == -1)
1527                         prevDiff = curDiff;
1528
1529                 // prevDiff is the preceding one if there is one, else it is the one we're on
1530                 SelectDiff(prevDiff, true, false);
1531         }
1532         else
1533         {
1534                 // We don't have a selected difference,
1535                 // but cursor can be inside inactive diff
1536                 int line = GetCursorPos().y;
1537                 if (!IsValidTextPosY(CPoint(0, line)))
1538                         line = m_nTopLine;
1539                 curDiff = pd->m_diffList.PrevSignificant3wayDiffFromLine(line, nDiffType);
1540                 if (curDiff >= 0)
1541                         SelectDiff(curDiff, true, false);
1542         }
1543 }
1544
1545 /**
1546  * @brief Update "Previous diff X and Y" UI items
1547  */
1548 void CMergeEditView::OnUpdatePrev3wayDiff(CCmdUI* pCmdUI, int nDiffType)
1549 {
1550         CMergeDoc *pd = GetDocument();
1551
1552         if (pd->m_nBuffers < 3)
1553         {
1554                 pCmdUI->Enable(false);
1555                 return;
1556         }
1557
1558         const DIFFRANGE * dfi = pd->m_diffList.FirstSignificant3wayDiffRange(nDiffType);
1559
1560         if (dfi == nullptr)
1561         {
1562                 // There aren't any significant differences
1563                 pCmdUI->Enable(false);
1564         }
1565         else
1566         {
1567                 // Enable if the end of the first significant difference is before caret
1568                 CPoint pos = GetCursorPos();
1569                 pCmdUI->Enable(pos.y > (long)dfi->dend);
1570         }
1571 }
1572
1573 void CMergeEditView::OnNextdiffLM()
1574 {
1575         OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1576 }
1577
1578 void CMergeEditView::OnUpdateNextdiffLM(CCmdUI* pCmdUI)
1579 {
1580         OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1581 }
1582
1583 void CMergeEditView::OnNextdiffLR()
1584 {
1585         OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1586 }
1587
1588 void CMergeEditView::OnUpdateNextdiffLR(CCmdUI* pCmdUI)
1589 {
1590         OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1591 }
1592
1593 void CMergeEditView::OnNextdiffMR()
1594 {
1595         OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1596 }
1597
1598 void CMergeEditView::OnUpdateNextdiffMR(CCmdUI* pCmdUI)
1599 {
1600         OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1601 }
1602
1603 void CMergeEditView::OnNextdiffLO()
1604 {
1605         OnNext3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1606 }
1607
1608 void CMergeEditView::OnUpdateNextdiffLO(CCmdUI* pCmdUI)
1609 {
1610         OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1611 }
1612
1613 void CMergeEditView::OnNextdiffMO()
1614 {
1615         OnNext3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1616 }
1617
1618 void CMergeEditView::OnUpdateNextdiffMO(CCmdUI* pCmdUI)
1619 {
1620         OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1621 }
1622
1623 void CMergeEditView::OnNextdiffRO()
1624 {
1625         OnNext3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1626 }
1627
1628 void CMergeEditView::OnUpdateNextdiffRO(CCmdUI* pCmdUI)
1629 {
1630         OnUpdateNext3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1631 }
1632
1633 void CMergeEditView::OnPrevdiffLM()
1634 {
1635         OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTMIDDLE);
1636 }
1637
1638 void CMergeEditView::OnUpdatePrevdiffLM(CCmdUI* pCmdUI)
1639 {
1640         OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTMIDDLE);
1641 }
1642
1643 void CMergeEditView::OnPrevdiffLR()
1644 {
1645         OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTRIGHT);
1646 }
1647
1648 void CMergeEditView::OnUpdatePrevdiffLR(CCmdUI* pCmdUI)
1649 {
1650         OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTRIGHT);
1651 }
1652
1653 void CMergeEditView::OnPrevdiffMR()
1654 {
1655         OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLERIGHT);
1656 }
1657
1658 void CMergeEditView::OnUpdatePrevdiffMR(CCmdUI* pCmdUI)
1659 {
1660         OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLERIGHT);
1661 }
1662
1663 void CMergeEditView::OnPrevdiffLO()
1664 {
1665         OnPrev3wayDiff(THREEWAYDIFFTYPE_LEFTONLY);
1666 }
1667
1668 void CMergeEditView::OnUpdatePrevdiffLO(CCmdUI* pCmdUI)
1669 {
1670         OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_LEFTONLY);
1671 }
1672
1673 void CMergeEditView::OnPrevdiffMO()
1674 {
1675         OnPrev3wayDiff(THREEWAYDIFFTYPE_MIDDLEONLY);
1676 }
1677
1678 void CMergeEditView::OnUpdatePrevdiffMO(CCmdUI* pCmdUI)
1679 {
1680         OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_MIDDLEONLY);
1681 }
1682
1683 void CMergeEditView::OnPrevdiffRO()
1684 {
1685         OnPrev3wayDiff(THREEWAYDIFFTYPE_RIGHTONLY);
1686 }
1687
1688 void CMergeEditView::OnUpdatePrevdiffRO(CCmdUI* pCmdUI)
1689 {
1690         OnUpdatePrev3wayDiff(pCmdUI, THREEWAYDIFFTYPE_RIGHTONLY);
1691 }
1692
1693 /**
1694  * @brief Clear selection
1695  */
1696 void CMergeEditView::SelectNone()
1697 {
1698         SetSelection (GetCursorPos(), GetCursorPos());
1699         UpdateCaret();
1700 }
1701
1702 /**
1703  * @brief Check if line is inside currently selected diff
1704  * @param [in] nLine 0-based linenumber in view
1705  * @sa CMergeDoc::GetCurrentDiff()
1706  * @sa CMergeDoc::LineInDiff()
1707  */
1708 bool CMergeEditView::IsLineInCurrentDiff(int nLine) const
1709 {
1710         // Check validity of nLine
1711 #ifdef _DEBUG
1712         if (nLine < 0)
1713                 _RPTF1(_CRT_ERROR, "Linenumber is negative (%d)!", nLine);
1714         int nLineCount = LocateTextBuffer()->GetLineCount();
1715         if (nLine >= nLineCount)
1716                 _RPTF2(_CRT_ERROR, "Linenumber > linecount (%d>%d)!", nLine, nLineCount);
1717 #endif
1718
1719         const CMergeDoc *pd = GetDocument();
1720         int curDiff = pd->GetCurrentDiff();
1721         if (curDiff == -1)
1722                 return false;
1723         return pd->m_diffList.LineInDiff(nLine, curDiff);
1724 }
1725
1726 /**
1727  * @brief Called when mouse left-button double-clicked
1728  *
1729  * Double-clicking mouse inside diff selects that diff
1730  */
1731 void CMergeEditView::OnLButtonDblClk(UINT nFlags, CPoint point)
1732 {
1733         CMergeDoc *pd = GetDocument();
1734         CPoint pos = GetCursorPos();
1735
1736         int diff = pd->m_diffList.LineToDiff(pos.y);
1737         if (diff != -1 && pd->m_diffList.IsDiffSignificant(diff))
1738                 SelectDiff(diff, false, false);
1739
1740         CCrystalEditViewEx::OnLButtonDblClk(nFlags, point);
1741 }
1742
1743 /**
1744  * @brief Called when mouse left button is released.
1745  *
1746  * If button is released outside diffs, current diff
1747  * is deselected.
1748  */
1749 void CMergeEditView::OnLButtonUp(UINT nFlags, CPoint point)
1750 {
1751         CMergeDoc *pd = GetDocument();
1752         CCrystalEditViewEx::OnLButtonUp(nFlags, point);
1753
1754         // If we have a selected diff, deselect it
1755         int nCurrentDiff = pd->GetCurrentDiff();
1756         if (nCurrentDiff != -1)
1757         {
1758                 CPoint pos = GetCursorPos();
1759                 if (!IsLineInCurrentDiff(pos.y))
1760                 {
1761                         pd->SetCurrentDiff(-1);
1762                         Invalidate();
1763                         pd->UpdateAllViews(this);
1764                 }
1765         }
1766 }
1767
1768 void CMergeEditView::OnX2Y(int srcPane, int dstPane)
1769 {
1770         // Check that right side is not readonly
1771         if (IsReadOnly(dstPane))
1772                 return;
1773
1774         CMergeDoc *pDoc = GetDocument();
1775         int currentDiff = pDoc->GetCurrentDiff();
1776
1777         if (currentDiff == -1)
1778         {
1779                 // No selected diff
1780                 // If cursor is inside diff get number of that diff
1781                 if (m_bCurrentLineIsDiff)
1782                 {
1783                         CPoint pt = GetCursorPos();
1784                         currentDiff = pDoc->m_diffList.LineToDiff(pt.y);
1785                 }
1786         }
1787
1788         if (IsSelection())
1789         {
1790                 if (!m_bColumnSelection)
1791                 {
1792                         int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
1793                         GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1794                         if (firstDiff != -1 && lastDiff != -1)
1795                         {
1796                                 CWaitCursor waitstatus;
1797                                 pDoc->CopyMultipleList(srcPane, dstPane, firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1798                         }
1799                 }
1800                 else
1801                 {
1802                         CWaitCursor waitstatus;
1803                         auto wordDiffs = GetColumnSelectedWordDiffIndice();
1804                         int i = 0;
1805                         std::for_each(wordDiffs.rbegin(), wordDiffs.rend(), [&](auto& it) {
1806                                 pDoc->WordListCopy(srcPane, dstPane, it.first, it.second[0], it.second[it.second.size() - 1], &it.second, i != 0, i == 0);
1807                                 ++i;
1808                         });
1809                 }
1810         }
1811         else if (currentDiff != -1 && pDoc->m_diffList.IsDiffSignificant(currentDiff))
1812         {
1813                 CWaitCursor waitstatus;
1814                 pDoc->ListCopy(srcPane, dstPane, currentDiff);
1815         }
1816 }
1817
1818 void CMergeEditView::OnUpdateX2Y(int dstPane, CCmdUI* pCmdUI)
1819 {
1820         // Check that right side is not readonly
1821         if (!IsReadOnly(dstPane))
1822         {
1823                 // If one or more diffs inside selection OR
1824                 // there is an active diff OR
1825                 // cursor is inside diff
1826                 if (IsSelection())
1827                 {
1828                         int firstDiff, lastDiff, firstWordDiff, lastWordDiff;
1829                         GetFullySelectedDiffs(firstDiff, lastDiff, firstWordDiff, lastWordDiff);
1830
1831                         pCmdUI->Enable(firstDiff != -1 && lastDiff != -1);
1832                 }
1833                 else
1834                 {
1835                         const int currDiff = GetDocument()->GetCurrentDiff();
1836                         if (currDiff != -1 && GetDocument()->m_diffList.IsDiffSignificant(currDiff))
1837                                 pCmdUI->Enable(true);
1838                         else
1839                                 pCmdUI->Enable(m_bCurrentLineIsDiff);
1840                 }
1841         }
1842         else
1843                 pCmdUI->Enable(false);
1844 }
1845
1846 /**
1847  * @brief Copy diff from left pane to right pane
1848  *
1849  * Difference is copied from left to right when
1850  * - difference is selected
1851  * - difference is inside selection (allows merging multiple differences).
1852  * - cursor is inside diff
1853  *
1854  * If there is selected diff outside selection, we copy selected
1855  * difference only.
1856  */
1857 void CMergeEditView::OnL2r()
1858 {
1859         int dstPane = (m_nThisPane < GetDocument()->m_nBuffers - 1) ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1860         int srcPane = dstPane - 1;
1861         OnX2Y(srcPane, dstPane);
1862 }
1863
1864 /**
1865  * @brief Called when "Copy to left" item is updated
1866  */
1867 void CMergeEditView::OnUpdateL2r(CCmdUI* pCmdUI)
1868 {
1869         OnUpdateX2Y(m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1, pCmdUI);
1870 }
1871
1872 /**
1873  * @brief Copy diff from right pane to left pane
1874  *
1875  * Difference is copied from left to right when
1876  * - difference is selected
1877  * - difference is inside selection (allows merging multiple differences).
1878  * - cursor is inside diff
1879  *
1880  * If there is selected diff outside selection, we copy selected
1881  * difference only.
1882  */
1883 void CMergeEditView::OnR2l()
1884 {
1885         int dstPane = (m_nThisPane > 0) ? m_nThisPane - 1 : 0;
1886         int srcPane = dstPane + 1;
1887         OnX2Y(srcPane, dstPane);
1888 }
1889
1890 /**
1891  * @brief Called when "Copy to right" item is updated
1892  */
1893 void CMergeEditView::OnUpdateR2l(CCmdUI* pCmdUI)
1894 {
1895         OnUpdateX2Y(m_nThisPane > 0 ? m_nThisPane - 1 : 0, pCmdUI);
1896 }
1897
1898 void CMergeEditView::OnCopyFromLeft()
1899 {
1900         int dstPane = m_nThisPane;
1901         int srcPane = dstPane - 1;
1902         if (srcPane < 0)
1903                 return;
1904         OnX2Y(srcPane, dstPane);
1905 }
1906
1907 void CMergeEditView::OnUpdateCopyFromLeft(CCmdUI* pCmdUI)
1908 {
1909         int dstPane = m_nThisPane;
1910         int srcPane = dstPane - 1;
1911         if (srcPane < 0)
1912                 pCmdUI->Enable(false);
1913         else
1914                 OnUpdateX2Y(dstPane, pCmdUI);
1915 }
1916
1917 void CMergeEditView::OnCopyFromRight()
1918 {
1919         int dstPane = m_nThisPane;
1920         int srcPane = dstPane + 1;
1921         if (srcPane >= GetDocument()->m_nBuffers)
1922                 return;
1923         OnX2Y(srcPane, dstPane);
1924 }
1925
1926 void CMergeEditView::OnUpdateCopyFromRight(CCmdUI* pCmdUI)
1927 {
1928         int dstPane = m_nThisPane;
1929         int srcPane = dstPane + 1;
1930         if (srcPane >= GetDocument()->m_nBuffers)
1931                 pCmdUI->Enable(false);
1932         else
1933                 OnUpdateX2Y(dstPane, pCmdUI);
1934 }
1935
1936 /**
1937  * @brief Copy all diffs from right pane to left pane
1938  */
1939 void CMergeEditView::OnAllLeft()
1940 {
1941         // Check that left side is not readonly
1942         int srcPane = m_nThisPane > 0 ? m_nThisPane : 1;
1943         int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
1944         if (IsReadOnly(dstPane))
1945                 return;
1946         CWaitCursor waitstatus;
1947
1948         GetDocument()->CopyAllList(srcPane, dstPane);
1949 }
1950
1951 /**
1952  * @brief Called when "Copy all to left" item is updated
1953  */
1954 void CMergeEditView::OnUpdateAllLeft(CCmdUI* pCmdUI)
1955 {
1956         // Check that left side is not readonly
1957         int dstPane = m_nThisPane > 0 ? m_nThisPane - 1 : 0;
1958         if (!IsReadOnly(dstPane))
1959                 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
1960         else
1961                 pCmdUI->Enable(false);
1962 }
1963
1964 /**
1965  * @brief Copy all diffs from left pane to right pane
1966  */
1967 void CMergeEditView::OnAllRight()
1968 {
1969         // Check that right side is not readonly
1970         int srcPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane : m_nThisPane - 1;
1971         int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1972         if (IsReadOnly(dstPane))
1973                 return;
1974
1975         CWaitCursor waitstatus;
1976
1977         GetDocument()->CopyAllList(srcPane, dstPane);
1978 }
1979
1980 /**
1981  * @brief Called when "Copy all to right" item is updated
1982  */
1983 void CMergeEditView::OnUpdateAllRight(CCmdUI* pCmdUI)
1984 {
1985         // Check that right side is not readonly
1986         int dstPane = m_nThisPane < GetDocument()->m_nBuffers - 1 ? m_nThisPane + 1 : GetDocument()->m_nBuffers - 1;
1987         if (!IsReadOnly(dstPane))
1988                 pCmdUI->Enable(GetDocument()->m_diffList.HasSignificantDiffs());
1989         else
1990                 pCmdUI->Enable(false);
1991 }
1992
1993 /**
1994  * @brief Do Auto merge
1995  */
1996 void CMergeEditView::OnAutoMerge()
1997 {
1998         // Check current pane is not readonly
1999         if (GetDocument()->IsModified() || GetDocument()->GetAutoMerged() || IsReadOnly(m_nThisPane))
2000                 return;
2001
2002         CWaitCursor waitstatus;
2003
2004         GetDocument()->DoAutoMerge(m_nThisPane);
2005 }
2006
2007 /**
2008  * @brief Called when "Auto Merge" item is updated
2009  */
2010 void CMergeEditView::OnUpdateAutoMerge(CCmdUI* pCmdUI)
2011 {
2012         pCmdUI->Enable(GetDocument()->m_nBuffers == 3 && 
2013                 !GetDocument()->IsModified() && 
2014                 !GetDocument()->GetAutoMerged() && 
2015                 !IsReadOnly(m_nThisPane));
2016 }
2017
2018 /**
2019  * @brief Add synchronization point
2020  */
2021 void CMergeEditView::OnAddSyncPoint()
2022 {
2023         GetDocument()->AddSyncPoint();
2024 }
2025
2026 /**
2027  * @brief Clear synchronization points
2028  */
2029 void CMergeEditView::OnClearSyncPoints()
2030 {
2031         GetDocument()->ClearSyncPoints();
2032 }
2033
2034 /**
2035  * @brief Called when "Clear Synchronization Points" item is updated
2036  */
2037 void CMergeEditView::OnUpdateClearSyncPoints(CCmdUI* pCmdUI)
2038 {
2039         pCmdUI->Enable(GetDocument()->HasSyncPoints());
2040 }
2041
2042 /**
2043  * @brief This function is called before other edit events.
2044  * @param [in] nAction Edit operation to do
2045  * @param [in] pszText Text to insert, delete etc
2046  * @sa CCrystalEditView::OnEditOperation()
2047  * @todo More edit-events for rescan delaying?
2048  */
2049 void CMergeEditView::OnEditOperation(int nAction, LPCTSTR pszText, size_t cchText)
2050 {
2051         if (IsReadOnly(m_nThisPane))
2052         {
2053                 // We must not arrive here, and assert helps detect troubles
2054                 ASSERT(false);
2055                 return;
2056         }
2057
2058         CMergeDoc* pDoc = GetDocument();
2059         pDoc->SetEditedAfterRescan(m_nThisPane);
2060
2061         // simple hook for multiplex undo operations
2062         // deleted by jtuc 2003-06-28
2063         // now AddUndoRecords does it (so we don't create entry for OnEditOperation with no Undo data in m_pTextBuffer)
2064         /*if(dynamic_cast<CMergeDoc::CDiffTextBuffer*>(m_pTextBuffer)->curUndoGroup())
2065         {
2066                 pDoc->undoTgt.erase(pDoc->curUndo, pDoc->undoTgt.end());
2067                 pDoc->undoTgt.push_back(this);
2068                 pDoc->curUndo = pDoc->undoTgt.end();
2069         }*/
2070
2071         // perform original function
2072         CCrystalEditViewEx::OnEditOperation(nAction, pszText, cchText);
2073
2074         // augment with additional operations
2075
2076         // Change header to inform about changed doc
2077         pDoc->UpdateHeaderPath(m_nThisPane);
2078
2079         // If automatic rescan enabled, rescan after edit events
2080         if (m_bAutomaticRescan)
2081         {
2082                 // keep document up to date     
2083                 // (Re)start timer to rescan only when user edits text
2084                 // If timer starting fails, rescan immediately
2085                 if (nAction == CE_ACTION_TYPING ||
2086                         nAction == CE_ACTION_REPLACE ||
2087                         nAction == CE_ACTION_BACKSPACE ||
2088                         nAction == CE_ACTION_INDENT ||
2089                         nAction == CE_ACTION_PASTE ||
2090                         nAction == CE_ACTION_DELSEL ||
2091                         nAction == CE_ACTION_DELETE ||
2092                         nAction == CE_ACTION_CUT)
2093                 {
2094                         if (!SetTimer(IDT_RESCAN, RESCAN_TIMEOUT, nullptr))
2095                                 pDoc->FlushAndRescan();
2096                 }
2097                 else
2098                         pDoc->FlushAndRescan();
2099         }
2100         else
2101         {
2102                 if (m_bWordWrap)
2103                 {
2104                         // Update other pane for sync line.
2105                         for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
2106                         {
2107                                 if (nPane == m_nThisPane)
2108                                         continue;
2109                                 CCrystalEditView *pView = GetGroupView(nPane);
2110                                 if (pView != nullptr)
2111                                         pView->Invalidate();
2112                         }
2113                 }
2114         }
2115 }
2116
2117 /**
2118  * @brief Redo last action
2119  */
2120 void CMergeEditView::OnEditRedo()
2121 {
2122         CWaitCursor waitstatus;
2123         CMergeDoc* pDoc = GetDocument();
2124         CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2125         if(tgt==this)
2126         {
2127                 if (IsReadOnly(m_nThisPane))
2128                         return;
2129
2130                 GetParentFrame()->SetActiveView(this, true);
2131                 if(CCrystalEditViewEx::DoEditRedo())
2132                 {
2133                         ++pDoc->curUndo;
2134                         pDoc->UpdateHeaderPath(m_nThisPane);
2135                         pDoc->FlushAndRescan();
2136                 }
2137         }
2138         else
2139         {
2140                 tgt->SendMessage(WM_COMMAND, ID_EDIT_REDO);
2141         }
2142 }
2143
2144 /**
2145  * @brief Called when "Redo" item is updated
2146  */
2147 void CMergeEditView::OnUpdateEditRedo(CCmdUI* pCmdUI)
2148 {
2149         CMergeDoc* pDoc = GetDocument();
2150         if (pDoc->curUndo!=pDoc->undoTgt.end())
2151         {
2152                 CMergeEditView *tgt = pDoc->GetView(m_nThisGroup, *(pDoc->curUndo));
2153                 pCmdUI->Enable( !IsReadOnly(tgt->m_nThisPane));
2154         }
2155         else
2156                 pCmdUI->Enable(false);
2157 }
2158
2159 void CMergeEditView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint)
2160 {
2161         CCrystalEditViewEx::OnUpdate(pSender, lHint, pHint);
2162 }
2163
2164 /**
2165  * @brief Scrolls to current diff and/or selects diff text
2166  * @param [in] bScroll If true scroll diff to view
2167  * @param [in] bSelectText If true select diff text
2168  * @note If bScroll and bSelectText are false, this does nothing!
2169  * @todo This shouldn't be called when no diff is selected, so
2170  * somebody could try to ASSERT(nDiff > -1)...
2171  */
2172 void CMergeEditView::ShowDiff(bool bScroll, bool bSelectText)
2173 {
2174         CMergeDoc *pd = GetDocument();
2175         const int nDiff = pd->GetCurrentDiff();
2176
2177         // Try to trap some errors
2178         if (nDiff >= pd->m_diffList.GetSize())
2179                 _RPTF2(_CRT_ERROR, "Selected diff > diffcount (%d > %d)!",
2180                         nDiff, pd->m_diffList.GetSize());
2181
2182         if (nDiff >= 0 && nDiff < pd->m_diffList.GetSize())
2183         {
2184                 CPoint ptStart, ptEnd;
2185                 DIFFRANGE curDiff;
2186                 pd->m_diffList.GetDiff(nDiff, curDiff);
2187
2188                 ptStart.x = 0;
2189                 ptStart.y = curDiff.dbegin;
2190                 ptEnd.x = 0;
2191                 ptEnd.y = curDiff.dend;
2192
2193                 if (bScroll)
2194                 {
2195                         if (!IsDiffVisible(curDiff, CONTEXT_LINES_BELOW))
2196                         {
2197                                 // Difference is not visible, scroll it so that max amount of
2198                                 // scrolling is done while keeping the diff in screen. So if
2199                                 // scrolling is downwards, scroll the diff to as up in screen
2200                                 // as possible. This usually brings next diff to the screen
2201                                 // and we don't need to scroll into it.
2202                                 int nLine = GetSubLineIndex(ptStart.y);
2203                                 if (nLine > CONTEXT_LINES_ABOVE)
2204                                 {
2205                                         nLine -= CONTEXT_LINES_ABOVE;
2206                                 }
2207                                 GetGroupView(m_nThisPane)->ScrollToSubLine(nLine);
2208                                 for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2209                                 {
2210                                         if (nPane != m_nThisPane)
2211                                                 GetGroupView(nPane)->ScrollToSubLine(nLine);
2212                                 }
2213                         }
2214                         GetGroupView(m_nThisPane)->SetCursorPos(ptStart);
2215                         GetGroupView(m_nThisPane)->SetAnchor(ptStart);
2216                         GetGroupView(m_nThisPane)->SetSelection(ptStart, ptStart);
2217                         for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2218                         {
2219                                 if (nPane != m_nThisPane)
2220                                 {
2221                                         GetGroupView(nPane)->SetCursorPos(ptStart);
2222                                         GetGroupView(nPane)->SetAnchor(ptStart);
2223                                         GetGroupView(nPane)->SetSelection(ptStart, ptStart);
2224                                 }
2225                         }
2226                 }
2227
2228                 if (bSelectText)
2229                 {
2230                         ptEnd.x = GetLineLength(ptEnd.y);
2231                         SetSelection(ptStart, ptEnd);
2232                         UpdateCaret();
2233                 }
2234                 else
2235                         Invalidate();
2236         }
2237 }
2238
2239
2240 void CMergeEditView::OnTimer(UINT_PTR nIDEvent)
2241 {
2242         // Maybe we want theApp::OnIdle to proceed before processing a timer message
2243         // ...but for this the queue must be empty
2244         // The timer message is a low priority message but the queue is maybe not yet empty
2245         // So we set a flag, wait for OnIdle to proceed, then come back here...
2246         // We come back here with a IDLE_TIMER OnTimer message (send with SendMessage
2247         // not with SetTimer so there is no delay)
2248
2249         // IDT_RESCAN was posted because the app wanted to do a flushAndRescan with some delay
2250
2251         // IDLE_TIMER is the false timer used to come back here after OnIdle
2252         // fTimerWaitingForIdle is a bool to store the commands waiting for idle
2253         // (one normal timer = one flag = one command)
2254
2255         if (nIDEvent == IDT_RESCAN)
2256         {
2257                 KillTimer(IDT_RESCAN);
2258                 fTimerWaitingForIdle |= FLAG_RESCAN_WAITS_FOR_IDLE;
2259                 // notify the app to come back after OnIdle
2260                 theApp.SetNeedIdleTimer();
2261         }
2262
2263         if (nIDEvent == IDLE_TIMER)
2264         {
2265                 // not a real timer, just come back after OnIdle
2266                 // look to flags to know what to do
2267                 if (fTimerWaitingForIdle & FLAG_RESCAN_WAITS_FOR_IDLE)
2268                         GetDocument()->RescanIfNeeded(RESCAN_TIMEOUT/1000);
2269                 fTimerWaitingForIdle = 0;
2270         }
2271
2272         CCrystalEditViewEx::OnTimer(nIDEvent);
2273 }
2274
2275 /**
2276  * @brief Returns if buffer is read-only
2277  * @note This has no any relation to file being read-only!
2278  */
2279 bool CMergeEditView::IsReadOnly(int pane) const
2280 {
2281         return GetDocument()->m_ptBuf[pane]->GetReadOnly() != false;
2282 }
2283
2284 /**
2285  * @brief Called when "Save left (as...)" item is updated
2286  */
2287 void CMergeEditView::OnUpdateFileSaveLeft(CCmdUI* pCmdUI)
2288 {
2289         CMergeDoc *pd = GetDocument();
2290         pCmdUI->Enable(!IsReadOnly(0) && pd->m_ptBuf[0]->IsModified());
2291 }
2292
2293 /**
2294  * @brief Called when "Save middle (as...)" item is updated
2295  */
2296 void CMergeEditView::OnUpdateFileSaveMiddle(CCmdUI* pCmdUI)
2297 {
2298         CMergeDoc *pd = GetDocument();
2299         pCmdUI->Enable(pd->m_nBuffers == 3 && !IsReadOnly(1) && pd->m_ptBuf[1]->IsModified());
2300 }
2301
2302 /**
2303  * @brief Called when "Save right (as...)" item is updated
2304  */
2305 void CMergeEditView::OnUpdateFileSaveRight(CCmdUI* pCmdUI)
2306 {
2307         CMergeDoc *pd = GetDocument();
2308         pCmdUI->Enable(!IsReadOnly(pd->m_nBuffers - 1) && pd->m_ptBuf[pd->m_nBuffers - 1]->IsModified());
2309 }
2310
2311 /**
2312  * @brief Refresh display using text-buffers
2313  * @note This DOES NOT reload files!
2314  */
2315 void CMergeEditView::OnRefresh()
2316 {
2317         CMergeDoc *pd = GetDocument();
2318         ASSERT(pd != nullptr);
2319         pd->FlushAndRescan(true);
2320 }
2321
2322 /**
2323  * @brief Enable/Disable automatic rescanning
2324  */
2325 bool CMergeEditView::EnableRescan(bool bEnable)
2326 {
2327         bool bOldValue = m_bAutomaticRescan;
2328         m_bAutomaticRescan = bEnable;
2329         return bOldValue;
2330 }
2331
2332 /**
2333  * @brief Handle some keys when in merging mode
2334  */
2335 bool CMergeEditView::MergeModeKeyDown(MSG* pMsg)
2336 {
2337         bool bHandled = false;
2338
2339         // Allow default text selection when SHIFT pressed
2340         if (::GetAsyncKeyState(VK_SHIFT))
2341                 return false;
2342
2343         // Allow default editor functions when CTRL pressed
2344         if (::GetAsyncKeyState(VK_CONTROL))
2345                 return false;
2346
2347         // If we are in merging mode (merge with cursor keys)
2348         // handle some keys here
2349         switch (pMsg->wParam)
2350         {
2351         case VK_LEFT:
2352                 OnR2l();
2353                 bHandled = true;
2354                 break;
2355
2356         case VK_RIGHT:
2357                 OnL2r();
2358                 bHandled = true;
2359                 break;
2360
2361         case VK_UP:
2362                 OnPrevdiff();
2363                 bHandled = true;
2364                 break;
2365
2366         case VK_DOWN:
2367                 OnNextdiff();
2368                 bHandled = true;
2369                 break;
2370         }
2371
2372         return bHandled;
2373 }
2374
2375 /**
2376  * @brief Called before messages are translated.
2377  *
2378  * Checks if ESC key was pressed, saves and closes doc.
2379  * Also if in merge mode traps cursor keys.
2380  */
2381 BOOL CMergeEditView::PreTranslateMessage(MSG* pMsg)
2382 {
2383         if (pMsg->message == WM_KEYDOWN)
2384         {
2385                 // If we are in merging mode (merge with cursor keys)
2386                 // handle some keys here
2387                 if (theApp.GetMergingMode())
2388                 {
2389                         bool bHandled = MergeModeKeyDown(pMsg);
2390                         if (bHandled)
2391                                 return false;
2392                 }
2393
2394                 // Close window if user has allowed it from options
2395                 if (pMsg->wParam == VK_ESCAPE)
2396                 {
2397                         bool bCloseWithEsc = GetOptionsMgr()->GetBool(OPT_CLOSE_WITH_ESC);
2398                         if (bCloseWithEsc)
2399                                 GetParentFrame()->PostMessage(WM_CLOSE, 0, 0);
2400                         return false;
2401                 }
2402         }
2403
2404         return CCrystalEditViewEx::PreTranslateMessage(pMsg);
2405 }
2406
2407 /**
2408  * @brief Called when "Save" item is updated
2409  */
2410 void CMergeEditView::OnUpdateFileSave(CCmdUI* pCmdUI)
2411 {
2412         CMergeDoc *pd = GetDocument();
2413
2414         bool bModified = false;
2415         for (int nPane = 0; nPane < pd->m_nBuffers; nPane++)
2416         {
2417                 if (pd->m_ptBuf[nPane]->IsModified())
2418                         bModified = true;
2419         }
2420         pCmdUI->Enable(bModified);
2421 }
2422
2423 /**
2424  * @brief Enable/disable left buffer read-only
2425  */
2426 void CMergeEditView::OnLeftReadOnly()
2427 {
2428         CMergeDoc *pd = GetDocument();
2429         bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2430         pd->m_ptBuf[0]->SetReadOnly(!bReadOnly);
2431 }
2432
2433 /**
2434  * @brief Called when "Left read-only" item is updated
2435  */
2436 void CMergeEditView::OnUpdateLeftReadOnly(CCmdUI* pCmdUI)
2437 {
2438         CMergeDoc *pd = GetDocument();
2439         bool bReadOnly = pd->m_ptBuf[0]->GetReadOnly();
2440         pCmdUI->Enable(true);
2441         pCmdUI->SetCheck(bReadOnly);
2442 }
2443
2444 /**
2445  * @brief Enable/disable middle buffer read-only
2446  */
2447 void CMergeEditView::OnMiddleReadOnly()
2448 {
2449         CMergeDoc *pd = GetDocument();
2450         if (pd->m_nBuffers == 3)
2451         {
2452                 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2453                 pd->m_ptBuf[1]->SetReadOnly(!bReadOnly);
2454         }
2455 }
2456
2457 /**
2458  * @brief Called when "Middle read-only" item is updated
2459  */
2460 void CMergeEditView::OnUpdateMiddleReadOnly(CCmdUI* pCmdUI)
2461 {
2462         CMergeDoc *pd = GetDocument();
2463         if (pd->m_nBuffers < 3)
2464         {
2465                 pCmdUI->Enable(false);
2466         }
2467         else
2468         {
2469                 bool bReadOnly = pd->m_ptBuf[1]->GetReadOnly();
2470                 pCmdUI->Enable(true);
2471                 pCmdUI->SetCheck(bReadOnly);
2472         }
2473 }
2474
2475 /**
2476  * @brief Enable/disable right buffer read-only
2477  */
2478 void CMergeEditView::OnRightReadOnly()
2479 {
2480         CMergeDoc *pd = GetDocument();
2481         bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2482         pd->m_ptBuf[pd->m_nBuffers - 1]->SetReadOnly(!bReadOnly);
2483 }
2484
2485 /**
2486  * @brief Called when "Left read-only" item is updated
2487  */
2488 void CMergeEditView::OnUpdateRightReadOnly(CCmdUI* pCmdUI)
2489 {
2490         CMergeDoc *pd = GetDocument();
2491         bool bReadOnly = pd->m_ptBuf[pd->m_nBuffers - 1]->GetReadOnly();
2492         pCmdUI->Enable(true);
2493         pCmdUI->SetCheck(bReadOnly);
2494 }
2495
2496 /// Store interface we use to display status line info
2497 void CMergeEditView::SetStatusInterface(IMergeEditStatus * piMergeEditStatus)
2498 {
2499         ASSERT(m_piMergeEditStatus == nullptr);
2500         m_piMergeEditStatus = piMergeEditStatus;
2501 }
2502
2503 /**
2504  * @brief Update status bar contents.
2505  */
2506 void CMergeEditView::UpdateStatusbar()
2507 {
2508         OnUpdateCaret();
2509 }
2510
2511 /**
2512  * @brief Update statusbar info, Override from CCrystalTextView
2513  * @note we tab-expand column, but we don't tab-expand char count,
2514  * since we want to show how many chars there are and tab is just one
2515  * character although it expands to several spaces.
2516  */
2517 void CMergeEditView::OnUpdateCaret()
2518 {
2519         if (m_piMergeEditStatus == nullptr || !IsTextBufferInitialized())
2520                 return;
2521
2522         CPoint cursorPos = GetCursorPos();
2523         int nScreenLine = cursorPos.y;
2524         const int nRealLine = ComputeRealLine(nScreenLine);
2525         CString sLine;
2526         int chars = -1;
2527         CString sEol;
2528         int column = -1;
2529         int columns = -1;
2530         int curChar = -1;
2531         DWORD dwLineFlags = 0;
2532
2533         dwLineFlags = m_pTextBuffer->GetLineFlags(nScreenLine);
2534         // Is this a ghost line ?
2535         if (dwLineFlags & LF_GHOST)
2536         {
2537                 // Ghost lines display eg "Line 12-13"
2538                 sLine.Format(_T("%d-%d"), nRealLine, nRealLine+1);
2539                 sEol = _T("hidden");
2540         }
2541         else
2542         {
2543                 // Regular lines display eg "Line 13 Characters: 25 EOL: CRLF"
2544                 sLine.Format(_T("%d"), nRealLine+1);
2545                 curChar = cursorPos.x + 1;
2546                 chars = GetLineLength(nScreenLine);
2547                 column = CalculateActualOffset(nScreenLine, cursorPos.x, true) + 1;
2548                 columns = CalculateActualOffset(nScreenLine, chars, true) + 1;
2549                 chars++;
2550                 if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2551                                 GetDocument()->IsMixedEOL(m_nThisPane))
2552                 {
2553                         sEol = GetTextBufferEol(nScreenLine);
2554                 }
2555                 else
2556                         sEol = _T("hidden");
2557         }
2558         m_piMergeEditStatus->SetLineInfo(sLine, column, columns,
2559                 curChar, chars, sEol, GetDocument()->m_ptBuf[m_nThisPane]->getCodepage(), GetDocument()->m_ptBuf[m_nThisPane]->getHasBom());
2560
2561         // Is cursor inside difference?
2562         if (dwLineFlags & LF_NONTRIVIAL_DIFF)
2563                 m_bCurrentLineIsDiff = true;
2564         else
2565                 m_bCurrentLineIsDiff = false;
2566
2567         UpdateLocationViewPosition(m_nTopSubLine, m_nTopSubLine + GetScreenLines());
2568 }
2569 /**
2570  * @brief Select linedifference in the current line.
2571  *
2572  * Select line difference in current line. Selection type
2573  * is choosed by highlight type.
2574  */
2575 template<bool reversed>
2576 void CMergeEditView::OnSelectLineDiff()
2577 {
2578         // Pass this to the document, to compare this file to other
2579         GetDocument()->Showlinediff(this, reversed);
2580 }
2581
2582 /// Enable select difference menuitem if current line is inside difference.
2583 void CMergeEditView::OnUpdateSelectLineDiff(CCmdUI* pCmdUI)
2584 {
2585         int line = GetCursorPos().y;
2586         bool enable = ((GetLineFlags(line) & (LF_DIFF | LF_GHOST)) != 0);
2587         if (GetDocument()->IsEditedAfterRescan(m_nThisPane))
2588                 enable = false;
2589         pCmdUI->Enable(enable);
2590 }
2591
2592 /**
2593  * @brief Enable/disable Replace-menuitem
2594  */
2595 void CMergeEditView::OnUpdateEditReplace(CCmdUI* pCmdUI)
2596 {
2597         CMergeDoc *pd = GetDocument();
2598         bool bReadOnly = pd->m_ptBuf[m_nThisPane]->GetReadOnly();
2599
2600         pCmdUI->Enable(!bReadOnly);
2601 }
2602
2603 /**
2604  * @brief Update readonly statusbaritem
2605  */
2606 void CMergeEditView::OnUpdateStatusRO(CCmdUI* pCmdUI)
2607 {
2608         bool bRO = GetDocument()->m_ptBuf[pCmdUI->m_nID - ID_STATUS_PANE0FILE_RO]->GetReadOnly();
2609         pCmdUI->Enable(bRO);
2610 }
2611
2612 /**
2613  * @brief Create the dynamic submenu for scripts
2614  */
2615 HMENU CMergeEditView::createScriptsSubmenu(HMENU hMenu)
2616 {
2617         // get scripts list
2618         std::vector<String> functionNamesList = FileTransform::GetFreeFunctionsInScripts(L"EDITOR_SCRIPT");
2619
2620         // empty the menu
2621         size_t i = GetMenuItemCount(hMenu);
2622         while (i --)
2623                 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2624
2625         if (functionNamesList.size() == 0)
2626         {
2627                 // no script : create a <empty> entry
2628                 AppendMenu(hMenu, MF_STRING, ID_NO_EDIT_SCRIPTS, _("< Empty >").c_str());
2629         }
2630         else
2631         {
2632                 // or fill in the submenu with the scripts names
2633                 int ID = ID_SCRIPT_FIRST;       // first ID in menu
2634                 for (i = 0 ; i < functionNamesList.size() ; i++, ID++)
2635                         AppendMenu(hMenu, MF_STRING, ID, functionNamesList[i].c_str());
2636
2637                 functionNamesList.clear();
2638         }
2639
2640         if (!plugin::IsWindowsScriptThere())
2641                 AppendMenu(hMenu, MF_STRING, ID_NO_SCT_SCRIPTS, _("WSH not found - .sct scripts disabled").c_str());
2642
2643         return hMenu;
2644 }
2645
2646 /**
2647  * @brief Create the dynamic submenu for prediffers
2648  *
2649  * @note The plugins are grouped in (suggested) and (not suggested)
2650  *       The IDs follow the order of GetAvailableScripts
2651  *       For example :
2652  *                              suggested 0         ID_1ST + 0 
2653  *                              suggested 1         ID_1ST + 2 
2654  *                              suggested 2         ID_1ST + 5 
2655  *                              not suggested 0     ID_1ST + 1 
2656  *                              not suggested 1     ID_1ST + 3 
2657  *                              not suggested 2     ID_1ST + 4 
2658  */
2659 HMENU CMergeEditView::createPrediffersSubmenu(HMENU hMenu)
2660 {
2661         // empty the menu
2662         int i = GetMenuItemCount(hMenu);
2663         while (i --)
2664                 DeleteMenu(hMenu, 0, MF_BYPOSITION);
2665
2666         CMergeDoc *pd = GetDocument();
2667         ASSERT(pd != nullptr);
2668
2669         // title
2670         AppendMenu(hMenu, MF_STRING, ID_NO_PREDIFFER, _("No prediffer (normal)").c_str());
2671
2672         // get the scriptlet files
2673         PluginArray * piScriptArray = 
2674                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
2675         PluginArray * piScriptArray2 = 
2676                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
2677
2678         // build the menu : first part, suggested plugins
2679         // title
2680         AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2681         AppendMenu(hMenu, MF_STRING, ID_SUGGESTED_PLUGINS, _("Suggested plugins").c_str());
2682
2683         int ID = ID_PREDIFFERS_FIRST;   // first ID in menu
2684         size_t iScript;
2685         for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2686         {
2687                 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2688                 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2689                         continue;
2690
2691                 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2692         }
2693         for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2694         {
2695                 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2696                 if (plugin->m_disabled || !plugin->TestAgainstRegList(pd->m_strBothFilenames))
2697                         continue;
2698
2699                 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2700         }
2701
2702         // build the menu : second part, others plugins
2703         // title
2704         AppendMenu(hMenu, MF_SEPARATOR, 0, nullptr);
2705         AppendMenu(hMenu, MF_STRING, ID_NOT_SUGGESTED_PLUGINS, _("Other plugins").c_str());
2706
2707         ID = ID_PREDIFFERS_FIRST;       // first ID in menu
2708         for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2709         {
2710                 const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2711                 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2712                         continue;
2713
2714                 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2715         }
2716         for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2717         {
2718                 const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2719                 if (plugin->m_disabled || plugin->TestAgainstRegList(pd->m_strBothFilenames) != false)
2720                         continue;
2721
2722                 AppendMenu(hMenu, MF_STRING, ID, plugin->m_name.c_str());
2723         }
2724
2725         // compute the m_CurrentPredifferID (to set the radio button)
2726         PrediffingInfo prediffer;
2727         pd->GetPrediffer(&prediffer);
2728
2729         if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
2730                 m_CurrentPredifferID = 0;
2731         else if (prediffer.m_PluginName.empty())
2732                 m_CurrentPredifferID = ID_NO_PREDIFFER;
2733         else
2734         {
2735                 ID = ID_PREDIFFERS_FIRST;       // first ID in menu
2736                 for (iScript = 0 ; iScript < piScriptArray->size() ; iScript++, ID ++)
2737                 {
2738                         const PluginInfoPtr & plugin = piScriptArray->at(iScript);
2739                         if (prediffer.m_PluginName == plugin->m_name)
2740                                 m_CurrentPredifferID = ID;
2741
2742                 }
2743                 for (iScript = 0 ; iScript < piScriptArray2->size() ; iScript++, ID ++)
2744                 {
2745                         const PluginInfoPtr & plugin = piScriptArray2->at(iScript);
2746                         if (prediffer.m_PluginName == plugin->m_name)
2747                                 m_CurrentPredifferID = ID;
2748                 }
2749         }
2750
2751         return hMenu;
2752 }
2753
2754 /**
2755  * @brief Offer a context menu built with scriptlet/ActiveX functions
2756  */
2757 void CMergeEditView::OnContextMenu(CWnd* pWnd, CPoint point)
2758 {
2759         // Create the menu and populate it with the available functions
2760         BCMenu menu;
2761         VERIFY(menu.LoadMenu(IDR_POPUP_MERGEVIEW));
2762
2763         // Remove copying item copying from active side
2764         if (m_nThisPane == 0) // left?
2765         {
2766                 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2767                 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2768         }
2769         if (m_nThisPane == GetDocument()->m_nBuffers - 1)
2770         {
2771                 menu.RemoveMenu(ID_COPY_FROM_LEFT, MF_BYCOMMAND);
2772                 menu.RemoveMenu(ID_COPY_FROM_RIGHT, MF_BYCOMMAND);
2773         }
2774
2775         VERIFY(menu.LoadToolbar(IDR_MAINFRAME));
2776         theApp.TranslateMenu(menu.m_hMenu);
2777
2778         BCMenu *pSub = static_cast<BCMenu *>(menu.GetSubMenu(0));
2779         ASSERT(pSub != nullptr);
2780
2781         // Context menu opened using keyboard has no coordinates
2782         if (point.x == -1 && point.y == -1)
2783         {
2784                 CRect rect;
2785                 GetClientRect(rect);
2786                 ClientToScreen(rect);
2787
2788                 point = rect.TopLeft();
2789                 point.Offset(5, 5);
2790         }
2791
2792         pSub->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
2793                 point.x, point.y, AfxGetMainWnd());
2794
2795 }
2796
2797 /**
2798  * @brief Update EOL mode in status bar
2799  */
2800 void CMergeEditView::OnUpdateStatusEOL(CCmdUI* pCmdUI)
2801 {
2802         GetGroupView(pCmdUI->m_nID - ID_STATUS_PANE0FILE_EOL)->OnUpdateIndicatorCRLF(pCmdUI);
2803 }
2804
2805 /**
2806  * @brief Change EOL mode and unify all the lines EOL to this new mode
2807  */
2808 void CMergeEditView::OnConvertEolTo(UINT nID )
2809 {
2810         CRLFSTYLE nStyle = CRLF_STYLE_AUTOMATIC;;
2811         switch (nID)
2812         {
2813                 case ID_EOL_TO_DOS:
2814                         nStyle = CRLF_STYLE_DOS;
2815                         break;
2816                 case ID_EOL_TO_UNIX:
2817                         nStyle = CRLF_STYLE_UNIX;
2818                         break;
2819                 case ID_EOL_TO_MAC:
2820                         nStyle = CRLF_STYLE_MAC;
2821                         break;
2822                 default:
2823                         // Catch errors
2824                         _RPTF0(_CRT_ERROR, "Unhandled EOL type conversion!");
2825                         break;
2826         }
2827         m_pTextBuffer->SetCRLFMode(nStyle);
2828
2829         // we don't need a derived applyEOLMode for ghost lines as they have no EOL char
2830         if (m_pTextBuffer->applyEOLMode())
2831         {
2832                 CMergeDoc *pd = GetDocument();
2833                 ASSERT(pd != nullptr);
2834                 pd->UpdateHeaderPath(m_nThisPane);
2835                 pd->FlushAndRescan(true);
2836         }
2837 }
2838
2839 /**
2840  * @brief allow convert to entries in file submenu
2841  */
2842 void CMergeEditView::OnUpdateConvertEolTo(CCmdUI* pCmdUI)
2843 {
2844         int nStyle = CRLF_STYLE_AUTOMATIC;
2845         switch (pCmdUI->m_nID)
2846         {
2847                 case ID_EOL_TO_DOS:
2848                         nStyle = CRLF_STYLE_DOS;
2849                         break;
2850                 case ID_EOL_TO_UNIX:
2851                         nStyle = CRLF_STYLE_UNIX;
2852                         break;
2853                 case ID_EOL_TO_MAC:
2854                         nStyle = CRLF_STYLE_MAC;
2855                         break;
2856                 default:
2857                         // Catch errors
2858                         _RPTF0(_CRT_ERROR, "Missing menuitem handler for EOL convert menu!");
2859                         break;
2860         }
2861
2862         if (GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
2863                 GetDocument()->IsMixedEOL(m_nThisPane) ||
2864                 nStyle != m_pTextBuffer->GetCRLFMode())
2865         {
2866                 pCmdUI->SetRadio(false);
2867
2868                 // Don't allow selecting other EOL style for protected pane
2869                 if (IsReadOnly(m_nThisPane))
2870                         pCmdUI->Enable(false);
2871         }
2872         else
2873                 pCmdUI->SetRadio(true);
2874 }
2875
2876 /**
2877  * @brief Copy diff from left to right and advance to next diff
2878  */
2879 void CMergeEditView::OnL2RNext()
2880 {
2881         OnL2r();
2882         if (IsCursorInDiff()) // for 3-way file compare
2883                 OnNextdiff();
2884         OnNextdiff();
2885 }
2886
2887 /**
2888  * @brief Update "Copy right and advance" UI item
2889  */
2890 void CMergeEditView::OnUpdateL2RNext(CCmdUI* pCmdUI)
2891 {
2892         OnUpdateL2r(pCmdUI);
2893 }
2894
2895 /**
2896  * @brief Copy diff from right to left and advance to next diff
2897  */
2898 void CMergeEditView::OnR2LNext()
2899 {
2900         OnR2l();
2901         if (IsCursorInDiff()) // for 3-way file compare
2902                 OnNextdiff();
2903         OnNextdiff();
2904 }
2905
2906 /**
2907  * @brief Update "Copy left and advance" UI item
2908  */
2909 void CMergeEditView::OnUpdateR2LNext(CCmdUI* pCmdUI)
2910 {
2911         OnUpdateR2l(pCmdUI);
2912 }
2913
2914 /**
2915  * @brief Change active pane in MergeView.
2916  * Changes active pane and makes sure cursor position is kept in
2917  * screen. Currently we put cursor in same line than in original
2918  * active pane but we could be smarter too? Maybe update cursor
2919  * only when it is not visible in new pane?
2920  */
2921 void CMergeEditView::OnChangePane()
2922 {
2923         CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
2924         CMergeEditView *pWnd = static_cast<CMergeEditView*>(pSplitterWnd->GetActivePane());
2925         CMergeDoc *pDoc = GetDocument();
2926         bool bFound = false;
2927         CMergeEditView *pNextActiveView = nullptr;
2928         std::vector<CMergeEditView *> list = pDoc->GetViewList();
2929         list.insert(list.end(), list.begin(), list.end());
2930         for (auto& pView : list)
2931         {
2932                 if (bFound && pView->m_bDetailView == pWnd->m_bDetailView)
2933                 {
2934                         pNextActiveView = pView;
2935                         break;
2936                 }
2937                 if (pWnd == pView)
2938                         bFound = true;
2939         }
2940         GetParentFrame()->SetActiveView(pNextActiveView);
2941         CPoint ptCursor = pWnd->GetCursorPos();
2942         ptCursor.x = 0;
2943         if (ptCursor.y >= pNextActiveView->GetLineCount())
2944                 ptCursor.y = pNextActiveView->GetLineCount() - 1;
2945         pNextActiveView->SetCursorPos(ptCursor);
2946         pNextActiveView->SetAnchor(ptCursor);
2947         pNextActiveView->SetSelection(ptCursor, ptCursor);
2948 }
2949
2950 /**
2951  * @brief Show "Go To" dialog and scroll views to line or diff.
2952  *
2953  * Before dialog is opened, current line and file is determined
2954  * and selected.
2955  * @note Conversions needed between apparent and real lines
2956  */
2957 void CMergeEditView::OnWMGoto()
2958 {
2959         WMGotoDlg dlg;
2960         CMergeDoc *pDoc = GetDocument();
2961         CPoint pos = GetCursorPos();
2962         int nRealLine = 0;
2963         int nLastLine = 0;
2964
2965         nRealLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(pos.y);
2966         int nLineCount = pDoc->m_ptBuf[m_nThisPane]->GetLineCount();
2967         nLastLine = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(nLineCount - 1);
2968
2969         // Set active file and current line selected in dialog
2970         dlg.m_strParam = strutils::to_str(nRealLine + 1);
2971         dlg.m_nFile = (pDoc->m_nBuffers < 3) ? (m_nThisPane == 1 ? 2 : 0) : m_nThisPane;
2972         dlg.m_nGotoWhat = 0;
2973
2974         if (dlg.DoModal() == IDOK)
2975         {
2976                 CMergeDoc * pDoc1 = GetDocument();
2977                 CMergeEditView * pCurrentView = nullptr;
2978
2979                 // Get views
2980                 pCurrentView = GetGroupView(m_nThisPane);
2981
2982                 int num = 0;
2983                 try { num = std::stoi(dlg.m_strParam) - 1; } catch(...) {}
2984
2985                 if (dlg.m_nGotoWhat == 0)
2986                 {
2987                         int nRealLine1 = num;
2988                         if (nRealLine1 < 0)
2989                                 nRealLine1 = 0;
2990                         if (nRealLine1 > nLastLine)
2991                                 nRealLine1 = nLastLine;
2992
2993                         GotoLine(nRealLine1, true, (pDoc1->m_nBuffers < 3) ? (dlg.m_nFile == 2 ? 1 : 0) : dlg.m_nFile);
2994                 }
2995                 else
2996                 {
2997                         int diff = num;
2998                         if (diff < 0)
2999                                 diff = 0;
3000                         if (diff >= pDoc1->m_diffList.GetSize())
3001                                 diff = pDoc1->m_diffList.GetSize();
3002
3003                         pCurrentView->SelectDiff(diff, true, false);
3004                 }
3005         }
3006 }
3007
3008 void CMergeEditView::OnShellMenu()
3009 {
3010         CFrameWnd *pFrame = GetTopLevelFrame();
3011         ASSERT(pFrame != nullptr);
3012         BOOL bAutoMenuEnableOld = pFrame->m_bAutoMenuEnable;
3013         pFrame->m_bAutoMenuEnable = FALSE;
3014
3015         String path = GetDocument()->m_filePaths[m_nThisPane];
3016         std::unique_ptr<CShellContextMenu> pContextMenu(new CShellContextMenu(0x9000, 0x9FFF));
3017         pContextMenu->Initialize();
3018         pContextMenu->AddItem(paths::GetParentPath(path), paths::FindFileName(path));
3019         pContextMenu->RequeryShellContextMenu();
3020         CPoint point;
3021         ::GetCursorPos(&point);
3022         HWND hWnd = GetSafeHwnd();
3023         BOOL nCmd = TrackPopupMenu(pContextMenu->GetHMENU(), TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, 0, hWnd, nullptr);
3024         if (nCmd)
3025                 pContextMenu->InvokeCommand(nCmd, hWnd);
3026         pContextMenu->ReleaseShellContextMenu();
3027
3028         pFrame->m_bAutoMenuEnable = bAutoMenuEnableOld;
3029 }
3030
3031 void CMergeEditView::OnUpdateShellMenu(CCmdUI* pCmdUI)
3032 {
3033         pCmdUI->Enable(!GetDocument()->m_filePaths[m_nThisPane].empty());
3034 }
3035
3036 /**
3037  * @brief Reload options.
3038  */
3039 void CMergeEditView::RefreshOptions()
3040
3041         m_bAutomaticRescan = GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN);
3042
3043         if (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0)
3044                 SetInsertTabs(true);
3045         else
3046                 SetInsertTabs(false);
3047
3048         SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3049
3050         if (!GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT))
3051                 SetTextType(CCrystalTextView::SRC_PLAIN);
3052
3053         SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3054         SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3055
3056         SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3057         SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE),
3058                 GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3059                 GetDocument()->IsMixedEOL(m_nThisPane));
3060
3061         Options::DiffColors::Load(GetOptionsMgr(), m_cachedColors);
3062 }
3063
3064 void CMergeEditView::OnScripts(UINT nID )
3065 {
3066         // text is CHAR if compiled without UNICODE, WCHAR with UNICODE
3067         String text = GetSelectedText();
3068
3069         // transform the text with a script/ActiveX function, event=EDITOR_SCRIPT
3070         bool bChanged = FileTransform::Interactive(text, L"EDITOR_SCRIPT", nID - ID_SCRIPT_FIRST);
3071         if (bChanged)
3072                 // now replace the text
3073                 ReplaceSelection(text.c_str(), text.length(), 0);
3074 }
3075
3076 /**
3077  * @brief Called when an editor script item is updated
3078  */
3079 void CMergeEditView::OnUpdateNoEditScripts(CCmdUI* pCmdUI)
3080 {
3081         // append the scripts submenu
3082         HMENU scriptsSubmenu = dynamic_cast<CMainFrame*>(AfxGetMainWnd())->GetScriptsSubmenu(AfxGetMainWnd()->GetMenu()->m_hMenu);
3083         if (scriptsSubmenu != nullptr)
3084                 createScriptsSubmenu(scriptsSubmenu);
3085
3086         pCmdUI->Enable(true);
3087 }
3088
3089 /**
3090  * @brief Called when an editor script item is updated
3091  */
3092 void CMergeEditView::OnUpdatePrediffer(CCmdUI* pCmdUI)
3093 {
3094         pCmdUI->Enable(true);
3095
3096         CMergeDoc *pd = GetDocument();
3097         ASSERT(pd != nullptr);
3098         PrediffingInfo prediffer;
3099         pd->GetPrediffer(&prediffer);
3100
3101         if (prediffer.m_PluginOrPredifferMode != PLUGIN_MANUAL)
3102         {
3103                 pCmdUI->SetRadio(false);
3104                 return;
3105         }
3106
3107         // Detect when CDiffWrapper::RunFileDiff has canceled a buggy prediffer
3108         if (prediffer.m_PluginName.empty())
3109                 m_CurrentPredifferID = ID_NO_PREDIFFER;
3110
3111         pCmdUI->SetRadio(pCmdUI->m_nID == static_cast<UINT>(m_CurrentPredifferID));
3112 }
3113
3114 /**
3115  * @brief Update "Prediffer" menuitem
3116  */
3117 void CMergeEditView::OnUpdateNoPrediffer(CCmdUI* pCmdUI)
3118 {
3119         // recreate the sub menu (to fill the "selected prediffers")
3120         GetMainFrame()->UpdatePrediffersMenu();
3121         pCmdUI->Enable();
3122 }
3123
3124 void CMergeEditView::OnNoPrediffer()
3125 {
3126         OnPrediffer(ID_NO_PREDIFFER);
3127 }
3128 /**
3129  * @brief Handler for all prediffer choices, including ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO, ID_NO_PREDIFFER, & specific prediffers
3130  */
3131 void CMergeEditView::OnPrediffer(UINT nID )
3132 {
3133         CMergeDoc *pd = GetDocument();
3134         ASSERT(pd != nullptr);
3135
3136         SetPredifferByMenu(nID);
3137         pd->FlushAndRescan(true);
3138 }
3139
3140 /**
3141  * @brief Handler for all prediffer choices.
3142  * Prediffer choises include ID_PREDIFF_MANUAL, ID_PREDIFF_AUTO,
3143  * ID_NO_PREDIFFER, & specific prediffers.
3144  */
3145 void CMergeEditView::SetPredifferByMenu(UINT nID )
3146 {
3147         CMergeDoc *pd = GetDocument();
3148         ASSERT(pd != nullptr);
3149
3150         if (nID == ID_NO_PREDIFFER)
3151         {
3152                 m_CurrentPredifferID = nID;
3153                 // All flags are set correctly during the construction
3154                 PrediffingInfo *infoPrediffer = new PrediffingInfo;
3155                 infoPrediffer->m_PluginOrPredifferMode = PLUGIN_MANUAL;
3156                 infoPrediffer->m_PluginName.clear();
3157                 pd->SetPrediffer(infoPrediffer);
3158                 pd->FlushAndRescan(true);
3159                 return;
3160         }
3161
3162         // get the scriptlet files
3163         PluginArray * piScriptArray = 
3164                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3165         PluginArray * piScriptArray2 = 
3166                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3167
3168         // build a PrediffingInfo structure fom the ID
3169         PrediffingInfo prediffer;
3170         prediffer.m_PluginOrPredifferMode = PLUGIN_MANUAL;
3171
3172         size_t pluginNumber = nID - ID_PREDIFFERS_FIRST;
3173         if (pluginNumber < piScriptArray->size())
3174         {
3175                 prediffer.m_bWithFile = true;
3176                 const PluginInfoPtr & plugin = piScriptArray->at(pluginNumber);
3177                 prediffer.m_PluginName = plugin->m_name;
3178         }
3179         else
3180         {
3181                 pluginNumber -= piScriptArray->size();
3182                 if (pluginNumber >= piScriptArray2->size())
3183                         return;
3184                 prediffer.m_bWithFile = false;
3185                 const PluginInfoPtr & plugin = piScriptArray2->at(pluginNumber);
3186                 prediffer.m_PluginName = plugin->m_name;
3187         }
3188
3189         // update data for the radio button
3190         m_CurrentPredifferID = nID;
3191
3192         // update the prediffer and rescan
3193         pd->SetPrediffer(&prediffer);
3194 }
3195
3196 /**
3197  * @brief Look through available prediffers, and return ID of requested one, if found
3198  */
3199 int CMergeEditView::FindPrediffer(LPCTSTR prediffer) const
3200 {
3201         size_t i;
3202         int ID = ID_PREDIFFERS_FIRST;
3203
3204         // Search file prediffers
3205         PluginArray * piScriptArray = 
3206                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"FILE_PREDIFF");
3207         for (i=0; i<piScriptArray->size(); ++i, ++ID)
3208         {
3209                 const PluginInfoPtr & plugin = piScriptArray->at(i);
3210                 if (plugin->m_name == prediffer)
3211                         return ID;
3212         }
3213
3214         // Search buffer prediffers
3215         PluginArray * piScriptArray2 = 
3216                 CAllThreadsScripts::GetActiveSet()->GetAvailableScripts(L"BUFFER_PREDIFF");
3217         for (i=0; i<piScriptArray2->size(); ++i, ++ID)
3218         {
3219                 const PluginInfoPtr & plugin = piScriptArray2->at(i);
3220                 if (plugin->m_name == prediffer)
3221                         return ID;
3222         }
3223         return -1;
3224 }
3225
3226
3227 /**
3228  * @brief Look through available prediffers, and return ID of requested one, if found
3229  */
3230 bool CMergeEditView::SetPredifferByName(const CString & prediffer)
3231 {
3232         int id = FindPrediffer(prediffer);
3233         if (id<0) return false;
3234         SetPredifferByMenu(id);
3235         return true;
3236 }
3237
3238 /** 
3239  * @brief Goto given line.
3240  * @param [in] nLine Destination linenumber
3241  * @param [in] bRealLine if true linenumber is real line, otherwise
3242  * it is apparent line (including deleted lines)
3243  * @param [in] pane Pane index of goto target pane (0 = left, 1 = right).
3244  */
3245 void CMergeEditView::GotoLine(UINT nLine, bool bRealLine, int pane)
3246 {
3247         CMergeDoc *pDoc = GetDocument();
3248         CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3249         CMergeEditView *pCurrentView = nullptr;
3250         if (pSplitterWnd != nullptr)
3251                 pCurrentView = static_cast<CMergeEditView*>
3252                         (pSplitterWnd->GetActivePane());
3253
3254         int nRealLine = nLine;
3255         int nApparentLine = nLine;
3256
3257         // Compute apparent (shown linenumber) line
3258         if (bRealLine)
3259         {
3260                 if (nRealLine > pDoc->m_ptBuf[pane]->GetLineCount() - 1)
3261                         nRealLine = pDoc->m_ptBuf[pane]->GetLineCount() - 1;
3262
3263                 nApparentLine = pDoc->m_ptBuf[pane]->ComputeApparentLine(nRealLine);
3264         }
3265         CPoint ptPos;
3266         ptPos.x = 0;
3267         ptPos.y = nApparentLine;
3268
3269         // Scroll line to center of view
3270         int nScrollLine = GetSubLineIndex(nApparentLine);
3271         nScrollLine -= GetScreenLines() / 2;
3272         if (nScrollLine < 0)
3273                 nScrollLine = 0;
3274
3275         for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++)
3276         {
3277                 CMergeEditView *pView = GetGroupView(nPane);
3278                 pView->ScrollToSubLine(nScrollLine);
3279                 if (ptPos.y < pView->GetLineCount())
3280                 {
3281                         pView->SetCursorPos(ptPos);
3282                         pView->SetAnchor(ptPos);
3283                 }
3284                 else
3285                 {
3286                         CPoint ptPos1(0, pView->GetLineCount() - 1);
3287                         pView->SetCursorPos(ptPos1);
3288                         pView->SetAnchor(ptPos1);
3289                 }
3290         }
3291
3292         // If goto target is another view - activate another view.
3293         // This is done for user convenience as user probably wants to
3294         // work with goto target file.
3295         if (GetGroupView(pane) != pCurrentView)
3296         {
3297                 if (pSplitterWnd != nullptr)
3298                 {
3299                         if (pSplitterWnd->GetColumnCount() > 1)
3300                                 pSplitterWnd->SetActivePane(0, pane);
3301                         else
3302                                 pSplitterWnd->SetActivePane(pane, 0);
3303                 }
3304         }
3305 }
3306
3307 /**
3308  * @brief Check for horizontal scroll. Re-route to CSplitterEx if not from
3309  * a scroll bar.
3310  */
3311 void CMergeEditView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3312 {
3313         if (pScrollBar == nullptr)
3314         {
3315                 // Scroll did not come frome a scroll bar
3316                 // Find the appropriate scroll bar
3317                 // and send the message to the splitter window instead
3318                 // The event should eventually come back here but with a valid scrollbar
3319                 // Along the way it will be propagated to other windows that need it
3320                 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3321                 CScrollBar* curBar = this->GetScrollBarCtrl(SB_HORZ);
3322                 pSplitterWnd->SendMessage(WM_HSCROLL,
3323                         MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3324                 return;
3325         }
3326         CCrystalTextView::OnHScroll (nSBCode, nPos, pScrollBar);
3327 }
3328
3329 /**
3330  * @brief When view is scrolled using scrollbars update location pane.
3331  */
3332 void CMergeEditView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar)
3333 {
3334         if (pScrollBar == nullptr)
3335         {
3336                 // Scroll did not come frome a scroll bar
3337                 // Find the appropriate scroll bar
3338                 // and send the message to the splitter window instead
3339                 // The event should eventually come back here but with a valid scrollbar
3340                 // Along the way it will be propagated to other windows that need it
3341                 CSplitterWnd *pSplitterWnd = GetParentSplitter(this, false);
3342                 CScrollBar* curBar = this->GetScrollBarCtrl(SB_VERT);
3343                 pSplitterWnd->SendMessage(WM_VSCROLL,
3344                         MAKELONG(nSBCode, nPos), (LPARAM)curBar->m_hWnd);
3345                 return;
3346         }
3347         CCrystalTextView::OnVScroll (nSBCode, nPos, pScrollBar);
3348
3349         if (nSBCode == SB_ENDSCROLL)
3350                 return;
3351
3352         // Note we cannot use nPos because of its 16-bit nature
3353         SCROLLINFO si = {0};
3354         si.cbSize = sizeof (si);
3355         si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_TRACKPOS;
3356         VERIFY (GetScrollInfo (SB_VERT, &si));
3357
3358         // Get the current position of scroll   box.
3359         int nCurPos =   si.nPos;
3360         
3361         UpdateLocationViewPosition(nCurPos, nCurPos + GetScreenLines());
3362 }
3363
3364 /**
3365  * @brief Copy selected lines adding linenumbers.
3366  */
3367 void CMergeEditView::OnEditCopyLineNumbers()
3368 {
3369         CPoint ptStart;
3370         CPoint ptEnd;
3371         CString strText;
3372         CString strLine;
3373         CString strNumLine;
3374
3375         CMergeDoc *pDoc = GetDocument();
3376         GetSelection(ptStart, ptEnd);
3377
3378         // Get last selected line (having widest linenumber)
3379         int line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(ptEnd.y);
3380         size_t nNumWidth = strutils::to_str(line + 1).length();
3381         
3382         for (int i = ptStart.y; i <= ptEnd.y; i++)
3383         {
3384                 if (GetLineFlags(i) & LF_GHOST || (GetEnableHideLines() && (GetLineFlags(i) & LF_INVISIBLE)))
3385                         continue;
3386
3387                 // We need to convert to real linenumbers
3388                 line = pDoc->m_ptBuf[m_nThisPane]->ComputeRealLine(i);
3389
3390                 // Insert spaces to align different width linenumbers (99, 100)
3391                 strLine = GetLineText(i);
3392                 CString sSpaces(' ', static_cast<int>(nNumWidth - strutils::to_str(line + 1).length()));
3393                 
3394                 strText += sSpaces;
3395                 strNumLine.Format(_T("%d: %s"), line + 1, (LPCTSTR)strLine);
3396                 strText += strNumLine;
3397         }
3398         PutToClipboard(strText, strText.GetLength(), m_bColumnSelection);
3399 }
3400
3401 void CMergeEditView::OnUpdateEditCopyLinenumbers(CCmdUI* pCmdUI)
3402 {
3403         CCrystalEditViewEx::OnUpdateEditCopy(pCmdUI);
3404 }
3405
3406 /**
3407  * @brief Open active file with associated application.
3408  *
3409  * First tries to open file using shell 'Edit' action, since that
3410  * action open scripts etc. to editor instead of running them. If
3411  * edit-action is not registered, 'Open' action is used.
3412  */
3413 void CMergeEditView::OnOpenFile()
3414 {
3415         CMergeDoc * pDoc = GetDocument();
3416         ASSERT(pDoc != nullptr);
3417
3418         String sFileName = pDoc->m_filePaths[m_nThisPane];
3419         if (sFileName.empty())
3420                 return;
3421         HINSTANCE rtn = ShellExecute(::GetDesktopWindow(), _T("edit"), sFileName.c_str(),
3422                         0, 0, SW_SHOWNORMAL);
3423         if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3424                 rtn = ShellExecute(::GetDesktopWindow(), _T("open"), sFileName.c_str(),
3425                          0, 0, SW_SHOWNORMAL);
3426         if (reinterpret_cast<uintptr_t>(rtn) == SE_ERR_NOASSOC)
3427                 OnOpenFileWith();
3428 }
3429
3430 /**
3431  * @brief Open active file with app selection dialog
3432  */
3433 void CMergeEditView::OnOpenFileWith()
3434 {
3435         CMergeDoc * pDoc = GetDocument();
3436         ASSERT(pDoc != nullptr);
3437
3438         String sFileName = pDoc->m_filePaths[m_nThisPane];
3439         if (sFileName.empty())
3440                 return;
3441
3442         CString sysdir;
3443         if (!GetSystemDirectory(sysdir.GetBuffer(MAX_PATH), MAX_PATH))
3444                 return;
3445         sysdir.ReleaseBuffer();
3446         CString arg = (CString)_T("shell32.dll,OpenAs_RunDLL ") + sFileName.c_str();
3447         ShellExecute(::GetDesktopWindow(), 0, _T("RUNDLL32.EXE"), arg,
3448                         sysdir, SW_SHOWNORMAL);
3449 }
3450
3451 /**
3452  * @brief Open active file with external editor
3453  */
3454 void CMergeEditView::OnOpenFileWithEditor()
3455 {
3456         CMergeDoc * pDoc = GetDocument();
3457         ASSERT(pDoc != nullptr);
3458
3459         String sFileName = pDoc->m_filePaths[m_nThisPane];
3460         if (sFileName.empty())
3461                 return;
3462
3463         int nRealLine = ComputeRealLine(GetCursorPos().y) + 1;
3464         theApp.OpenFileToExternalEditor(sFileName, nRealLine);
3465 }
3466
3467 /**
3468  * @brief Force repaint of the location pane.
3469  */
3470 void CMergeEditView::RepaintLocationPane()
3471 {
3472         // Must force recalculation due to caching of data in location pane.
3473         CLocationView *pLocationView = GetDocument()->GetLocationView();
3474         if (pLocationView != nullptr)
3475                 pLocationView->ForceRecalculate();
3476 }
3477
3478 /**
3479  * @brief Enables/disables linediff (different color for diffs)
3480  */
3481 void CMergeEditView::OnViewLineDiffs()
3482 {
3483         bool bWordDiffHighlight = GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT);
3484         GetOptionsMgr()->SaveOption(OPT_WORDDIFF_HIGHLIGHT, !bWordDiffHighlight);
3485
3486         // Call CMergeDoc RefreshOptions() to refresh *both* views
3487         CMergeDoc *pDoc = GetDocument();
3488         pDoc->RefreshOptions();
3489         pDoc->FlushAndRescan(true);
3490 }
3491
3492 void CMergeEditView::OnUpdateViewLineDiffs(CCmdUI* pCmdUI)
3493 {
3494         pCmdUI->Enable(true);
3495         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_WORDDIFF_HIGHLIGHT));
3496 }
3497
3498 /**
3499  * @brief Enables/disables line number
3500  */
3501 void CMergeEditView::OnViewLineNumbers()
3502 {
3503         GetOptionsMgr()->SaveOption(OPT_VIEW_LINENUMBERS, !GetViewLineNumbers());
3504
3505         // Call CMergeDoc RefreshOptions() to refresh *both* views
3506         CMergeDoc *pDoc = GetDocument();
3507         pDoc->RefreshOptions();
3508 }
3509
3510 void CMergeEditView::OnUpdateViewLineNumbers(CCmdUI* pCmdUI)
3511 {
3512         pCmdUI->Enable(true);
3513         pCmdUI->SetCheck(GetViewLineNumbers());
3514 }
3515
3516 /**
3517  * @brief Enables/disables word wrap
3518  */
3519 void CMergeEditView::OnViewWordWrap()
3520 {
3521         GetOptionsMgr()->SaveOption(OPT_WORDWRAP, !m_bWordWrap);
3522
3523         // Call CMergeDoc RefreshOptions() to refresh *both* views
3524         CMergeDoc *pDoc = GetDocument();
3525         pDoc->RefreshOptions();
3526         pDoc->UpdateAllViews(this);
3527
3528         UpdateCaret();
3529 }
3530
3531 void CMergeEditView::OnUpdateViewWordWrap(CCmdUI* pCmdUI)
3532 {
3533         pCmdUI->Enable(true);
3534         pCmdUI->SetCheck(m_bWordWrap);
3535 }
3536
3537 void CMergeEditView::OnViewWhitespace() 
3538 {
3539         GetOptionsMgr()->SaveOption(OPT_VIEW_WHITESPACE, !GetViewTabs());
3540
3541         // Call CMergeDoc RefreshOptions() to refresh *both* views
3542         CMergeDoc *pDoc = GetDocument();
3543         pDoc->RefreshOptions();
3544 }
3545
3546 void CMergeEditView::OnUpdateViewWhitespace(CCmdUI* pCmdUI) 
3547 {
3548         pCmdUI->SetCheck(GetViewTabs());
3549 }
3550
3551 void CMergeEditView::OnSize(UINT nType, int cx, int cy) 
3552 {
3553         if (!IsInitialized())
3554                 return;
3555
3556         CMergeDoc * pDoc = GetDocument();
3557         if (m_nThisPane < pDoc->m_nBuffers - 1)
3558         {
3559                 // To calculate subline index correctly
3560                 // we have to invalidate line cache in all pane before calling the function related the subline.
3561                 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++) 
3562                 {
3563                         CMergeEditView *pView = GetGroupView(nPane);
3564                         if (pView != nullptr)
3565                                 pView->InvalidateScreenRect(false);
3566                 }
3567         }
3568         else
3569         {
3570                 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++) 
3571                 {
3572                         CMergeEditView *pView = GetGroupView(nPane);
3573                         if (pView != nullptr)
3574                                 pView->Invalidate();
3575                 }
3576         }
3577         // recalculate m_nTopSubLine
3578         m_nTopSubLine = GetSubLineIndex(m_nTopLine);
3579
3580         UpdateCaret();
3581         
3582         RecalcVertScrollBar (false, false);
3583         RecalcHorzScrollBar (false, false);
3584 }
3585
3586 /**
3587 * @brief allocates GDI resources for printing
3588 * @param pDC [in] points to the printer device context
3589 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3590 */
3591 void CMergeEditView::OnBeginPrinting(CDC * pDC, CPrintInfo * pInfo)
3592 {
3593         GetParentFrame()->PostMessage(WM_TIMER);
3594
3595         for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3596         {
3597                 CMergeEditView *pView = GetDocument()->GetView(m_nThisGroup, pane);
3598                 pView->m_bPrintHeader = true;
3599                 pView->m_bPrintFooter = true;
3600                 pView->CGhostTextView::OnBeginPrinting(pDC, pInfo);
3601         }
3602 }
3603
3604 /**
3605 * @brief frees GDI resources for printing
3606 * @param pDC [in] points to the printer device context
3607 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3608 */
3609 void CMergeEditView::OnEndPrinting(CDC * pDC, CPrintInfo * pInfo)
3610 {
3611         for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3612                 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::OnEndPrinting(pDC, pInfo);
3613
3614         GetParentFrame()->PostMessage(WM_TIMER);
3615 }
3616
3617 /**
3618 * @brief Gets header text to print
3619 * @param [in]  nPageNum the page number to print
3620 * @param [out] header text to print
3621 */
3622 void CMergeEditView::GetPrintHeaderText(int nPageNum, CString & text)
3623 {
3624         text = GetDocument()->GetTitle();
3625 }
3626
3627 /**
3628 * @brief Prints header
3629 * @param [in] nPageNum the page number to print
3630 */
3631 void CMergeEditView::PrintHeader(CDC * pdc, int nPageNum)
3632 {
3633         if (m_nThisPane > 0)
3634                 return;
3635         int oldRight = m_rcPrintArea.right;
3636         m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3637         CGhostTextView::PrintHeader(pdc, nPageNum);
3638         m_rcPrintArea.right = oldRight;
3639 }
3640
3641 /**
3642 * @brief Prints footer
3643 * @param [in] nPageNum the page number to print
3644 */
3645 void CMergeEditView::PrintFooter(CDC * pdc, int nPageNum)
3646 {
3647         if (m_nThisPane > 0)
3648                 return;
3649         int oldRight = m_rcPrintArea.right;
3650         m_rcPrintArea.right += m_rcPrintArea.Width() * (GetDocument()->m_nBuffers - 1);
3651         CGhostTextView::PrintFooter(pdc, nPageNum);
3652         m_rcPrintArea.right = oldRight;
3653 }
3654
3655 void CMergeEditView::RecalcPageLayouts (CDC * pDC, CPrintInfo * pInfo)
3656 {
3657         for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3658                 GetDocument()->GetView(m_nThisGroup, pane)->CGhostTextView::RecalcPageLayouts(pDC, pInfo);
3659 }
3660
3661 /**
3662 * @brief Prints or previews both panes.
3663 * @param pDC [in] points to the printer device context
3664 * @param pInfo [in] points to a CPrintInfo structure that describes the current print job
3665 */
3666 void CMergeEditView::OnPrint(CDC* pDC, CPrintInfo* pInfo) 
3667 {
3668         CRect rDraw = pInfo->m_rectDraw;
3669         CSize sz = rDraw.Size();
3670         CMergeDoc *pDoc = GetDocument();
3671
3672         SIZE szLeftTop, szRightBottom;
3673         GetPrintMargins(szLeftTop.cx, szLeftTop.cy, szRightBottom.cx, szRightBottom.cy);
3674         pDC->HIMETRICtoLP(&szLeftTop);
3675         pDC->HIMETRICtoLP(&szRightBottom);
3676         
3677         int midX = (sz.cx - szLeftTop.cx - szRightBottom.cx) / pDoc->m_nBuffers;
3678
3679         // print pane
3680         for (int pane = 0; pane < pDoc->m_nBuffers; pane++)
3681         {
3682                 pInfo->m_rectDraw.left = rDraw.left + midX * pane;
3683                 pInfo->m_rectDraw.right = pInfo->m_rectDraw.left + midX + szLeftTop.cx + szRightBottom.cx;
3684                 CMergeEditView* pPane = pDoc->GetView(m_nThisGroup, pane);
3685                 pPane->CGhostTextView::OnPrint(pDC, pInfo);
3686         }
3687 }
3688
3689 bool CMergeEditView::IsInitialized() const
3690 {
3691         CMergeEditView * pThis = const_cast<CMergeEditView *>(this);
3692         CDiffTextBuffer * pBuffer = dynamic_cast<CDiffTextBuffer *>(pThis->LocateTextBuffer());
3693         return pBuffer->IsInitialized();
3694 }
3695
3696 /**
3697  * @brief returns the number of empty lines which are added for synchronizing the line in two/three panes.
3698  */
3699 int CMergeEditView::GetEmptySubLines( int nLineIndex )
3700 {
3701         int     nBreaks[3] = {0};
3702         int nMaxBreaks = -1;
3703         CMergeDoc * pDoc = GetDocument();
3704         for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++) 
3705         {
3706                 CMergeEditView *pView = GetGroupView(nPane);
3707                 if (pView != nullptr)
3708                 {
3709                         if (nLineIndex >= pView->GetLineCount())
3710                                 return 0;
3711                         pView->WrapLineCached( nLineIndex, pView->GetScreenChars(), nullptr, nBreaks[nPane] );
3712                 }
3713                 nMaxBreaks = max(nMaxBreaks, nBreaks[nPane]);
3714         }
3715
3716         if (nBreaks[m_nThisPane] < nMaxBreaks)
3717                 return nMaxBreaks - nBreaks[m_nThisPane];
3718         else
3719                 return 0;
3720 }
3721
3722 /**
3723  * @brief Invalidate sub line index cache from the specified index to the end of file.
3724  * @param [in] nLineIndex Index of the first line to invalidate 
3725  */
3726 void CMergeEditView::InvalidateSubLineIndexCache( int nLineIndex )
3727 {
3728         CMergeDoc * pDoc = GetDocument();
3729         ASSERT(pDoc != nullptr);
3730
3731     // We have to invalidate sub line index cache on both panes.
3732         for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++) 
3733         {
3734                 CMergeEditView *pView = GetGroupView(nPane);
3735                 if (pView != nullptr)
3736                         pView->CCrystalTextView::InvalidateSubLineIndexCache( nLineIndex );
3737         }
3738 }
3739
3740 void CMergeEditView::SetWordWrapping( bool bWordWrap )
3741 {
3742         for (int pane = 0; pane < GetDocument()->m_nBuffers; pane++)
3743                 GetGroupView(pane)->m_bWordWrap = bWordWrap;
3744         CCrystalTextView::SetWordWrapping(bWordWrap);
3745 }
3746
3747 /**
3748  * @brief Swap the positions of the two panes
3749  */
3750 void CMergeEditView::OnViewSwapPanes()
3751 {
3752         GetDocument()->SwapFiles();
3753 }
3754
3755 /**
3756  * @brief Check if cursor is inside difference.
3757  * @return true if cursor is inside difference.
3758  */
3759 bool CMergeEditView::IsCursorInDiff() const
3760 {
3761         return m_bCurrentLineIsDiff;
3762 }
3763
3764 /**
3765 * @brief Determine if difference is visible on screen.
3766 * @param [in] nDiff Number of diff to check.
3767 * @return true if difference is visible.
3768 */
3769 bool CMergeEditView::IsDiffVisible(int nDiff)
3770 {
3771         const CMergeDoc *pd = GetDocument();
3772
3773         DIFFRANGE diff;
3774         pd->m_diffList.GetDiff(nDiff, diff);
3775
3776         return IsDiffVisible(diff);
3777 }
3778
3779 /**
3780  * @brief Determine if difference is visible on screen.
3781  * @param [in] diff diff to check.
3782  * @param [in] nLinesBelow Allow "minimizing" the number of visible lines.
3783  * @return true if difference is visible, false otherwise.
3784  */
3785 bool CMergeEditView::IsDiffVisible(const DIFFRANGE& diff, int nLinesBelow /*=0*/)
3786 {
3787         const int nDiffStart = GetSubLineIndex(diff.dbegin);
3788         const int nDiffEnd = GetSubLineIndex(diff.dend);
3789         // Diff's height is last line - first line + last line's line count
3790         const int nDiffHeight = nDiffEnd - nDiffStart + GetSubLines(diff.dend) + 1;
3791
3792         // If diff first line outside current view - context OR
3793         // if diff last line outside current view - context OR
3794         // if diff is bigger than screen
3795         if ((nDiffStart < m_nTopSubLine) ||
3796                 (nDiffEnd >= m_nTopSubLine + GetScreenLines() - nLinesBelow) ||
3797                 (nDiffHeight >= GetScreenLines()))
3798         {
3799                 return false;
3800         }
3801         else
3802         {
3803                 return true;
3804         }
3805 }
3806
3807 /** @brief Open help from mainframe when user presses F1*/
3808 void CMergeEditView::OnHelp()
3809 {
3810         theApp.ShowHelp(MergeViewHelpLocation);
3811 }
3812
3813 /**
3814  * @brief Called after document is loaded.
3815  * This function is called from CMergeDoc::OpenDocs() after documents are
3816  * loaded. So this is good place to set View's options etc.
3817  */
3818 void CMergeEditView::DocumentsLoaded()
3819 {
3820         // Enable/disable automatic rescan (rescanning after edit)
3821         EnableRescan(GetOptionsMgr()->GetBool(OPT_AUTOMATIC_RESCAN));
3822
3823         // SetTextType will revert to language dependent defaults for tab
3824         SetTabSize(GetOptionsMgr()->GetInt(OPT_TAB_SIZE));
3825         SetViewTabs(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE));
3826         const bool mixedEOLs = GetOptionsMgr()->GetBool(OPT_ALLOW_MIXED_EOL) ||
3827                 GetDocument()->IsMixedEOL(m_nThisPane);
3828         SetViewEols(GetOptionsMgr()->GetBool(OPT_VIEW_WHITESPACE), mixedEOLs);
3829         SetWordWrapping(GetOptionsMgr()->GetBool(OPT_WORDWRAP));
3830         SetViewLineNumbers(GetOptionsMgr()->GetBool(OPT_VIEW_LINENUMBERS));
3831         SetSelectionMargin(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3832
3833         // Enable Backspace at beginning of line
3834         SetDisableBSAtSOL(false);
3835
3836         // Set tab type (tabs/spaces)
3837         bool bInsertTabs = (GetOptionsMgr()->GetInt(OPT_TAB_TYPE) == 0);
3838         SetInsertTabs(bInsertTabs);
3839
3840         // Sometimes WinMerge doesn't update scrollbars correctly (they remain
3841         // disabled) after docs are open in screen. So lets make sure they are
3842         // really updated, even though this is unnecessary in most cases.
3843         RecalcHorzScrollBar();
3844         RecalcVertScrollBar();
3845 }
3846
3847 /**
3848  * @brief Update LocationView position.
3849  * This function updates LocationView position to given lines.
3850  * Usually we want to lines in file compare view and area in
3851  * LocationView to match. Be extra carefull to not call non-existing
3852  * LocationView.
3853  * @param [in] nTopLine Top line of current view.
3854  * @param [in] nBottomLine Bottom line of current view.
3855  */
3856 void CMergeEditView::UpdateLocationViewPosition(int nTopLine /*=-1*/,
3857                 int nBottomLine /*= -1*/)
3858 {
3859         CMergeDoc *pDoc = GetDocument();
3860         if (pDoc == nullptr)
3861                 return;
3862
3863         CLocationView *pLocationView = pDoc->GetLocationView();
3864
3865         if (pLocationView != nullptr && IsWindow(pLocationView->GetSafeHwnd()))
3866         {
3867                 pLocationView->UpdateVisiblePos(nTopLine, nBottomLine);
3868         }
3869 }
3870
3871 /**
3872  * @brief Enable/Disable view's selection margins.
3873  * Selection margins show bookmarks and word-wrap symbols, so they are pretty
3874  * useful. But it appears many users don't use/need those features and for them
3875  * selection margins are just wasted screen estate.
3876  */
3877 void CMergeEditView::OnViewMargin()
3878 {
3879         bool bViewMargin = GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN);
3880         GetOptionsMgr()->SaveOption(OPT_VIEW_FILEMARGIN, !bViewMargin);
3881
3882         SetSelectionMargin(!bViewMargin);
3883         CMergeDoc *pDoc = GetDocument();
3884         pDoc->RefreshOptions();
3885         pDoc->UpdateAllViews(this);
3886 }
3887
3888 /**
3889  * @brief Update GUI for Enable/Disable view's selection margin.
3890  * @param [in] pCmdUI Pointer to UI item to update.
3891  */
3892 void CMergeEditView::OnUpdateViewMargin(CCmdUI* pCmdUI)
3893 {
3894         pCmdUI->Enable(true);
3895         pCmdUI->SetCheck(GetOptionsMgr()->GetBool(OPT_VIEW_FILEMARGIN));
3896 }
3897
3898 /**
3899 * @brief Create the "Change Scheme" sub menu.
3900 * @param [in] pCmdUI Pointer to UI item to update.
3901 */
3902 void CMergeEditView::OnUpdateViewChangeScheme(CCmdUI *pCmdUI)
3903 {
3904         // Delete the place holder menu.
3905         pCmdUI->m_pSubMenu->DeleteMenu(0, MF_BYPOSITION);
3906
3907         const HMENU hSubMenu = pCmdUI->m_pSubMenu->m_hMenu;
3908
3909         String name = theApp.LoadString(ID_COLORSCHEME_FIRST);
3910         AppendMenu(hSubMenu, MF_STRING, ID_COLORSCHEME_FIRST, name.c_str());
3911         AppendMenu(hSubMenu, MF_SEPARATOR, 0, nullptr);
3912
3913         for (int i = ID_COLORSCHEME_FIRST + 1; i <= ID_COLORSCHEME_LAST; ++i)
3914         {
3915                 name = theApp.LoadString(i);
3916                 AppendMenu(hSubMenu, MF_STRING, i, name.c_str());
3917         }
3918
3919         pCmdUI->Enable(true);
3920 }
3921
3922 /**
3923 * @brief Change the editor's syntax highlighting scheme.
3924 * @param [in] nID Selected color scheme sub menu id.
3925 */
3926 void CMergeEditView::OnChangeScheme(UINT nID)
3927 {
3928         CMergeDoc *pDoc = GetDocument();
3929         ASSERT(pDoc != nullptr);
3930
3931         for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++) 
3932         {
3933                 CMergeEditView *pView = GetGroupView(nPane);
3934                 ASSERT(pView != nullptr);
3935
3936                 if (pView != nullptr)
3937                 {
3938                         pView->SetTextType(CCrystalTextView::TextType(nID - ID_COLORSCHEME_FIRST));
3939                         pView->SetDisableBSAtSOL(false);
3940                 }
3941         }
3942
3943         pDoc->UpdateAllViews(nullptr);
3944 }
3945
3946 /**
3947 * @brief Enable all color schemes sub menu items.
3948 * @param [in] pCmdUI Pointer to UI item to update.
3949 */
3950 void CMergeEditView::OnUpdateChangeScheme(CCmdUI* pCmdUI)
3951 {
3952         const bool bIsCurrentScheme = (static_cast<UINT>(m_CurSourceDef->type) == (pCmdUI->m_nID - ID_COLORSCHEME_FIRST));
3953         pCmdUI->SetRadio(bIsCurrentScheme);
3954         pCmdUI->Enable(GetOptionsMgr()->GetBool(OPT_SYNTAX_HIGHLIGHT));
3955 }
3956
3957 /**
3958  * @brief Called when mouse's wheel is scrolled.
3959  */
3960 BOOL CMergeEditView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
3961 {
3962         if ( nFlags == MK_CONTROL )
3963         {
3964                 short amount = zDelta < 0 ? -1: 1;
3965                 ZoomText(amount);
3966
3967                 // no default CCrystalTextView
3968                 return CView::OnMouseWheel(nFlags, zDelta, pt);
3969         }
3970
3971         if (nFlags == MK_SHIFT)
3972         {
3973                 SCROLLINFO si = { sizeof SCROLLINFO };
3974                 si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE;
3975
3976                 VERIFY(GetScrollInfo(SB_HORZ, &si));
3977
3978                 // new horz pos
3979                 si.nPos -= zDelta / 40;
3980                 if (si.nPos > si.nMax) si.nPos = si.nMax;
3981                 if (si.nPos < si.nMin) si.nPos = si.nMin;
3982
3983                 SetScrollInfo(SB_HORZ, &si);
3984
3985                 // for update
3986                 SendMessage(WM_HSCROLL, MAKEWPARAM(SB_THUMBPOSITION, si.nPos) , NULL );
3987
3988                 // no default CCrystalTextView
3989                 return CView::OnMouseWheel(nFlags, zDelta, pt);
3990         }
3991
3992         return CGhostTextView::OnMouseWheel(nFlags, zDelta, pt);
3993 }
3994
3995 /**
3996  * @brief Change font size (zoom) in views.
3997  * @param [in] amount Amount of change/zoom, negative number makes
3998  *  font smaller, positive number bigger and 0 reset the font size.
3999  */
4000 void CMergeEditView::ZoomText(short amount)
4001 {
4002         LOGFONT lf = { 0 };
4003         GetFont(lf);
4004
4005         const int nLogPixelsY = CClientDC(this).GetDeviceCaps(LOGPIXELSY);
4006         int nPointSize = -MulDiv(lf.lfHeight, 72, nLogPixelsY);
4007
4008         if ( amount == 0)
4009         {
4010                 nPointSize = -MulDiv(GetOptionsMgr()->GetInt(OPT_FONT_FILECMP + OPT_FONT_HEIGHT), 72, nLogPixelsY);
4011         }
4012
4013         nPointSize += amount;
4014         if (nPointSize < 2)
4015                 nPointSize = 2;
4016
4017         lf.lfHeight = -MulDiv(nPointSize, nLogPixelsY, 72);
4018
4019         CMergeDoc *pDoc = GetDocument();
4020         ASSERT(pDoc != nullptr);
4021
4022         if (pDoc != nullptr)
4023         {
4024                 for (int nPane = 0; nPane < pDoc->m_nBuffers; nPane++) 
4025                 {
4026                         CMergeEditView *pView = GetGroupView(nPane);
4027                         ASSERT(pView != nullptr);
4028                         
4029                         if (pView != nullptr)
4030                         {
4031                                 pView->SetFont(lf);
4032                         }
4033                 }
4034         }
4035 }
4036
4037 /**
4038  * @brief Called when user selects View/Zoom In from menu.
4039  */
4040 void CMergeEditView::OnViewZoomIn()
4041 {
4042         ZoomText(1);
4043 }
4044
4045 /**
4046  * @brief Called when user selects View/Zoom Out from menu.
4047  */
4048 void CMergeEditView::OnViewZoomOut()
4049 {
4050         ZoomText(-1);
4051 }
4052
4053 /**
4054  * @brief Called when user selects View/Zoom Normal from menu.
4055  */
4056 void CMergeEditView::OnViewZoomNormal()
4057 {
4058         ZoomText(0);
4059 }
4060
4061 void CMergeEditView::OnDropFiles(const std::vector<String>& tFiles)
4062 {
4063         if (tFiles.size() > 1 || paths::IsDirectory(tFiles[0]))
4064         {
4065                 GetMainFrame()->GetDropHandler()->GetCallback()(tFiles);
4066                 return;
4067         }
4068
4069         GetDocument()->ChangeFile(m_nThisPane, tFiles[0]);
4070 }
4071
4072 void CMergeEditView::OnWindowSplit()
4073 {
4074
4075         auto& wndSplitter = dynamic_cast<CMergeEditFrame *>(GetParentFrame())->GetSplitter();
4076         CMergeDoc *pDoc = GetDocument();
4077         CMergeEditView *pView = pDoc->GetView(0, m_nThisPane);
4078         auto* pwndSplitterChild = pView->GetParentSplitter(pView, false);
4079         int nBuffer = m_nThisPane;
4080         if (pDoc->m_nGroups <= 2)
4081         {
4082                 wndSplitter.SplitRow(1);
4083                 wndSplitter.EqualizeRows();
4084         }
4085         else
4086         {
4087                 wndSplitter.SetActivePane(0, 0);
4088                 wndSplitter.DeleteRow(1);
4089                 if (pwndSplitterChild->GetColumnCount() > 1)
4090                         pwndSplitterChild->SetActivePane(0, nBuffer);
4091                 else
4092                         pwndSplitterChild->SetActivePane(nBuffer, 0);
4093         }
4094 }
4095
4096 void CMergeEditView::OnUpdateWindowSplit(CCmdUI* pCmdUI)
4097 {
4098         pCmdUI->Enable(TRUE);
4099         pCmdUI->SetCheck(GetDocument()->m_nGroups > 2);
4100 }
4101