OSDN Git Service

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