OSDN Git Service

Update version number to 1.2.1.0
[tortoisegit/TortoiseGitJp.git] / src / TortoiseMerge / BaseView.cpp
1 // TortoiseMerge - a Diff/Patch program\r
2 \r
3 // Copyright (C) 2003-2009 - TortoiseSVN\r
4 \r
5 // This program is free software; you can redistribute it and/or\r
6 // modify it under the terms of the GNU General Public License\r
7 // as published by the Free Software Foundation; either version 2\r
8 // of the License, or (at your option) any later version.\r
9 \r
10 // This program is distributed in the hope that it will be useful,\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 // GNU General Public License for more details.\r
14 \r
15 // You should have received a copy of the GNU General Public License\r
16 // along with this program; if not, write to the Free Software Foundation,\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
18 //\r
19 \r
20 #include "stdafx.h"\r
21 #include "registry.h"\r
22 #include "TortoiseMerge.h"\r
23 #include "MainFrm.h"\r
24 #include "BaseView.h"\r
25 #include "DiffColors.h"\r
26 #include "StringUtils.h"\r
27 \r
28 #include <deque>\r
29 \r
30 #ifdef _DEBUG\r
31 #define new DEBUG_NEW\r
32 #endif\r
33 \r
34 #define MARGINWIDTH 20\r
35 #define HEADERHEIGHT 10\r
36 \r
37 #define MAXFONTS 8\r
38 \r
39 #define INLINEADDED_COLOR                       RGB(255, 255, 150)\r
40 #define INLINEREMOVED_COLOR                     RGB(200, 100, 100)\r
41 #define MODIFIED_COLOR                          RGB(220, 220, 255)\r
42 \r
43 #define IDT_SCROLLTIMER 101\r
44 \r
45 CBaseView * CBaseView::m_pwndLeft = NULL;\r
46 CBaseView * CBaseView::m_pwndRight = NULL;\r
47 CBaseView * CBaseView::m_pwndBottom = NULL;\r
48 CLocatorBar * CBaseView::m_pwndLocator = NULL;\r
49 CLineDiffBar * CBaseView::m_pwndLineDiffBar = NULL;\r
50 CMFCStatusBar * CBaseView::m_pwndStatusBar = NULL;\r
51 CMainFrame * CBaseView::m_pMainFrame = NULL;\r
52 \r
53 IMPLEMENT_DYNCREATE(CBaseView, CView)\r
54 \r
55 CBaseView::CBaseView()\r
56 {\r
57         m_pCacheBitmap = NULL;\r
58         m_pViewData = NULL;\r
59         m_pOtherViewData = NULL;\r
60         m_nLineHeight = -1;\r
61         m_nCharWidth = -1;\r
62         m_nScreenChars = -1;\r
63         m_nMaxLineLength = -1;\r
64         m_nScreenLines = -1;\r
65         m_nTopLine = 0;\r
66         m_nOffsetChar = 0;\r
67         m_nDigits = 0;\r
68         m_nMouseLine = -1;\r
69         m_bMouseWithin = FALSE;\r
70         m_bIsHidden = FALSE;\r
71         lineendings = EOL_AUTOLINE;\r
72         m_bCaretHidden = true;\r
73         m_ptCaretPos.x = 0;\r
74         m_ptCaretPos.y = 0;\r
75         m_nCaretGoalPos = 0;\r
76         m_ptSelectionStartPos = m_ptCaretPos;\r
77         m_ptSelectionEndPos = m_ptCaretPos;\r
78         m_ptSelectionOrigin = m_ptCaretPos;\r
79         m_bFocused = FALSE;\r
80         m_bShowSelection = true;\r
81         texttype = CFileTextLines::AUTOTYPE;\r
82         m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseMerge\\ViewWhitespaces"), 1);\r
83         m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);\r
84         m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE);\r
85         m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR);\r
86         m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR);\r
87         m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);\r
88         m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));\r
89         m_sWordSeparators = CRegString(_T("Software\\TortoiseMerge\\WordSeparators"), _T("[]();.,{}!@#$%^&*-+=|/\\<>'`~"));;\r
90         m_bIconLFs = CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);\r
91         m_nSelBlockStart = -1;\r
92         m_nSelBlockEnd = -1;\r
93         m_bModified = FALSE;\r
94         m_bOtherDiffChecked = false;\r
95         m_bInlineWordDiff = true;\r
96         m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);\r
97         for (int i=0; i<MAXFONTS; i++)\r
98         {\r
99                 m_apFonts[i] = NULL;\r
100         }\r
101         m_hConflictedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDLINE), \r
102                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
103         m_hConflictedIgnoredIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDIGNOREDLINE), \r
104                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
105         m_hRemovedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_REMOVEDLINE), \r
106                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
107         m_hAddedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ADDEDLINE), \r
108                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
109         m_hWhitespaceBlockIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_WHITESPACELINE),\r
110                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
111         m_hEqualIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EQUALLINE),\r
112                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
113         m_hLineEndingCR = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCR),\r
114                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
115         m_hLineEndingCRLF = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCRLF),\r
116                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
117         m_hLineEndingLF = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGLF),\r
118                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
119         m_hEditedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEEDITED),\r
120                                                                         IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);\r
121         for (int i=0; i<1024; ++i)\r
122                 m_sConflictedText += _T("??");\r
123         m_sNoLineNr.LoadString(IDS_EMPTYLINETT);\r
124         EnableToolTips();\r
125 }\r
126 \r
127 CBaseView::~CBaseView()\r
128 {\r
129         if (m_pCacheBitmap)\r
130         {\r
131                 m_pCacheBitmap->DeleteObject();\r
132                 delete m_pCacheBitmap;\r
133         }\r
134         for (int i=0; i<MAXFONTS; i++)\r
135         {\r
136                 if (m_apFonts[i] != NULL)\r
137                 {\r
138                         m_apFonts[i]->DeleteObject();\r
139                         delete m_apFonts[i];\r
140                 }\r
141                 m_apFonts[i] = NULL;\r
142         }\r
143         DestroyIcon(m_hAddedIcon);\r
144         DestroyIcon(m_hRemovedIcon);\r
145         DestroyIcon(m_hConflictedIcon);\r
146         DestroyIcon(m_hConflictedIgnoredIcon);\r
147         DestroyIcon(m_hWhitespaceBlockIcon);\r
148         DestroyIcon(m_hEqualIcon);\r
149         DestroyIcon(m_hLineEndingCR);\r
150         DestroyIcon(m_hLineEndingCRLF);\r
151         DestroyIcon(m_hLineEndingLF);\r
152         DestroyIcon(m_hEditedIcon);\r
153 }\r
154 \r
155 BEGIN_MESSAGE_MAP(CBaseView, CView)\r
156         ON_WM_VSCROLL()\r
157         ON_WM_HSCROLL()\r
158         ON_WM_ERASEBKGND()\r
159         ON_WM_CREATE()\r
160         ON_WM_DESTROY()\r
161         ON_WM_SIZE()\r
162         ON_WM_MOUSEWHEEL()\r
163         ON_WM_SETCURSOR()\r
164         ON_WM_KILLFOCUS()\r
165         ON_WM_SETFOCUS()\r
166         ON_WM_CONTEXTMENU()\r
167         ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)\r
168         ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)\r
169         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)\r
170         ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)\r
171         ON_WM_KEYDOWN()\r
172         ON_WM_LBUTTONDOWN()\r
173         ON_COMMAND(ID_EDIT_COPY, OnEditCopy)\r
174         ON_WM_MOUSEMOVE()\r
175         ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)\r
176         ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)\r
177         ON_WM_CHAR()\r
178         ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)\r
179         ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)\r
180         ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)\r
181         ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)\r
182         ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)\r
183         ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)\r
184         ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)\r
185         ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)\r
186         ON_WM_MOUSELEAVE()\r
187         ON_WM_TIMER()\r
188 END_MESSAGE_MAP()\r
189 \r
190 \r
191 void CBaseView::DocumentUpdated()\r
192 {\r
193         if (m_pCacheBitmap != NULL)\r
194         {\r
195                 m_pCacheBitmap->DeleteObject();\r
196                 delete m_pCacheBitmap;\r
197                 m_pCacheBitmap = NULL;\r
198         }\r
199         m_nLineHeight = -1;\r
200         m_nCharWidth = -1;\r
201         m_nScreenChars = -1;\r
202         m_nMaxLineLength = -1;\r
203         m_nScreenLines = -1;\r
204         m_nTopLine = 0;\r
205         m_bModified = FALSE;\r
206         m_bOtherDiffChecked = false;\r
207         m_nDigits = 0;\r
208         m_nMouseLine = -1;\r
209 \r
210         m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);\r
211         m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);\r
212         m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE);\r
213         m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR);\r
214         m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR);\r
215         m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);\r
216         m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));\r
217         m_bIconLFs = CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);\r
218         for (int i=0; i<MAXFONTS; i++)\r
219         {\r
220                 if (m_apFonts[i] != NULL)\r
221                 {\r
222                         m_apFonts[i]->DeleteObject();\r
223                         delete m_apFonts[i];\r
224                 }\r
225                 m_apFonts[i] = NULL;\r
226         }\r
227         m_nSelBlockStart = -1;\r
228         m_nSelBlockEnd = -1;\r
229         RecalcVertScrollBar();\r
230         RecalcHorzScrollBar();\r
231         UpdateStatusBar();\r
232         Invalidate();\r
233 }\r
234 \r
235 void CBaseView::UpdateStatusBar()\r
236 {\r
237         int nRemovedLines = 0;\r
238         int nAddedLines = 0;\r
239         int nConflictedLines = 0;\r
240 \r
241         if (m_pViewData)\r
242         {\r
243                 for (int i=0; i<m_pViewData->GetCount(); i++)\r
244                 {\r
245                         DiffStates state = m_pViewData->GetState(i);\r
246                         switch (state)\r
247                         {\r
248                         case DIFFSTATE_ADDED:\r
249                         case DIFFSTATE_IDENTICALADDED:\r
250                         case DIFFSTATE_THEIRSADDED:\r
251                         case DIFFSTATE_YOURSADDED:\r
252                         case DIFFSTATE_CONFLICTADDED:\r
253                                 nAddedLines++;\r
254                                 break;\r
255                         case DIFFSTATE_IDENTICALREMOVED:\r
256                         case DIFFSTATE_REMOVED:\r
257                         case DIFFSTATE_THEIRSREMOVED:\r
258                         case DIFFSTATE_YOURSREMOVED:\r
259                                 nRemovedLines++;\r
260                                 break;\r
261                         case DIFFSTATE_CONFLICTED:\r
262                         case DIFFSTATE_CONFLICTED_IGNORED:\r
263                                 nConflictedLines++;\r
264                                 break;\r
265                         }\r
266                 }\r
267         }\r
268 \r
269         CString sBarText;\r
270         CString sTemp;\r
271 \r
272         switch (texttype)\r
273         {\r
274         case CFileTextLines::ASCII:\r
275                 sBarText = _T("ASCII ");\r
276                 break;\r
277         case CFileTextLines::BINARY:\r
278                 sBarText = _T("BINARY ");\r
279                 break;\r
280         case CFileTextLines::UNICODE_LE:\r
281                 sBarText = _T("UTF-16LE ");\r
282                 break;\r
283         case CFileTextLines::UTF8:\r
284                 sBarText = _T("UTF8 ");\r
285                 break;\r
286         case CFileTextLines::UTF8BOM:\r
287                 sBarText = _T("UTF8 BOM ");\r
288                 break;\r
289         }\r
290 \r
291         switch(lineendings)\r
292         {\r
293         case EOL_LF:\r
294                 sBarText += _T("LF ");\r
295                 break;\r
296         case EOL_CRLF:\r
297                 sBarText += _T("CRLF ");\r
298                 break;\r
299         case EOL_LFCR:\r
300                 sBarText += _T("LFCR ");\r
301                 break;\r
302         case EOL_CR:\r
303                 sBarText += _T("CR ");\r
304                 break;\r
305         }\r
306 \r
307         if (sBarText.IsEmpty())\r
308                 sBarText  += _T(" / ");\r
309 \r
310         if (nRemovedLines)\r
311         {\r
312                 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);\r
313                 if (!sBarText.IsEmpty())\r
314                         sBarText += _T(" / ");\r
315                 sBarText += sTemp;\r
316         }\r
317         if (nAddedLines)\r
318         {\r
319                 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);\r
320                 if (!sBarText.IsEmpty())\r
321                         sBarText += _T(" / ");\r
322                 sBarText += sTemp;\r
323         }\r
324         if (nConflictedLines)\r
325         {\r
326                 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);\r
327                 if (!sBarText.IsEmpty())\r
328                         sBarText += _T(" / ");\r
329                 sBarText += sTemp;\r
330         }\r
331         if (m_pwndStatusBar)\r
332         {\r
333                 UINT nID;\r
334                 UINT nStyle;\r
335                 int cxWidth;\r
336                 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);\r
337                 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)\r
338                 {\r
339                         sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);\r
340                 }\r
341                 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)\r
342                 {\r
343                         sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);\r
344                         sBarText = sTemp+sBarText;\r
345                 }\r
346                 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)\r
347                 {\r
348                         sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);\r
349                         sBarText = sTemp+sBarText;\r
350                 }\r
351                 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);\r
352                 //calculate the width of the text\r
353                 CDC * pDC = m_pwndStatusBar->GetDC();\r
354                 if (pDC)\r
355                 {\r
356                         CSize size = pDC->GetTextExtent(sBarText);\r
357                         m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);\r
358                         ReleaseDC(pDC);\r
359                 }\r
360                 m_pwndStatusBar->SetPaneText(nIndex, sBarText);\r
361         }\r
362 }\r
363 \r
364 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs) \r
365 {\r
366         if (!CView::PreCreateWindow(cs))\r
367                 return FALSE;\r
368 \r
369         cs.dwExStyle |= WS_EX_CLIENTEDGE;\r
370         cs.style &= ~WS_BORDER;\r
371         cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, \r
372                 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);\r
373 \r
374         CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);\r
375         if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))\r
376         {\r
377                 //      View must always create its own scrollbars,\r
378                 //      if only it's not used within splitter\r
379                 cs.style |= (WS_HSCROLL | WS_VSCROLL);\r
380         }\r
381         cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);\r
382         return TRUE;\r
383 }\r
384 \r
385 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/, BOOL bStrikeOut /*= FALSE*/)\r
386 {\r
387         int nIndex = 0;\r
388         if (bBold)\r
389                 nIndex |= 1;\r
390         if (bItalic)\r
391                 nIndex |= 2;\r
392         if (bStrikeOut)\r
393                 nIndex |= 4;\r
394         if (m_apFonts[nIndex] == NULL)\r
395         {\r
396                 m_apFonts[nIndex] = new CFont;\r
397                 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;\r
398                 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;\r
399                 m_lfBaseFont.lfItalic = (BYTE) bItalic;\r
400                 m_lfBaseFont.lfStrikeOut = (BYTE) bStrikeOut;\r
401                 if (bStrikeOut)\r
402                         m_lfBaseFont.lfStrikeOut = (BYTE)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE);\r
403                 CDC * pDC = GetDC();\r
404                 if (pDC)\r
405                 {\r
406                         m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);\r
407                         ReleaseDC(pDC);\r
408                 }\r
409                 _tcsncpy_s(m_lfBaseFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseMerge\\LogFontName"), _T("Courier New")), 32);\r
410                 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))\r
411                 {\r
412                         delete m_apFonts[nIndex];\r
413                         m_apFonts[nIndex] = NULL;\r
414                         return CView::GetFont();\r
415                 }\r
416         }\r
417         return m_apFonts[nIndex];\r
418 }\r
419 \r
420 void CBaseView::CalcLineCharDim()\r
421 {\r
422         CDC *pDC = GetDC();\r
423         CFont *pOldFont = pDC->SelectObject(GetFont());\r
424         CSize szCharExt = pDC->GetTextExtent(_T("X"));\r
425         m_nLineHeight = szCharExt.cy;\r
426         if (m_nLineHeight <= 0)\r
427                 m_nLineHeight = -1;\r
428         m_nCharWidth = szCharExt.cx;\r
429         if (m_nCharWidth <= 0)\r
430                 m_nCharWidth = -1;\r
431         pDC->SelectObject(pOldFont);\r
432         ReleaseDC(pDC);\r
433 }\r
434 \r
435 int CBaseView::GetScreenChars()\r
436 {\r
437         if (m_nScreenChars == -1)\r
438         {\r
439                 CRect rect;\r
440                 GetClientRect(&rect);\r
441                 m_nScreenChars = (rect.Width() - GetMarginWidth()) / GetCharWidth();\r
442         }\r
443         return m_nScreenChars;\r
444 }\r
445 \r
446 int CBaseView::GetAllMinScreenChars() const \r
447 {\r
448         int nChars = 0;\r
449         if (IsLeftViewGood())\r
450                 nChars = m_pwndLeft->GetScreenChars();\r
451         if (IsRightViewGood())\r
452                 nChars = (nChars < m_pwndRight->GetScreenChars() ? nChars : m_pwndRight->GetScreenChars());\r
453         if (IsBottomViewGood())\r
454                 nChars = (nChars < m_pwndBottom->GetScreenChars() ? nChars : m_pwndBottom->GetScreenChars());\r
455         return nChars;\r
456 }\r
457 \r
458 int CBaseView::GetAllMaxLineLength() const \r
459 {\r
460         int nLength = 0;\r
461         if (IsLeftViewGood())\r
462                 nLength = m_pwndLeft->GetMaxLineLength();\r
463         if (IsRightViewGood())\r
464                 nLength = (nLength > m_pwndRight->GetMaxLineLength() ? nLength : m_pwndRight->GetMaxLineLength());\r
465         if (IsBottomViewGood())\r
466                 nLength = (nLength > m_pwndBottom->GetMaxLineLength() ? nLength : m_pwndBottom->GetMaxLineLength());\r
467         return nLength;\r
468 }\r
469 \r
470 int CBaseView::GetLineHeight()\r
471 {\r
472         if (m_nLineHeight == -1)\r
473                 CalcLineCharDim();\r
474         if (m_nLineHeight <= 0)\r
475                 return 1;\r
476         return m_nLineHeight;\r
477 }\r
478 \r
479 int CBaseView::GetCharWidth()\r
480 {\r
481         if (m_nCharWidth == -1)\r
482                 CalcLineCharDim();\r
483         if (m_nCharWidth <= 0)\r
484                 return 1;\r
485         return m_nCharWidth;\r
486 }\r
487 \r
488 int CBaseView::GetMaxLineLength()\r
489 {\r
490         if (m_nMaxLineLength == -1)\r
491         {\r
492                 m_nMaxLineLength = 0;\r
493                 int nLineCount = GetLineCount();\r
494                 for (int i=0; i<nLineCount; i++)\r
495                 {\r
496                         int nActualLength = GetLineActualLength(i);\r
497                         if (m_nMaxLineLength < nActualLength)\r
498                                 m_nMaxLineLength = nActualLength;\r
499                 }\r
500         }\r
501         return m_nMaxLineLength;\r
502 }\r
503 \r
504 int CBaseView::GetLineActualLength(int index) const\r
505 {\r
506         if (m_pViewData == NULL)\r
507                 return 0;\r
508 \r
509         return CalculateActualOffset(index, GetLineLength(index));\r
510 }\r
511 \r
512 int CBaseView::GetLineLength(int index) const\r
513 {\r
514         if (m_pViewData == NULL)\r
515                 return 0;\r
516         if (m_pViewData->GetCount() == 0)\r
517                 return 0;\r
518         int nLineLength = m_pViewData->GetLine(index).GetLength();\r
519         ASSERT(nLineLength >= 0);\r
520         return nLineLength;\r
521 }\r
522 \r
523 int CBaseView::GetLineCount() const\r
524 {\r
525         if (m_pViewData == NULL)\r
526                 return 1;\r
527         int nLineCount = m_pViewData->GetCount();\r
528         ASSERT(nLineCount >= 0);\r
529         return nLineCount;\r
530 }\r
531 \r
532 LPCTSTR CBaseView::GetLineChars(int index) const\r
533 {\r
534         if (m_pViewData == NULL)\r
535                 return 0;\r
536         if (m_pViewData->GetCount() == 0)\r
537                 return 0;\r
538         return m_pViewData->GetLine(index);\r
539 }\r
540 \r
541 void CBaseView::CheckOtherView()\r
542 {\r
543         if (m_bOtherDiffChecked)\r
544                 return;\r
545         // find out what the 'other' file is\r
546         m_pOtherViewData = NULL;\r
547         if (this == m_pwndLeft && IsRightViewGood())\r
548                 m_pOtherViewData = m_pwndRight->m_pViewData;\r
549 \r
550         if (this == m_pwndRight && IsLeftViewGood())\r
551                 m_pOtherViewData = m_pwndLeft->m_pViewData;\r
552 \r
553         m_bOtherDiffChecked = true;\r
554 }\r
555 \r
556 CString CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex)\r
557 {\r
558         enum { MAX_WHITESPACEBLOCK_SIZE = 8 };\r
559         ASSERT(viewData);\r
560         \r
561         DiffStates origstate = viewData->GetState(nLineIndex);\r
562 \r
563         // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends\r
564         int nStartBlock = nLineIndex;\r
565         int nEndBlock = nLineIndex;\r
566         while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))\r
567         {\r
568                 DiffStates state = viewData->GetState(nStartBlock - 1);\r
569                 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))\r
570                         origstate = state;\r
571                 if ((origstate == state) || (state == DIFFSTATE_EMPTY))\r
572                         nStartBlock--;\r
573                 else\r
574                         break;\r
575         }\r
576         while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))\r
577         {\r
578                 DiffStates state = viewData->GetState(nEndBlock + 1);\r
579                 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))\r
580                         origstate = state;\r
581                 if ((origstate == state) || (state == DIFFSTATE_EMPTY))\r
582                         nEndBlock++;\r
583                 else\r
584                         break;\r
585         }\r
586         \r
587         CString block;\r
588         for (int i = nStartBlock; i <= nEndBlock; ++i)\r
589                 block += viewData->GetLine(i);\r
590         return block;\r
591 }\r
592 \r
593 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical)\r
594 {\r
595         enum { MAX_WHITESPACEBLOCK_SIZE = 8 };\r
596         CheckOtherView();\r
597         if (!m_pOtherViewData)\r
598                 return false;\r
599         if (\r
600                 (m_pViewData->GetState(nLineIndex) == DIFFSTATE_NORMAL) &&\r
601                 (m_pOtherViewData->GetLine(nLineIndex) == m_pViewData->GetLine(nLineIndex))\r
602         )\r
603                 return false;\r
604 \r
605         CString mine = GetWhitespaceBlock(m_pViewData, nLineIndex);\r
606         CString other = GetWhitespaceBlock(m_pOtherViewData, min(nLineIndex, m_pOtherViewData->GetCount() - 1));\r
607         bIdentical = mine == other;\r
608         \r
609         mine.Replace(_T(" "), _T(""));\r
610         mine.Replace(_T("\t"), _T(""));\r
611         mine.Replace(_T("\r"), _T(""));\r
612         mine.Replace(_T("\n"), _T(""));\r
613         other.Replace(_T(" "), _T(""));\r
614         other.Replace(_T("\t"), _T(""));\r
615         other.Replace(_T("\r"), _T(""));\r
616         other.Replace(_T("\n"), _T(""));\r
617         \r
618         return (mine == other) && (!mine.IsEmpty());\r
619 }\r
620 \r
621 int CBaseView::GetLineNumber(int index) const\r
622 {\r
623         if (m_pViewData == NULL)\r
624                 return -1;\r
625         if (m_pViewData->GetLineNumber(index)==DIFF_EMPTYLINENUMBER)\r
626                 return -1;\r
627         return m_pViewData->GetLineNumber(index);\r
628 }\r
629 \r
630 int CBaseView::GetScreenLines()\r
631 {\r
632         if (m_nScreenLines == -1)\r
633         {\r
634                 SCROLLBARINFO sbi;\r
635                 sbi.cbSize = sizeof(sbi);\r
636                 GetScrollBarInfo(OBJID_HSCROLL, &sbi);\r
637                 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;\r
638 \r
639                 CRect rect;\r
640                 GetClientRect(&rect);\r
641                 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();\r
642         }\r
643         return m_nScreenLines;\r
644 }\r
645 \r
646 int CBaseView::GetAllMinScreenLines() const\r
647 {\r
648         int nLines = 0;\r
649         if (IsLeftViewGood())\r
650                 nLines = m_pwndLeft->GetScreenLines();\r
651         if (IsRightViewGood())\r
652                 nLines = (nLines < m_pwndRight->GetScreenLines() ? nLines : m_pwndRight->GetScreenLines());\r
653         if (IsBottomViewGood())\r
654                 nLines = (nLines < m_pwndBottom->GetScreenLines() ? nLines : m_pwndBottom->GetScreenLines());\r
655         return nLines;\r
656 }\r
657 \r
658 int CBaseView::GetAllLineCount() const\r
659 {\r
660         int nLines = 0;\r
661         if (IsLeftViewGood())\r
662                 nLines = m_pwndLeft->GetLineCount();\r
663         if (IsRightViewGood())\r
664                 nLines = (nLines > m_pwndRight->GetLineCount() ? nLines : m_pwndRight->GetLineCount());\r
665         if (IsBottomViewGood())\r
666                 nLines = (nLines > m_pwndBottom->GetLineCount() ? nLines : m_pwndBottom->GetLineCount());\r
667         return nLines;\r
668 }\r
669 \r
670 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)\r
671 {\r
672         if (IsLeftViewGood())\r
673                 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);\r
674         if (IsRightViewGood())\r
675                 m_pwndRight->RecalcVertScrollBar(bPositionOnly);\r
676         if (IsBottomViewGood())\r
677                 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);\r
678 }\r
679 \r
680 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)\r
681 {\r
682         SCROLLINFO si;\r
683         si.cbSize = sizeof(si);\r
684         if (bPositionOnly)\r
685         {\r
686                 si.fMask = SIF_POS;\r
687                 si.nPos = m_nTopLine;\r
688         }\r
689         else\r
690         {\r
691                 EnableScrollBarCtrl(SB_VERT, TRUE);\r
692                 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)\r
693                 {\r
694                         m_nTopLine = 0;\r
695                         Invalidate();\r
696                 }\r
697                 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;\r
698                 si.nMin = 0;\r
699                 si.nMax = GetAllLineCount();\r
700                 si.nPage = GetAllMinScreenLines();\r
701                 si.nPos = m_nTopLine;\r
702         }\r
703         VERIFY(SetScrollInfo(SB_VERT, &si));\r
704 }\r
705 \r
706 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) \r
707 {\r
708         CView::OnVScroll(nSBCode, nPos, pScrollBar);\r
709         if (m_pwndLeft)\r
710                 m_pwndLeft->OnDoVScroll(nSBCode,  nPos, pScrollBar, this);\r
711         if (m_pwndRight)\r
712                 m_pwndRight->OnDoVScroll(nSBCode,  nPos, pScrollBar, this);\r
713         if (m_pwndBottom)\r
714                 m_pwndBottom->OnDoVScroll(nSBCode,  nPos, pScrollBar, this);\r
715         if (m_pwndLocator)\r
716                 m_pwndLocator->Invalidate();\r
717 }\r
718 \r
719 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)\r
720 {\r
721         //      Note we cannot use nPos because of its 16-bit nature\r
722         SCROLLINFO si;\r
723         si.cbSize = sizeof(si);\r
724         si.fMask = SIF_ALL;\r
725         VERIFY(master->GetScrollInfo(SB_VERT, &si));\r
726 \r
727         int nPageLines = GetScreenLines();\r
728         int nLineCount = GetLineCount();\r
729 \r
730         RECT thumbrect;\r
731         POINT thumbpoint;\r
732         int nNewTopLine;\r
733 \r
734         static LONG textwidth = 0;\r
735         static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));\r
736         switch (nSBCode)\r
737         {\r
738         case SB_TOP:\r
739                 nNewTopLine = 0;\r
740                 break;\r
741         case SB_BOTTOM:\r
742                 nNewTopLine = nLineCount - nPageLines + 1;\r
743                 break;\r
744         case SB_LINEUP:\r
745                 nNewTopLine = m_nTopLine - 1;\r
746                 break;\r
747         case SB_LINEDOWN:\r
748                 nNewTopLine = m_nTopLine + 1;\r
749                 break;\r
750         case SB_PAGEUP:\r
751                 nNewTopLine = m_nTopLine - si.nPage + 1;\r
752                 break;\r
753         case SB_PAGEDOWN:\r
754                 nNewTopLine = m_nTopLine + si.nPage - 1;\r
755                 break;\r
756         case SB_THUMBPOSITION:\r
757                 m_ScrollTool.Clear();\r
758                 nNewTopLine = si.nTrackPos;\r
759                 textwidth = 0;\r
760                 break;\r
761         case SB_THUMBTRACK:\r
762                 nNewTopLine = si.nTrackPos;\r
763                 if (GetFocus() == this)\r
764                 {\r
765                         GetClientRect(&thumbrect);\r
766                         ClientToScreen(&thumbrect);\r
767                         thumbpoint.x = thumbrect.right;\r
768                         thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);\r
769                         m_ScrollTool.Init(&thumbpoint);\r
770                         if (textwidth == 0)\r
771                         {\r
772                                 CString sTemp = sFormat;\r
773                                 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);\r
774                                 textwidth = m_ScrollTool.GetTextWidth(sTemp);\r
775                         }\r
776                         thumbpoint.x -= textwidth;\r
777                         int line = GetLineNumber(nNewTopLine);\r
778                         if (line >= 0)\r
779                                 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);\r
780                         else\r
781                                 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);\r
782                 }\r
783                 break;\r
784         default:\r
785                 return;\r
786         }\r
787 \r
788         if (nNewTopLine < 0)\r
789                 nNewTopLine = 0;\r
790         if (nNewTopLine >= nLineCount)\r
791                 nNewTopLine = nLineCount - 1;\r
792         ScrollToLine(nNewTopLine);\r
793 }\r
794 \r
795 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)\r
796 {\r
797         if (IsLeftViewGood())\r
798                 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);\r
799         if (IsRightViewGood())\r
800                 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);\r
801         if (IsBottomViewGood())\r
802                 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);\r
803 }\r
804 \r
805 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)\r
806 {\r
807         SCROLLINFO si;\r
808         si.cbSize = sizeof(si);\r
809         if (bPositionOnly)\r
810         {\r
811                 si.fMask = SIF_POS;\r
812                 si.nPos = m_nOffsetChar;\r
813         }\r
814         else\r
815         {\r
816                 EnableScrollBarCtrl(SB_HORZ, TRUE);\r
817                 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar > 0)\r
818                 {\r
819                         m_nOffsetChar = 0;\r
820                         Invalidate();\r
821                 }\r
822                 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;\r
823                 si.nMin = 0;\r
824                 si.nMax = GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();\r
825                 si.nPage = GetAllMinScreenChars();\r
826                 si.nPos = m_nOffsetChar;\r
827         }\r
828         VERIFY(SetScrollInfo(SB_HORZ, &si));\r
829 }\r
830 \r
831 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) \r
832 {\r
833         CView::OnHScroll(nSBCode, nPos, pScrollBar);\r
834         if (m_pwndLeft)\r
835                 m_pwndLeft->OnDoHScroll(nSBCode,  nPos, pScrollBar, this);\r
836         if (m_pwndRight)\r
837                 m_pwndRight->OnDoHScroll(nSBCode,  nPos, pScrollBar, this);\r
838         if (m_pwndBottom)\r
839                 m_pwndBottom->OnDoHScroll(nSBCode,  nPos, pScrollBar, this);\r
840         if (m_pwndLocator)\r
841                 m_pwndLocator->Invalidate();\r
842 }\r
843 \r
844 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master) \r
845 {\r
846         SCROLLINFO si;\r
847         si.cbSize = sizeof(si);\r
848         si.fMask = SIF_ALL;\r
849         VERIFY(master->GetScrollInfo(SB_HORZ, &si));\r
850 \r
851         int nPageChars = GetScreenChars();\r
852         int nMaxLineLength = GetMaxLineLength();\r
853 \r
854         int nNewOffset;\r
855         switch (nSBCode)\r
856         {\r
857         case SB_LEFT:\r
858                 nNewOffset = 0;\r
859                 break;\r
860         case SB_BOTTOM:\r
861                 nNewOffset = nMaxLineLength - nPageChars + 1;\r
862                 break;\r
863         case SB_LINEUP:\r
864                 nNewOffset = m_nOffsetChar - 1;\r
865                 break;\r
866         case SB_LINEDOWN:\r
867                 nNewOffset = m_nOffsetChar + 1;\r
868                 break;\r
869         case SB_PAGEUP:\r
870                 nNewOffset = m_nOffsetChar - si.nPage + 1;\r
871                 break;\r
872         case SB_PAGEDOWN:\r
873                 nNewOffset = m_nOffsetChar + si.nPage - 1;\r
874                 break;\r
875         case SB_THUMBPOSITION:\r
876         case SB_THUMBTRACK:\r
877                 nNewOffset = si.nTrackPos;\r
878                 break;\r
879         default:\r
880                 return;\r
881         }\r
882 \r
883         if (nNewOffset >= nMaxLineLength)\r
884                 nNewOffset = nMaxLineLength - 1;\r
885         if (nNewOffset < 0)\r
886                 nNewOffset = 0;\r
887         ScrollToChar(nNewOffset, TRUE);\r
888 }\r
889 \r
890 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)\r
891 {\r
892         if (m_nOffsetChar != nNewOffsetChar)\r
893         {\r
894                 int nScrollChars = m_nOffsetChar - nNewOffsetChar;\r
895                 m_nOffsetChar = nNewOffsetChar;\r
896                 CRect rcScroll;\r
897                 GetClientRect(&rcScroll);\r
898                 rcScroll.left += GetMarginWidth();\r
899                 rcScroll.top += GetLineHeight()+HEADERHEIGHT;\r
900                 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);\r
901                 // update the view header\r
902                 rcScroll.left = 0;\r
903                 rcScroll.top = 0;\r
904                 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;\r
905                 InvalidateRect(&rcScroll, FALSE);\r
906                 UpdateWindow();\r
907                 if (bTrackScrollBar)\r
908                         RecalcHorzScrollBar(TRUE);\r
909                 UpdateCaret();\r
910         }\r
911 }\r
912 \r
913 void CBaseView::ScrollSide(int delta)\r
914 {\r
915         int nNewOffset = m_nOffsetChar;\r
916         nNewOffset += delta;\r
917         int nMaxLineLength = GetMaxLineLength();\r
918         if (nNewOffset >= nMaxLineLength)\r
919                 nNewOffset = nMaxLineLength - 1;\r
920         if (nNewOffset < 0)\r
921                 nNewOffset = 0;\r
922         ScrollToChar(nNewOffset, TRUE);\r
923         if (m_pwndLineDiffBar)\r
924                 m_pwndLineDiffBar->Invalidate();\r
925         UpdateCaret();\r
926 }\r
927 \r
928 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)\r
929 {\r
930         if (m_nTopLine != nNewTopLine)\r
931         {\r
932                 if (nNewTopLine < 0)\r
933                         nNewTopLine = 0;\r
934                 int nScrollLines = m_nTopLine - nNewTopLine;\r
935                 m_nTopLine = nNewTopLine;\r
936                 CRect rcScroll;\r
937                 GetClientRect(&rcScroll);\r
938                 rcScroll.top += GetLineHeight()+HEADERHEIGHT;\r
939                 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);\r
940                 UpdateWindow();\r
941                 if (bTrackScrollBar)\r
942                         RecalcVertScrollBar(TRUE);\r
943                 UpdateCaret();\r
944         }\r
945 }\r
946 \r
947 \r
948 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)\r
949 {\r
950         pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));\r
951 \r
952         if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))\r
953         {\r
954                 DiffStates state = m_pViewData->GetState(nLineIndex);\r
955                 HICON icon = NULL;\r
956                 switch (state)\r
957                 {\r
958                 case DIFFSTATE_ADDED:\r
959                 case DIFFSTATE_THEIRSADDED:\r
960                 case DIFFSTATE_YOURSADDED:\r
961                 case DIFFSTATE_IDENTICALADDED:\r
962                 case DIFFSTATE_CONFLICTADDED:\r
963                         icon = m_hAddedIcon;\r
964                         break;\r
965                 case DIFFSTATE_REMOVED:\r
966                 case DIFFSTATE_THEIRSREMOVED:\r
967                 case DIFFSTATE_YOURSREMOVED:\r
968                 case DIFFSTATE_IDENTICALREMOVED:\r
969                         icon = m_hRemovedIcon;\r
970                         break;\r
971                 case DIFFSTATE_CONFLICTED:\r
972                         icon = m_hConflictedIcon;\r
973                         break;\r
974                 case DIFFSTATE_CONFLICTED_IGNORED:\r
975                         icon = m_hConflictedIgnoredIcon;\r
976                         break;\r
977                 case DIFFSTATE_EDITED:\r
978                         icon = m_hEditedIcon;\r
979                         break;\r
980                 default:\r
981                         break;\r
982                 }\r
983                 bool bIdentical = false;\r
984                 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))\r
985                 {\r
986                         if (bIdentical)\r
987                                 icon = m_hEqualIcon;\r
988                         else\r
989                                 icon = m_hWhitespaceBlockIcon;\r
990                 }\r
991 \r
992                 if (icon)\r
993                 {\r
994                         ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);\r
995                 }\r
996                 if ((m_bViewLinenumbers)&&(m_nDigits))\r
997                 {\r
998                         int nLineNumber = GetLineNumber(nLineIndex);\r
999                         if (nLineNumber >= 0)\r
1000                         {\r
1001                                 CString sLinenumberFormat;\r
1002                                 CString sLinenumber;\r
1003                                 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);\r
1004                                 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);\r
1005                                 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));\r
1006                                 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));\r
1007 \r
1008                                 pdc->SelectObject(GetFont());\r
1009                                 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);\r
1010                         }\r
1011                 }\r
1012         }\r
1013 }\r
1014 \r
1015 int CBaseView::GetMarginWidth()\r
1016 {\r
1017         if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))\r
1018         {\r
1019                 int nWidth = GetCharWidth();\r
1020                 if (m_nDigits <= 0)\r
1021                 {\r
1022                         int nLength = (int)m_pViewData->GetCount();\r
1023                         // find out how many digits are needed to show the highest line number\r
1024                         int nDigits = 1;\r
1025                         while (nLength / 10)\r
1026                         {\r
1027                                 nDigits++;\r
1028                                 nLength /= 10;\r
1029                         }\r
1030                         m_nDigits = nDigits;\r
1031                 }\r
1032                 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);\r
1033         }\r
1034         return MARGINWIDTH;\r
1035 }\r
1036 \r
1037 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)\r
1038 {\r
1039         CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);\r
1040         COLORREF crBk, crFg;\r
1041         CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);\r
1042         crBk = ::GetSysColor(COLOR_SCROLLBAR);\r
1043         if (IsBottomViewGood())\r
1044         {\r
1045                 pdc->SetBkColor(crBk);\r
1046         }\r
1047         else\r
1048         {\r
1049 \r
1050                 if (this == m_pwndRight)\r
1051                 {\r
1052                         CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED, crBk, crFg);\r
1053                         pdc->SetBkColor(crBk);\r
1054                 }\r
1055                 else\r
1056                 {\r
1057                         CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED, crBk, crFg);\r
1058                         pdc->SetBkColor(crBk);\r
1059                 }\r
1060         }\r
1061         pdc->FillSolidRect(textrect, crBk);\r
1062 \r
1063         pdc->SetTextColor(crFg);\r
1064 \r
1065         pdc->SelectObject(GetFont(FALSE, TRUE, FALSE));\r
1066         if (IsModified())\r
1067         {\r
1068                 if (m_sWindowName.Left(2).Compare(_T("* "))!=0)\r
1069                         m_sWindowName = _T("* ") + m_sWindowName;\r
1070         }\r
1071         else\r
1072         {\r
1073                 if (m_sWindowName.Left(2).Compare(_T("* "))==0)\r
1074                         m_sWindowName = m_sWindowName.Mid(2);\r
1075         }\r
1076         CString sViewTitle = m_sWindowName;\r
1077         int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());\r
1078         if (nStringLength > rect.Width())\r
1079         {\r
1080                 int offset = min(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);\r
1081 \r
1082                 sViewTitle = m_sWindowName.Mid(offset);\r
1083         }\r
1084         pdc->ExtTextOut(max(rect.left + (rect.Width()-nStringLength)/2, 1), \r
1085                 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);\r
1086         if (this->GetFocus() == this)\r
1087                 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);\r
1088         else\r
1089                 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);\r
1090 }\r
1091 \r
1092 void CBaseView::OnDraw(CDC * pDC)\r
1093 {\r
1094         CRect rcClient;\r
1095         GetClientRect(rcClient);\r
1096         \r
1097         int nLineCount = GetLineCount();\r
1098         int nLineHeight = GetLineHeight();\r
1099 \r
1100         CDC cacheDC;\r
1101         VERIFY(cacheDC.CreateCompatibleDC(pDC));\r
1102         if (m_pCacheBitmap == NULL)\r
1103         {\r
1104                 m_pCacheBitmap = new CBitmap;\r
1105                 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));\r
1106         }\r
1107         CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);\r
1108 \r
1109         DrawHeader(pDC, rcClient);\r
1110         \r
1111         CRect rcLine;\r
1112         rcLine = rcClient;\r
1113         rcLine.top += nLineHeight+HEADERHEIGHT;\r
1114         rcLine.bottom = rcLine.top + nLineHeight;\r
1115         CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);\r
1116         CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);\r
1117 \r
1118         int nCurrentLine = m_nTopLine;\r
1119         while (rcLine.top < rcClient.bottom)\r
1120         {\r
1121                 if (nCurrentLine < nLineCount)\r
1122                 {\r
1123                         DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);\r
1124                         DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);\r
1125                 }\r
1126                 else\r
1127                 {\r
1128                         DrawMargin(&cacheDC, rcCacheMargin, -1);\r
1129                         DrawSingleLine(&cacheDC, rcCacheLine, -1);\r
1130                 }\r
1131 \r
1132                 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));\r
1133 \r
1134                 nCurrentLine ++;\r
1135                 rcLine.OffsetRect(0, nLineHeight);\r
1136         }\r
1137 \r
1138         cacheDC.SelectObject(pOldBitmap);\r
1139         cacheDC.DeleteDC();\r
1140 }\r
1141 \r
1142 BOOL CBaseView::IsLineRemoved(int nLineIndex)\r
1143 {\r
1144         DiffStates state = DIFFSTATE_UNKNOWN;\r
1145         if (m_pViewData)\r
1146                 state = m_pViewData->GetState(nLineIndex);\r
1147         BOOL ret = FALSE;\r
1148         switch (state)\r
1149         {\r
1150         case DIFFSTATE_REMOVED:\r
1151         case DIFFSTATE_THEIRSREMOVED:\r
1152         case DIFFSTATE_YOURSREMOVED:\r
1153         case DIFFSTATE_IDENTICALREMOVED:\r
1154                 ret = TRUE;\r
1155                 break;\r
1156         default:\r
1157                 ret = FALSE;\r
1158                 break;\r
1159         }\r
1160         return ret;\r
1161 }\r
1162 \r
1163 bool CBaseView::IsLineConflicted(int nLineIndex)\r
1164 {\r
1165         DiffStates state = DIFFSTATE_UNKNOWN;\r
1166         if (m_pViewData)\r
1167                 state = m_pViewData->GetState(nLineIndex);\r
1168         bool ret = false;\r
1169         switch (state)\r
1170         {\r
1171         case DIFFSTATE_CONFLICTED:\r
1172         case DIFFSTATE_CONFLICTED_IGNORED:\r
1173         case DIFFSTATE_CONFLICTEMPTY:\r
1174         case DIFFSTATE_CONFLICTADDED:\r
1175                 ret = true;\r
1176                 break;\r
1177         default:\r
1178                 ret = false;\r
1179                 break;\r
1180         }\r
1181         return ret;\r
1182 }\r
1183 \r
1184 COLORREF CBaseView::IntenseColor(long scale, COLORREF col)\r
1185 {\r
1186         // if the color is already dark (gray scale below 127),\r
1187         // then lighten the color by 'scale', otherwise darken it\r
1188         int Gray  = (((int)GetRValue(col)) + GetGValue(col) + GetBValue(col))/3;\r
1189         if (Gray > 127)\r
1190         {\r
1191                 long red   = MulDiv(GetRValue(col),(255-scale),255);\r
1192                 long green = MulDiv(GetGValue(col),(255-scale),255);\r
1193                 long blue  = MulDiv(GetBValue(col),(255-scale),255);\r
1194 \r
1195                 return RGB(red, green, blue);\r
1196         }\r
1197         long R = MulDiv(255-GetRValue(col),scale,255)+GetRValue(col);\r
1198         long G = MulDiv(255-GetGValue(col),scale,255)+GetGValue(col);\r
1199         long B = MulDiv(255-GetBValue(col),scale,255)+GetBValue(col);\r
1200 \r
1201         return RGB(R, G, B);\r
1202 }\r
1203 \r
1204 COLORREF CBaseView::InlineDiffColor(int nLineIndex)\r
1205 {\r
1206         return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;\r
1207 }\r
1208 \r
1209 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)\r
1210 {\r
1211         if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < m_pViewData->GetCount())))\r
1212                 return;\r
1213 \r
1214         EOL ending = m_pViewData->GetLineEnding(nLineIndex);\r
1215         if (m_bIconLFs)\r
1216         {\r
1217                 HICON hEndingIcon = NULL;\r
1218                 switch (ending)\r
1219                 {\r
1220                 case EOL_CR:    hEndingIcon = m_hLineEndingCR;          break;\r
1221                 case EOL_CRLF:  hEndingIcon = m_hLineEndingCRLF;        break;\r
1222                 case EOL_LF:    hEndingIcon = m_hLineEndingLF;          break;\r
1223                 default: return;\r
1224                 }\r
1225                 if (origin.x < (rc.left-GetCharWidth()))\r
1226                         return;\r
1227                 // If EOL style has changed, color end-of-line markers as inline differences.\r
1228                 if(\r
1229                         m_bShowInlineDiff && m_pOtherViewData &&\r
1230                         (nLineIndex < m_pOtherViewData->GetCount()) &&\r
1231                         (ending != EOL_NOENDING) &&\r
1232                         (ending != m_pOtherViewData->GetLineEnding(nLineIndex) &&\r
1233                         (m_pOtherViewData->GetLineEnding(nLineIndex) != EOL_NOENDING))\r
1234                         )\r
1235                 {\r
1236                         pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));\r
1237                 }\r
1238 \r
1239                 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);\r
1240         }\r
1241         else\r
1242         {\r
1243                 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);\r
1244                 CPen * oldpen = pDC->SelectObject(&pen);\r
1245                 int yMiddle = origin.y + rc.Height()/2;\r
1246                 int xMiddle = origin.x+GetCharWidth()/2;\r
1247                 switch (ending)\r
1248                 {\r
1249                 case EOL_CR:\r
1250                         // arrow from right to left\r
1251                         pDC->MoveTo(origin.x+GetCharWidth(), yMiddle);\r
1252                         pDC->LineTo(origin.x, yMiddle);\r
1253                         pDC->LineTo(origin.x+4, yMiddle+4);\r
1254                         pDC->MoveTo(origin.x, yMiddle);\r
1255                         pDC->LineTo(origin.x+4, yMiddle-4);\r
1256                         break;\r
1257                 case EOL_CRLF:\r
1258                         // arrow from top to middle+2, then left\r
1259                         pDC->MoveTo(origin.x+GetCharWidth(), rc.top);\r
1260                         pDC->LineTo(origin.x+GetCharWidth(), yMiddle);\r
1261                         pDC->LineTo(origin.x, yMiddle);\r
1262                         pDC->LineTo(origin.x+4, yMiddle+4);\r
1263                         pDC->MoveTo(origin.x, yMiddle);\r
1264                         pDC->LineTo(origin.x+4, yMiddle-4);\r
1265                         break;\r
1266                 case EOL_LF:\r
1267                         // arrow from top to bottom\r
1268                         pDC->MoveTo(xMiddle, rc.top);\r
1269                         pDC->LineTo(xMiddle, rc.bottom-1);\r
1270                         pDC->LineTo(xMiddle+4, rc.bottom-5);\r
1271                         pDC->MoveTo(xMiddle, rc.bottom-1);\r
1272                         pDC->LineTo(xMiddle-4, rc.bottom-5);\r
1273                         break;\r
1274                 }\r
1275                 pDC->SelectObject(oldpen);\r
1276         }       \r
1277 }\r
1278 \r
1279 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)\r
1280 {\r
1281         const int THICKNESS = 2;\r
1282         COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);\r
1283         if ((nLineIndex == m_nSelBlockStart) && m_bShowSelection)\r
1284         {\r
1285                 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);\r
1286         }               \r
1287         if ((nLineIndex == m_nSelBlockEnd) && m_bShowSelection)\r
1288         {\r
1289                 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);\r
1290         }\r
1291 }\r
1292 \r
1293 void CBaseView::DrawText(\r
1294         CDC * pDC, const CRect &rc, LPCTSTR text, int textlength, int nLineIndex, POINT coords, bool bModified, bool bInlineDiff)\r
1295 {\r
1296         ASSERT(m_pViewData && (nLineIndex < m_pViewData->GetCount()));\r
1297         DiffStates diffState = m_pViewData->GetState(nLineIndex);\r
1298         \r
1299         // first suppose the whole line is selected\r
1300         int selectedStart = 0, selectedEnd = textlength;\r
1301         \r
1302         if ((m_ptSelectionStartPos.y > nLineIndex) || (m_ptSelectionEndPos.y < nLineIndex)\r
1303                 || ! m_bShowSelection)\r
1304         {\r
1305                 // this line has no selected text\r
1306                 selectedStart = textlength;\r
1307         }\r
1308         else if ((m_ptSelectionStartPos.y == nLineIndex) || (m_ptSelectionEndPos.y == nLineIndex))\r
1309         {\r
1310                 // the line is partially selected\r
1311                 int xoffs = m_nOffsetChar + (coords.x - GetMarginWidth()) / GetCharWidth();\r
1312                 if (m_ptSelectionStartPos.y == nLineIndex)\r
1313                 {\r
1314                         // the first line of selection\r
1315                         int nSelectionStartOffset = CalculateActualOffset(m_ptSelectionStartPos.y, m_ptSelectionStartPos.x);\r
1316                         selectedStart = max(min(nSelectionStartOffset - xoffs, textlength), 0);\r
1317                 }\r
1318 \r
1319                 if (m_ptSelectionEndPos.y == nLineIndex)\r
1320                 {\r
1321                         // the last line of selection\r
1322                         int nSelectionEndOffset = CalculateActualOffset(m_ptSelectionEndPos.y, m_ptSelectionEndPos.x);\r
1323                         selectedEnd = max(min(nSelectionEndOffset - xoffs, textlength), 0);\r
1324                 }\r
1325         }\r
1326 \r
1327         COLORREF crBkgnd, crText;\r
1328         CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);\r
1329         if (bModified || (diffState == DIFFSTATE_EDITED))\r
1330                 crBkgnd = m_ModifiedBk;\r
1331         if (bInlineDiff)\r
1332                 crBkgnd = InlineDiffColor(nLineIndex);\r
1333 \r
1334         pDC->SetBkColor(crBkgnd);\r
1335         pDC->SetTextColor(crText);\r
1336         if (selectedStart>=0)\r
1337                 VERIFY(pDC->ExtTextOut(coords.x, coords.y, ETO_CLIPPED, &rc, text, selectedStart, NULL));\r
1338 \r
1339         long intenseColorScale = m_bFocused ? 70 : 30;\r
1340         pDC->SetBkColor(IntenseColor(intenseColorScale, crBkgnd));\r
1341         pDC->SetTextColor(IntenseColor(intenseColorScale, crText));\r
1342         VERIFY(pDC->ExtTextOut(\r
1343                 coords.x + selectedStart * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,\r
1344                 text + selectedStart, selectedEnd - selectedStart, NULL));\r
1345 \r
1346         pDC->SetBkColor(crBkgnd);\r
1347         pDC->SetTextColor(crText);\r
1348         if (textlength - selectedEnd >= 0)\r
1349                 VERIFY(pDC->ExtTextOut(\r
1350                                         coords.x + selectedEnd * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,\r
1351                                         text + selectedEnd, textlength - selectedEnd, NULL));\r
1352 }\r
1353 \r
1354 bool CBaseView::DrawInlineDiff(CDC *pDC, const CRect &rc, int nLineIndex, const CString &line, CPoint &origin)\r
1355 {\r
1356         if (!m_bShowInlineDiff || line.IsEmpty())\r
1357                 return false;\r
1358         if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))\r
1359                 return false;\r
1360 \r
1361         LPCTSTR pszDiffChars = NULL;\r
1362         int nDiffLength = 0;\r
1363         if (m_pOtherViewData)\r
1364         {\r
1365                 int index = min(nLineIndex, m_pOtherViewData->GetCount() - 1);\r
1366                 pszDiffChars = m_pOtherViewData->GetLine(index);\r
1367                 nDiffLength = m_pOtherViewData->GetLine(index).GetLength();\r
1368         }\r
1369 \r
1370         if (!pszDiffChars || !*pszDiffChars)\r
1371                 return false;\r
1372 \r
1373         CString diffline;\r
1374         ExpandChars(pszDiffChars, 0, nDiffLength, diffline);\r
1375         svn_diff_t * diff = NULL;\r
1376         m_svnlinediff.Diff(&diff, line, line.GetLength(), diffline, diffline.GetLength(), m_bInlineWordDiff);\r
1377         if (!diff || !SVNLineDiff::ShowInlineDiff(diff))\r
1378                 return false;\r
1379 \r
1380         int lineoffset = 0;\r
1381         std::deque<int> removedPositions;\r
1382         while (diff)\r
1383         {\r
1384                 apr_off_t len = diff->original_length;\r
1385 \r
1386                 CString s;\r
1387                 for (int i = 0; i < len; ++i)\r
1388                 {\r
1389                         s += m_svnlinediff.m_line1tokens[lineoffset].c_str();\r
1390                         lineoffset++;\r
1391                 }\r
1392                 bool isModified = diff->type == svn_diff__type_diff_modified;\r
1393                 DrawText(pDC, rc, (LPCTSTR)s, s.GetLength(), nLineIndex, origin, true, isModified);\r
1394                 origin.x += pDC->GetTextExtent(s).cx;\r
1395 \r
1396                 if (isModified && (len < diff->modified_length))\r
1397                         removedPositions.push_back(origin.x - 1);\r
1398 \r
1399                 diff = diff->next;\r
1400         }\r
1401         // Draw vertical bars at removed chunks' positions.\r
1402         for (std::deque<int>::iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)\r
1403                 pDC->FillSolidRect(*it, rc.top, 1, rc.Height(), m_InlineRemovedBk);\r
1404         return true;\r
1405 }\r
1406 \r
1407 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)\r
1408 {\r
1409         if (nLineIndex >= GetLineCount())\r
1410                 nLineIndex = -1;\r
1411         ASSERT(nLineIndex >= -1);\r
1412 \r
1413         if ((nLineIndex == -1) || !m_pViewData)\r
1414         {\r
1415                 // Draw line beyond the text\r
1416                 COLORREF crBkgnd, crText;\r
1417                 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);\r
1418                 pDC->FillSolidRect(rc, crBkgnd);\r
1419                 return;\r
1420         }\r
1421 \r
1422         DiffStates diffState = m_pViewData->GetState(nLineIndex);\r
1423         COLORREF crBkgnd, crText;\r
1424         CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);\r
1425 \r
1426         if (diffState == DIFFSTATE_CONFLICTED)\r
1427         {\r
1428                 // conflicted lines are shown without 'text' on them\r
1429                 CRect rect = rc;\r
1430                 pDC->FillSolidRect(rc, crBkgnd);\r
1431                 // now draw some faint text patterns\r
1432                 pDC->SetTextColor(IntenseColor(130, crBkgnd));\r
1433                 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);\r
1434                 DrawBlockLine(pDC, rc, nLineIndex);\r
1435                 return;\r
1436         }\r
1437 \r
1438         CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);\r
1439         int nLength = GetLineLength(nLineIndex);\r
1440         if (nLength == 0)\r
1441         {\r
1442                 // Draw the empty line\r
1443                 pDC->FillSolidRect(rc, crBkgnd);\r
1444                 DrawBlockLine(pDC, rc, nLineIndex);\r
1445                 DrawLineEnding(pDC, rc, nLineIndex, origin);\r
1446                 return;\r
1447         }\r
1448         LPCTSTR pszChars = GetLineChars(nLineIndex);\r
1449         if (pszChars == NULL)\r
1450                 return;\r
1451 \r
1452         CheckOtherView();\r
1453 \r
1454         // Draw the line\r
1455 \r
1456         pDC->SelectObject(GetFont(FALSE, FALSE, IsLineRemoved(nLineIndex)));\r
1457         CString line;\r
1458         ExpandChars(pszChars, 0, nLength, line);\r
1459 \r
1460         int nWidth = rc.right - origin.x;\r
1461         int savedx = origin.x;\r
1462         bool bInlineDiffDrawn =\r
1463                 nWidth > 0 && diffState != DIFFSTATE_NORMAL &&\r
1464                 DrawInlineDiff(pDC, rc, nLineIndex, line, origin);\r
1465 \r
1466         if (!bInlineDiffDrawn)\r
1467         {\r
1468                 int nCount = min(line.GetLength(), nWidth / GetCharWidth() + 1);\r
1469                 DrawText(pDC, rc, line, nCount, nLineIndex, origin, false, false);\r
1470         }\r
1471 \r
1472         origin.x = savedx + pDC->GetTextExtent(line).cx;\r
1473 \r
1474         // draw white space after the end of line\r
1475         CRect frect = rc;\r
1476         if (origin.x > frect.left)\r
1477                 frect.left = origin.x;\r
1478         if (bInlineDiffDrawn)\r
1479                 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);\r
1480         if (frect.right > frect.left)\r
1481                 pDC->FillSolidRect(frect, crBkgnd);\r
1482         // draw the whitespace chars\r
1483         if (m_bViewWhitespace)\r
1484         {\r
1485                 int xpos = 0;\r
1486                 int y = rc.top + (rc.bottom-rc.top)/2;\r
1487 \r
1488                 int nActualOffset = 0;\r
1489                 while ((nActualOffset < m_nOffsetChar) && (*pszChars))\r
1490                 {\r
1491                         if (*pszChars == _T('\t'))\r
1492                                 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());\r
1493                         else\r
1494                                 nActualOffset++;\r
1495                         pszChars++;\r
1496                 }\r
1497                 if (nActualOffset > m_nOffsetChar)\r
1498                         pszChars--;\r
1499 \r
1500                 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);\r
1501                 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);\r
1502                 while (*pszChars)\r
1503                 {\r
1504                         switch (*pszChars)\r
1505                         {\r
1506                         case _T('\t'):\r
1507                                 {\r
1508                                         // draw an arrow\r
1509                                         CPen * oldPen = pDC->SelectObject(&pen);\r
1510                                         int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();\r
1511                                         pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);\r
1512                                         pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);\r
1513                                         pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);\r
1514                                         pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);\r
1515                                         pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);\r
1516                                         xpos += nSpaces;\r
1517                                         pDC->SelectObject(oldPen);\r
1518                                 }\r
1519                                 break;\r
1520                         case _T(' '):\r
1521                                 {\r
1522                                         // draw a small dot\r
1523                                         CPen * oldPen = pDC->SelectObject(&pen2);\r
1524                                         pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);\r
1525                                         pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);\r
1526                                         xpos++;\r
1527                                         pDC->SelectObject(oldPen);\r
1528                                 }\r
1529                                 break;\r
1530                         default:\r
1531                                 xpos++;\r
1532                                 break;\r
1533                         }\r
1534                         pszChars++;\r
1535                 }\r
1536         }\r
1537         DrawBlockLine(pDC, rc, nLineIndex);\r
1538         DrawLineEnding(pDC, rc, nLineIndex, origin);\r
1539 }\r
1540 \r
1541 void CBaseView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)\r
1542 {\r
1543         if (nCount <= 0)\r
1544         {\r
1545                 line = _T("");\r
1546                 return;\r
1547         }\r
1548 \r
1549         int nTabSize = GetTabSize();\r
1550 \r
1551         int nActualOffset = 0;\r
1552         for (int i=0; i<nOffset; i++)\r
1553         {\r
1554                 if (pszChars[i] == _T('\t'))\r
1555                         nActualOffset += (nTabSize - nActualOffset % nTabSize);\r
1556                 else\r
1557                         nActualOffset ++;\r
1558         }\r
1559 \r
1560         pszChars += nOffset;\r
1561         int nLength = nCount;\r
1562 \r
1563         int nTabCount = 0;\r
1564         for (int i=0; i<nLength; i++)\r
1565         {\r
1566                 if (pszChars[i] == _T('\t'))\r
1567                         nTabCount ++;\r
1568         }\r
1569 \r
1570         LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);\r
1571         int nCurPos = 0;\r
1572         if (nTabCount > 0 || m_bViewWhitespace)\r
1573         {\r
1574                 for (int i=0; i<nLength; i++)\r
1575                 {\r
1576                         if (pszChars[i] == _T('\t'))\r
1577                         {\r
1578                                 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;\r
1579                                 while (nSpaces > 0)\r
1580                                 {\r
1581                                         pszBuf[nCurPos ++] = _T(' ');\r
1582                                         nSpaces --;\r
1583                                 }\r
1584                         }\r
1585                         else\r
1586                         {\r
1587                                 pszBuf[nCurPos] = pszChars[i];\r
1588                                 nCurPos ++;\r
1589                         }\r
1590                 }\r
1591         }\r
1592         else\r
1593         {\r
1594                 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);\r
1595                 nCurPos = nLength;\r
1596         }\r
1597         pszBuf[nCurPos] = 0;\r
1598         line.ReleaseBuffer();\r
1599 }\r
1600 \r
1601 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)\r
1602 {\r
1603         if ((m_pwndLeft)&&(m_pwndRight))\r
1604         {\r
1605                 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1606                 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1607         }\r
1608         else\r
1609         {\r
1610                 if (m_pwndLeft)\r
1611                         m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1612                 if (m_pwndRight)\r
1613                         m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1614         }\r
1615         if (m_pwndBottom)\r
1616                 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1617         if (m_pwndLocator)\r
1618                 m_pwndLocator->Invalidate();\r
1619 }\r
1620 \r
1621 void CBaseView::GoToLine(int nNewLine, BOOL bAll)\r
1622 {\r
1623         //almost the same as ScrollAllToLine, but try to put the line in the\r
1624         //middle of the view, not on top\r
1625         int nNewTopLine = nNewLine - GetScreenLines()/2;\r
1626         if (nNewTopLine < 0)\r
1627                 nNewTopLine = 0;\r
1628         if (m_pViewData)\r
1629         {\r
1630                 if (nNewTopLine >= m_pViewData->GetCount())\r
1631                         nNewTopLine = m_pViewData->GetCount()-1;\r
1632                 if (bAll)\r
1633                         ScrollAllToLine(nNewTopLine);\r
1634                 else\r
1635                         ScrollToLine(nNewTopLine);\r
1636         }\r
1637 }\r
1638 \r
1639 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)\r
1640 {\r
1641         return TRUE;\r
1642 }\r
1643 \r
1644 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)\r
1645 {\r
1646         if (CView::OnCreate(lpCreateStruct) == -1)\r
1647                 return -1;\r
1648 \r
1649         memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));\r
1650         //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));\r
1651         //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));\r
1652         m_lfBaseFont.lfHeight = 0;\r
1653         m_lfBaseFont.lfWeight = FW_NORMAL;\r
1654         m_lfBaseFont.lfItalic = FALSE;\r
1655         m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;\r
1656         m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1657         m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1658         m_lfBaseFont.lfQuality = DEFAULT_QUALITY;\r
1659         m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;\r
1660 \r
1661         return 0;\r
1662 }\r
1663 \r
1664 void CBaseView::OnDestroy()\r
1665 {\r
1666         CView::OnDestroy();\r
1667         for (int i=0; i<MAXFONTS; i++)\r
1668         {\r
1669                 if (m_apFonts[i] != NULL)\r
1670                 {\r
1671                         m_apFonts[i]->DeleteObject();\r
1672                         delete m_apFonts[i];\r
1673                         m_apFonts[i] = NULL;\r
1674                 }\r
1675         }\r
1676         if (m_pCacheBitmap != NULL)\r
1677         {\r
1678                 delete m_pCacheBitmap;\r
1679                 m_pCacheBitmap = NULL;\r
1680         }\r
1681 }\r
1682 \r
1683 void CBaseView::OnSize(UINT nType, int cx, int cy)\r
1684 {\r
1685         if (m_pCacheBitmap != NULL)\r
1686         {\r
1687                 m_pCacheBitmap->DeleteObject();\r
1688                 delete m_pCacheBitmap;\r
1689                 m_pCacheBitmap = NULL;\r
1690         }\r
1691         // make sure the view header is redrawn\r
1692         CRect rcScroll;\r
1693         GetClientRect(&rcScroll);\r
1694         rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;\r
1695         InvalidateRect(&rcScroll, FALSE);\r
1696 \r
1697         m_nScreenLines = -1;\r
1698         m_nScreenChars = -1;\r
1699         RecalcVertScrollBar();\r
1700         RecalcHorzScrollBar();\r
1701         CView::OnSize(nType, cx, cy);\r
1702 }\r
1703 \r
1704 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)\r
1705 {\r
1706         if (m_pwndLeft)\r
1707                 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);\r
1708         if (m_pwndRight)\r
1709                 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);\r
1710         if (m_pwndBottom)\r
1711                 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);\r
1712         if (m_pwndLocator)\r
1713                 m_pwndLocator->Invalidate();\r
1714         return CView::OnMouseWheel(nFlags, zDelta, pt);\r
1715 }\r
1716 \r
1717 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)\r
1718 {\r
1719         if (GetKeyState(VK_CONTROL)&0x8000)\r
1720         {\r
1721                 // Ctrl-Wheel scrolls sideways\r
1722                 ScrollSide(-zDelta/30);\r
1723         }\r
1724         else\r
1725         {\r
1726                 int nLineCount = GetLineCount();\r
1727                 int nTopLine = m_nTopLine;\r
1728                 nTopLine -= (zDelta/30);\r
1729                 if (nTopLine < 0)\r
1730                         nTopLine = 0;\r
1731                 if (nTopLine >= nLineCount)\r
1732                         nTopLine = nLineCount - 1;\r
1733                 ScrollToLine(nTopLine, TRUE);\r
1734         }\r
1735 }\r
1736 \r
1737 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)\r
1738 {\r
1739         if (nHitTest == HTCLIENT)\r
1740         {\r
1741                 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));    // Set To Arrow Cursor\r
1742                 return TRUE;\r
1743         }\r
1744         return CView::OnSetCursor(pWnd, nHitTest, message);\r
1745 }\r
1746 \r
1747 void CBaseView::OnKillFocus(CWnd* pNewWnd)\r
1748 {\r
1749         CView::OnKillFocus(pNewWnd);\r
1750         m_bFocused = FALSE;\r
1751         UpdateCaret();\r
1752         Invalidate();\r
1753 }\r
1754 \r
1755 void CBaseView::OnSetFocus(CWnd* pOldWnd)\r
1756 {\r
1757         CView::OnSetFocus(pOldWnd);\r
1758         m_bFocused = TRUE;\r
1759         UpdateCaret();\r
1760         Invalidate();\r
1761 }\r
1762 \r
1763 int CBaseView::GetLineFromPoint(CPoint point)\r
1764 {\r
1765         ScreenToClient(&point);\r
1766         return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);\r
1767 }\r
1768 \r
1769 bool CBaseView::OnContextMenu(CPoint /*point*/, int /*nLine*/, DiffStates /*state*/)\r
1770 {\r
1771         return false;\r
1772 }\r
1773 \r
1774 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)\r
1775 {\r
1776         int nLine = GetLineFromPoint(point);\r
1777 \r
1778         if (!m_pViewData)\r
1779                 return;\r
1780         if (m_nSelBlockEnd >= GetLineCount())\r
1781                 m_nSelBlockEnd = GetLineCount()-1;\r
1782         if ((nLine <= m_pViewData->GetCount())&&(nLine > m_nTopLine))\r
1783         {\r
1784                 int nIndex = nLine - 1;\r
1785                 DiffStates state = m_pViewData->GetState(nIndex);\r
1786                 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))\r
1787                 {\r
1788                         // if there's nothing selected, or if the selection is outside the window then\r
1789                         // select the diff block under the cursor.\r
1790                         if (((m_nSelBlockStart<0)&&(m_nSelBlockEnd<0))||\r
1791                                 ((m_nSelBlockEnd < m_nTopLine)||(m_nSelBlockStart > m_nTopLine+m_nScreenLines)))\r
1792                         {\r
1793                                 while (nIndex >= 0)\r
1794                                 {\r
1795                                         if (nIndex == 0)\r
1796                                         {\r
1797                                                 nIndex--;\r
1798                                                 break;\r
1799                                         }\r
1800                                         if (state != m_pViewData->GetState(--nIndex))\r
1801                                                 break;\r
1802                                 }\r
1803                                 m_nSelBlockStart = nIndex+1;\r
1804                                 while (nIndex < (m_pViewData->GetCount()-1))\r
1805                                 {\r
1806                                         if (state != m_pViewData->GetState(++nIndex))\r
1807                                                 break;\r
1808                                 }\r
1809                                 if ((nIndex == (m_pViewData->GetCount()-1))&&(state == m_pViewData->GetState(nIndex)))\r
1810                                         m_nSelBlockEnd = nIndex;\r
1811                                 else\r
1812                                         m_nSelBlockEnd = nIndex-1;\r
1813                                 SetupSelection(m_nSelBlockStart, m_nSelBlockEnd);\r
1814                                 m_ptCaretPos.x = 0;\r
1815                                 m_ptCaretPos.y = nLine - 1;\r
1816                                 UpdateCaret();\r
1817                         }\r
1818                 }\r
1819                 if (((state == DIFFSTATE_NORMAL)||(state == DIFFSTATE_UNKNOWN)) &&\r
1820                         (m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))\r
1821                 {\r
1822                         // find a more 'relevant' state in the selection\r
1823                         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; ++i)\r
1824                         {\r
1825                                 state = m_pViewData->GetState(i);\r
1826                                 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))\r
1827                                         break;\r
1828                         }\r
1829                 }\r
1830                 bool bKeepSelection = OnContextMenu(point, nLine, state);\r
1831                 if (! bKeepSelection)\r
1832                         ClearSelection();\r
1833                 RefreshViews();\r
1834         }\r
1835 }\r
1836 \r
1837 void CBaseView::RefreshViews()\r
1838 {\r
1839         if (m_pwndLeft)\r
1840         {\r
1841                 m_pwndLeft->UpdateStatusBar();\r
1842                 m_pwndLeft->Invalidate();\r
1843         }\r
1844         if (m_pwndRight)\r
1845         {\r
1846                 m_pwndRight->UpdateStatusBar();\r
1847                 m_pwndRight->Invalidate();\r
1848         }\r
1849         if (m_pwndBottom)\r
1850         {\r
1851                 m_pwndBottom->UpdateStatusBar();\r
1852                 m_pwndBottom->Invalidate();\r
1853         }\r
1854         if (m_pwndLocator)\r
1855                 m_pwndLocator->Invalidate();\r
1856 }\r
1857 \r
1858 void CBaseView::GoToFirstDifference()\r
1859 {\r
1860         m_ptCaretPos.y = 0;\r
1861         SelectNextBlock(1, false, false);\r
1862 }\r
1863 \r
1864 void CBaseView::HiglightLines(int start, int end /* = -1 */)\r
1865 {\r
1866         ClearSelection();\r
1867         m_nSelBlockStart = start;\r
1868         if (end < 0)\r
1869                 end = start;\r
1870         m_nSelBlockEnd = end;\r
1871         m_ptCaretPos.x = 0;\r
1872         m_ptCaretPos.y = start;\r
1873         UpdateCaret();\r
1874         Invalidate();\r
1875 }\r
1876 \r
1877 void CBaseView::SetupSelection(int start, int end)\r
1878 {\r
1879         if (IsBottomViewGood())\r
1880         {\r
1881                 m_pwndBottom->m_nSelBlockStart = start;\r
1882                 m_pwndBottom->m_nSelBlockEnd = end;\r
1883                 m_pwndBottom->Invalidate();\r
1884         }\r
1885         if (IsLeftViewGood())\r
1886         {\r
1887                 m_pwndLeft->m_nSelBlockStart = start;\r
1888                 m_pwndLeft->m_nSelBlockEnd = end;\r
1889                 m_pwndLeft->Invalidate();\r
1890         }\r
1891         if (IsRightViewGood())\r
1892         {\r
1893                 m_pwndRight->m_nSelBlockStart = start;\r
1894                 m_pwndRight->m_nSelBlockEnd = end;\r
1895                 m_pwndRight->Invalidate();\r
1896         }\r
1897 }\r
1898 \r
1899 void CBaseView::OnMergePreviousconflict()\r
1900 {\r
1901         SelectNextBlock(-1, true);\r
1902 }\r
1903 \r
1904 void CBaseView::OnMergeNextconflict()\r
1905 {\r
1906         SelectNextBlock(1, true);\r
1907 }\r
1908 \r
1909 void CBaseView::OnMergeNextdifference()\r
1910 {\r
1911         SelectNextBlock(1, false);\r
1912 }\r
1913 \r
1914 void CBaseView::OnMergePreviousdifference()\r
1915 {\r
1916         SelectNextBlock(-1, false);\r
1917 }\r
1918 \r
1919 void CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */)\r
1920 {\r
1921         if (! m_pViewData)\r
1922                 return;\r
1923 \r
1924         if (m_pViewData->GetCount() == 0)\r
1925                 return;\r
1926 \r
1927         int nCenterPos = m_ptCaretPos.y;\r
1928         int nLimit = 0;\r
1929         if (nDirection > 0)\r
1930                 nLimit = m_pViewData->GetCount() - 1;\r
1931 \r
1932         if (nCenterPos >= m_pViewData->GetCount())\r
1933                 nCenterPos = m_pViewData->GetCount()-1;\r
1934 \r
1935         if (bSkipEndOfCurrentBlock) \r
1936         {\r
1937                 // Find end of current block\r
1938                 DiffStates state = m_pViewData->GetState(nCenterPos);\r
1939                 while ((nCenterPos != nLimit) && \r
1940                        (m_pViewData->GetState(nCenterPos)==state))\r
1941                         nCenterPos += nDirection;\r
1942         }\r
1943 \r
1944         // Find next diff/conflict block\r
1945         while (nCenterPos != nLimit)\r
1946         {\r
1947                 DiffStates linestate = m_pViewData->GetState(nCenterPos);\r
1948                 if (!bConflict &&\r
1949                         (linestate != DIFFSTATE_NORMAL) &&\r
1950                         (linestate != DIFFSTATE_UNKNOWN))\r
1951                         break;\r
1952                 if (bConflict &&\r
1953                         ((linestate == DIFFSTATE_CONFLICTADDED) ||\r
1954                          (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||\r
1955                          (linestate == DIFFSTATE_CONFLICTED) ||\r
1956                          (linestate == DIFFSTATE_CONFLICTEMPTY)))\r
1957                         break;\r
1958 \r
1959                 nCenterPos += nDirection;\r
1960         }\r
1961 \r
1962         // Find end of new block\r
1963         DiffStates state = m_pViewData->GetState(nCenterPos);\r
1964         int nBlockEnd = nCenterPos;\r
1965         while ((nBlockEnd != nLimit) &&  \r
1966                          (state == m_pViewData->GetState(nBlockEnd + nDirection)))\r
1967                 nBlockEnd += nDirection;\r
1968 \r
1969         int nTopPos = nCenterPos - (GetScreenLines()/2);\r
1970         if (nTopPos < 0)\r
1971                 nTopPos = 0;\r
1972 \r
1973         m_ptCaretPos.x = 0;\r
1974         m_ptCaretPos.y = nCenterPos;\r
1975         ClearSelection();\r
1976         if (nDirection > 0)\r
1977                 SetupSelection(nCenterPos, nBlockEnd);\r
1978         else\r
1979                 SetupSelection(nBlockEnd, nCenterPos);\r
1980 \r
1981         ScrollAllToLine(nTopPos, FALSE);\r
1982         RecalcAllVertScrollBars(TRUE);\r
1983         m_nCaretGoalPos = 0;\r
1984         UpdateCaret();\r
1985         ShowDiffLines(nCenterPos);\r
1986 }\r
1987 \r
1988 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)\r
1989 {\r
1990         // need to handle both ANSI and UNICODE versions of the message\r
1991         TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;\r
1992         TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;\r
1993         CString strTipText;\r
1994         UINT nID = (UINT)pNMHDR->idFrom;\r
1995         if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||\r
1996                 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))\r
1997         {\r
1998                 // idFrom is actually the HWND of the tool\r
1999                 nID = ::GetDlgCtrlID((HWND)nID);\r
2000         }\r
2001 \r
2002         if (pNMHDR->idFrom == (UINT)m_hWnd)\r
2003         {\r
2004                 if (m_sWindowName.Left(2).Compare(_T("* "))==0)\r
2005                 {\r
2006                         strTipText = m_sWindowName.Mid(2) + _T("\r\n") + m_sFullFilePath;\r
2007                 }\r
2008                 else\r
2009                 {\r
2010                         strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;\r
2011                 }\r
2012         }\r
2013         else\r
2014                 return FALSE;\r
2015         \r
2016         *pResult = 0;\r
2017         if (strTipText.IsEmpty())\r
2018                 return TRUE;\r
2019 \r
2020         if (pNMHDR->code == TTN_NEEDTEXTA)\r
2021         {\r
2022                 pTTTA->lpszText = m_szTip;\r
2023                 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);\r
2024         }\r
2025         else\r
2026         {\r
2027                 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);\r
2028                 pTTTW->lpszText = m_wszTip;\r
2029         }\r
2030 \r
2031         return TRUE;    // message was handled\r
2032 }\r
2033 \r
2034 \r
2035 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const\r
2036 {\r
2037         CRect rcClient;\r
2038         GetClientRect(rcClient);\r
2039         CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);\r
2040         if (textrect.PtInRect(point))\r
2041         {\r
2042                 // inside the header part of the view (showing the filename)\r
2043                 pTI->hwnd = this->m_hWnd;\r
2044                 this->GetClientRect(&pTI->rect);\r
2045                 pTI->uFlags  |= TTF_ALWAYSTIP | TTF_IDISHWND;\r
2046                 pTI->uId = (UINT)m_hWnd;\r
2047                 pTI->lpszText = LPSTR_TEXTCALLBACK;\r
2048 \r
2049                 // we want multi line tooltips\r
2050                 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;\r
2051                 if (pToolTip->GetSafeHwnd() != NULL)\r
2052                 {\r
2053                         pToolTip->SetMaxTipWidth(INT_MAX);\r
2054                 }\r
2055 \r
2056                 return 1;\r
2057         }\r
2058         return -1;\r
2059 }\r
2060 \r
2061 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)\r
2062 {\r
2063         bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);\r
2064         bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);\r
2065         switch (nChar)\r
2066         {\r
2067         case VK_PRIOR:\r
2068                 {\r
2069                         m_ptCaretPos.y -= GetScreenLines();\r
2070                         m_ptCaretPos.y = max(m_ptCaretPos.y, 0);\r
2071                         m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);\r
2072                         if (bShift)\r
2073                                 AdjustSelection();\r
2074                         else\r
2075                                 ClearSelection();\r
2076                         UpdateCaret();\r
2077                         EnsureCaretVisible();\r
2078                         ShowDiffLines(m_ptCaretPos.y);\r
2079                 }\r
2080                 break;\r
2081         case VK_NEXT:\r
2082                 {\r
2083                         m_ptCaretPos.y += GetScreenLines();\r
2084                         if (m_ptCaretPos.y >= GetLineCount())\r
2085                                 m_ptCaretPos.y = GetLineCount()-1;\r
2086                         m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);\r
2087                         if (bShift)\r
2088                                 AdjustSelection();\r
2089                         else\r
2090                                 ClearSelection();\r
2091                         UpdateCaret();\r
2092                         EnsureCaretVisible();\r
2093                         ShowDiffLines(m_ptCaretPos.y);\r
2094                 }\r
2095                 break;\r
2096         case VK_HOME:\r
2097                 {\r
2098                         if (bControl)\r
2099                         {\r
2100                                 ScrollAllToLine(0);\r
2101                                 m_ptCaretPos.x = 0;\r
2102                                 m_ptCaretPos.y = 0;\r
2103                                 m_nCaretGoalPos = 0;\r
2104                                 if (bShift)\r
2105                                         AdjustSelection();\r
2106                                 else\r
2107                                         ClearSelection();\r
2108                                 UpdateCaret();\r
2109                         }\r
2110                         else\r
2111                         {\r
2112                                 m_ptCaretPos.x = 0;\r
2113                                 m_nCaretGoalPos = 0;\r
2114                                 if (bShift)\r
2115                                         AdjustSelection();\r
2116                                 else\r
2117                                         ClearSelection();\r
2118                                 EnsureCaretVisible();\r
2119                                 UpdateCaret();\r
2120                         }\r
2121                 }\r
2122                 break;\r
2123         case VK_END:\r
2124                 {\r
2125                         if (bControl)\r
2126                         {\r
2127                                 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());\r
2128                                 m_ptCaretPos.y = GetLineCount()-1;\r
2129                                 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);\r
2130                                 UpdateGoalPos();\r
2131                                 if (bShift)\r
2132                                         AdjustSelection();\r
2133                                 else\r
2134                                         ClearSelection();\r
2135                                 UpdateCaret();\r
2136                         }\r
2137                         else\r
2138                         {\r
2139                                 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);\r
2140                                 UpdateGoalPos();\r
2141                                 if (bShift)\r
2142                                         AdjustSelection();\r
2143                                 else\r
2144                                         ClearSelection();\r
2145                                 EnsureCaretVisible();\r
2146                                 UpdateCaret();\r
2147                         }\r
2148                 }\r
2149                 break;\r
2150         case VK_BACK:\r
2151                 {\r
2152                         if (m_bCaretHidden)\r
2153                                 break;\r
2154 \r
2155                         if (! HasTextSelection()) {\r
2156                                 if (m_ptCaretPos.y == 0 && m_ptCaretPos.x == 0)\r
2157                                         break;\r
2158                                 m_ptSelectionEndPos = m_ptCaretPos;\r
2159                                 MoveCaretLeft();\r
2160                                 m_ptSelectionStartPos = m_ptCaretPos;\r
2161                         }\r
2162                         RemoveSelectedText();\r
2163                 }\r
2164                 break;\r
2165         case VK_DELETE:\r
2166                 {\r
2167                         if (m_bCaretHidden)\r
2168                                 break;\r
2169 \r
2170                         if (! HasTextSelection()) {\r
2171                                 if (! MoveCaretRight())\r
2172                                         break;\r
2173                                 m_ptSelectionEndPos = m_ptCaretPos;\r
2174                                 MoveCaretLeft();\r
2175                                 m_ptSelectionStartPos = m_ptCaretPos;\r
2176                         }\r
2177                         RemoveSelectedText();\r
2178                 }\r
2179                 break;\r
2180         }\r
2181         CView::OnKeyDown(nChar, nRepCnt, nFlags);\r
2182 }\r
2183 \r
2184 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)\r
2185 {\r
2186         int nClickedLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);\r
2187         nClickedLine--;         //we need the index\r
2188         if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))\r
2189         {\r
2190                 m_ptCaretPos.y = nClickedLine;\r
2191                 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());\r
2192                 UpdateGoalPos();\r
2193 \r
2194                 if (nFlags & MK_SHIFT)\r
2195                         AdjustSelection();\r
2196                 else\r
2197                 {\r
2198                         ClearSelection();\r
2199                         SetupSelection(m_ptCaretPos.y, m_ptCaretPos.y);\r
2200                 }\r
2201 \r
2202                 UpdateCaret();\r
2203 \r
2204                 Invalidate();\r
2205         }\r
2206 \r
2207         CView::OnLButtonDown(nFlags, point);\r
2208 }\r
2209 \r
2210 void CBaseView::OnEditCopy()\r
2211 {\r
2212         if ((m_ptSelectionStartPos.x == m_ptSelectionEndPos.x)&&(m_ptSelectionStartPos.y == m_ptSelectionEndPos.y))\r
2213                 return;\r
2214         // first store the selected lines in one CString\r
2215         CString sCopyData;\r
2216         for (int i=m_ptSelectionStartPos.y; i<=m_ptSelectionEndPos.y; i++)\r
2217         {\r
2218                 switch (m_pViewData->GetState(i))\r
2219                 {\r
2220                 case DIFFSTATE_EMPTY:\r
2221                         break;\r
2222                 case DIFFSTATE_UNKNOWN:\r
2223                 case DIFFSTATE_NORMAL:\r
2224                 case DIFFSTATE_REMOVED:\r
2225                 case DIFFSTATE_REMOVEDWHITESPACE:\r
2226                 case DIFFSTATE_ADDED:\r
2227                 case DIFFSTATE_ADDEDWHITESPACE:\r
2228                 case DIFFSTATE_WHITESPACE:\r
2229                 case DIFFSTATE_WHITESPACE_DIFF:\r
2230                 case DIFFSTATE_CONFLICTED:\r
2231                 case DIFFSTATE_CONFLICTED_IGNORED:\r
2232                 case DIFFSTATE_CONFLICTADDED:\r
2233                 case DIFFSTATE_CONFLICTEMPTY:\r
2234                 case DIFFSTATE_CONFLICTRESOLVED:\r
2235                 case DIFFSTATE_IDENTICALREMOVED:\r
2236                 case DIFFSTATE_IDENTICALADDED:\r
2237                 case DIFFSTATE_THEIRSREMOVED:\r
2238                 case DIFFSTATE_THEIRSADDED:\r
2239                 case DIFFSTATE_YOURSREMOVED:\r
2240                 case DIFFSTATE_YOURSADDED:\r
2241                 case DIFFSTATE_EDITED:\r
2242                         sCopyData += m_pViewData->GetLine(i);\r
2243                         sCopyData += _T("\r\n");\r
2244                         break;\r
2245                 }\r
2246         }\r
2247         // remove the last \r\n\r
2248         sCopyData = sCopyData.Left(sCopyData.GetLength()-2);\r
2249         // remove the non-selected chars from the first line\r
2250         sCopyData = sCopyData.Mid(m_ptSelectionStartPos.x);\r
2251         // remove the non-selected chars from the last line\r
2252         int lastLinePos = sCopyData.ReverseFind('\n');\r
2253         lastLinePos += 1;\r
2254         if (lastLinePos == 0)\r
2255                 lastLinePos -= m_ptSelectionStartPos.x;\r
2256         sCopyData = sCopyData.Left(lastLinePos+m_ptSelectionEndPos.x);\r
2257         if (!sCopyData.IsEmpty())\r
2258         {\r
2259                 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);\r
2260         }\r
2261 }\r
2262 \r
2263 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)\r
2264 {\r
2265         if (m_pMainFrame->m_nMoveMovesToIgnore > 0) {\r
2266                 --m_pMainFrame->m_nMoveMovesToIgnore;\r
2267                 CView::OnMouseMove(nFlags, point);\r
2268                 return;\r
2269         }\r
2270 \r
2271         int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);\r
2272         nMouseLine--;           //we need the index\r
2273         if (nMouseLine < -1)\r
2274         {\r
2275                 nMouseLine = -1;\r
2276         }\r
2277         ShowDiffLines(nMouseLine);\r
2278 \r
2279         KillTimer(IDT_SCROLLTIMER);\r
2280         if (nFlags & MK_LBUTTON)\r
2281         {\r
2282                 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;\r
2283                 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;\r
2284                 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());\r
2285                 if (((m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))&&\r
2286                         ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))\r
2287                 {\r
2288                         m_ptCaretPos.y = nMouseLine;\r
2289                         m_ptCaretPos.x = charIndex;\r
2290                         UpdateGoalPos();\r
2291                         AdjustSelection();\r
2292                         UpdateCaret();\r
2293                         Invalidate();\r
2294                         UpdateWindow();\r
2295                 }\r
2296                 if (nMouseLine < m_nTopLine)\r
2297                 {\r
2298                         ScrollToLine(m_nTopLine-1, TRUE);\r
2299                         SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2300                 }\r
2301                 if (nMouseLine >= m_nTopLine + GetScreenLines())\r
2302                 {\r
2303                         ScrollToLine(m_nTopLine+1, TRUE);\r
2304                         SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2305                 }\r
2306                 if (charIndex <= m_nOffsetChar)\r
2307                 {\r
2308                         ScrollSide(-1);\r
2309                         SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2310                 }\r
2311                 if (charIndex >= (GetScreenChars()+m_nOffsetChar))\r
2312                 {\r
2313                         ScrollSide(1);\r
2314                         SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2315                 }\r
2316         }\r
2317 \r
2318         if (!m_bMouseWithin)\r
2319         { \r
2320                 m_bMouseWithin = TRUE;\r
2321                 TRACKMOUSEEVENT tme;\r
2322                 tme.cbSize = sizeof(TRACKMOUSEEVENT);\r
2323                 tme.dwFlags = TME_LEAVE;\r
2324                 tme.hwndTrack = m_hWnd;\r
2325                 _TrackMouseEvent(&tme);\r
2326         }\r
2327 \r
2328         CView::OnMouseMove(nFlags, point);\r
2329 }\r
2330 \r
2331 void CBaseView::OnMouseLeave()\r
2332 {\r
2333         ShowDiffLines(-1);\r
2334         m_bMouseWithin = FALSE;\r
2335         KillTimer(IDT_SCROLLTIMER);\r
2336         CView::OnMouseLeave();\r
2337 }\r
2338 \r
2339 void CBaseView::OnTimer(UINT_PTR nIDEvent)\r
2340 {\r
2341         if (nIDEvent == IDT_SCROLLTIMER)\r
2342         {\r
2343                 POINT point;\r
2344                 GetCursorPos(&point);\r
2345                 ScreenToClient(&point);\r
2346                 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);\r
2347                 nMouseLine--;           //we need the index\r
2348                 if (nMouseLine < -1)\r
2349                 {\r
2350                         nMouseLine = -1;\r
2351                 }\r
2352                 if (GetKeyState(VK_LBUTTON)&0x8000)\r
2353                 {\r
2354                         int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;\r
2355                         saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;\r
2356                         int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());\r
2357                         if (nMouseLine < m_nTopLine)\r
2358                         {\r
2359                                 ScrollToLine(m_nTopLine-1, TRUE);\r
2360                                 SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2361                         }\r
2362                         if (nMouseLine >= m_nTopLine + GetScreenLines())\r
2363                         {\r
2364                                 ScrollToLine(m_nTopLine+1, TRUE);\r
2365                                 SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2366                         }\r
2367                         if (charIndex <= m_nOffsetChar)\r
2368                         {\r
2369                                 ScrollSide(-1);\r
2370                                 SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2371                         }\r
2372                         if (charIndex >= GetScreenChars())\r
2373                         {\r
2374                                 ScrollSide(1);\r
2375                                 SetTimer(IDT_SCROLLTIMER, 20, NULL);\r
2376                         }\r
2377                 }\r
2378 \r
2379         }\r
2380 \r
2381         CView::OnTimer(nIDEvent);\r
2382 }\r
2383 \r
2384 void CBaseView::SelectLines(int nLine1, int nLine2)\r
2385 {\r
2386         if (nLine2 == -1)\r
2387                 nLine2 = nLine1;\r
2388         m_nSelBlockStart = nLine1;\r
2389         m_nSelBlockEnd = nLine2;\r
2390         Invalidate();\r
2391 }\r
2392 \r
2393 void CBaseView::ShowDiffLines(int nLine)\r
2394 {\r
2395         if ((nLine >= m_nTopLine)&&(nLine < GetLineCount()))\r
2396         {\r
2397                 if ((m_pwndRight)&&(m_pwndRight->m_pViewData)&&(m_pwndLeft)&&(m_pwndLeft->m_pViewData)&&(!m_pMainFrame->m_bOneWay))\r
2398                 {\r
2399                         nLine = (nLine > m_pwndRight->m_pViewData->GetCount() ? -1 : nLine);\r
2400                         nLine = (nLine > m_pwndLeft->m_pViewData->GetCount() ? -1 : nLine);\r
2401 \r
2402                         if (nLine >= 0)\r
2403                         {\r
2404                                 if (nLine != m_nMouseLine)\r
2405                                 {\r
2406                                         m_nMouseLine = nLine;\r
2407                                         if (nLine >= GetLineCount())\r
2408                                                 nLine = -1;\r
2409                                         m_pwndLineDiffBar->ShowLines(nLine);\r
2410                                 }\r
2411                         }\r
2412                 }\r
2413         }\r
2414         else\r
2415         {\r
2416                 m_pwndLineDiffBar->ShowLines(nLine);\r
2417         }\r
2418 }\r
2419 \r
2420 void CBaseView::UseTheirAndYourBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)\r
2421 {\r
2422         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2423                 return;\r
2424         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2425         {\r
2426                 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);\r
2427                 m_pwndBottom->m_pViewData->SetLine(i, m_pwndLeft->m_pViewData->GetLine(i));\r
2428                 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);\r
2429                 m_pwndBottom->m_pViewData->SetState(i, m_pwndLeft->m_pViewData->GetState(i));\r
2430                 m_pwndBottom->m_pViewData->SetLineEnding(i, EOL_AUTOLINE);\r
2431                 if (m_pwndBottom->IsLineConflicted(i))\r
2432                 {\r
2433                         if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2434                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2435                         else\r
2436                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);\r
2437                 }\r
2438                 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);\r
2439         }\r
2440 \r
2441         // your block is done, now insert their block\r
2442         int index = m_nSelBlockEnd+1;\r
2443         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2444         {\r
2445                 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2446                 m_pwndBottom->m_pViewData->InsertData(index, m_pwndRight->m_pViewData->GetData(i));\r
2447                 if (m_pwndBottom->IsLineConflicted(index))\r
2448                 {\r
2449                         if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2450                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2451                         else\r
2452                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);\r
2453                 }\r
2454                 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);\r
2455                 index++;\r
2456         }\r
2457         // adjust line numbers\r
2458         for (int i=m_nSelBlockEnd+1; i<GetLineCount(); ++i)\r
2459         {\r
2460                 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);\r
2461                 if (oldline >= 0)\r
2462                         m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));\r
2463         }\r
2464 \r
2465         // now insert an empty block in both yours and theirs\r
2466         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2467         {\r
2468                 rightstate.addedlines.push_back(m_nSelBlockStart);\r
2469                 m_pwndRight->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2470                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2471                 leftstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2472         }\r
2473         RecalcAllVertScrollBars();\r
2474         m_pwndBottom->SetModified();\r
2475         m_pwndLeft->SetModified();\r
2476         m_pwndRight->SetModified();\r
2477 }\r
2478 \r
2479 void CBaseView::UseYourAndTheirBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)\r
2480 {\r
2481         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2482                 return;\r
2483         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2484         {\r
2485                 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);\r
2486                 m_pwndBottom->m_pViewData->SetLine(i, m_pwndRight->m_pViewData->GetLine(i));\r
2487                 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);\r
2488                 m_pwndBottom->m_pViewData->SetState(i, m_pwndRight->m_pViewData->GetState(i));\r
2489                 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);\r
2490                 m_pwndBottom->m_pViewData->SetLineEnding(i, EOL_AUTOLINE);\r
2491                 if (m_pwndBottom->IsLineConflicted(i))\r
2492                 {\r
2493                         if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2494                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2495                         else\r
2496                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);\r
2497                 }\r
2498                 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);\r
2499         }\r
2500 \r
2501         // your block is done, now insert their block\r
2502         int index = m_nSelBlockEnd+1;\r
2503         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2504         {\r
2505                 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2506                 m_pwndBottom->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));\r
2507                 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);\r
2508                 if (m_pwndBottom->IsLineConflicted(index))\r
2509                 {\r
2510                         if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2511                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2512                         else\r
2513                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);\r
2514                 }\r
2515                 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);\r
2516                 index++;\r
2517         }\r
2518         // adjust line numbers\r
2519         for (int i=m_nSelBlockEnd+1; i<m_pwndBottom->GetLineCount(); ++i)\r
2520         {\r
2521                 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);\r
2522                 if (oldline >= 0)\r
2523                         m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));\r
2524         }\r
2525 \r
2526         // now insert an empty block in both yours and theirs\r
2527         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2528         {\r
2529                 leftstate.addedlines.push_back(m_nSelBlockStart);\r
2530                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2531                 m_pwndRight->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2532                 rightstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2533         }\r
2534 \r
2535         RecalcAllVertScrollBars();\r
2536         m_pwndBottom->SetModified();\r
2537         m_pwndLeft->SetModified();\r
2538         m_pwndRight->SetModified();\r
2539 }\r
2540 \r
2541 void CBaseView::UseBothRightFirst(viewstate &rightstate, viewstate &leftstate)\r
2542 {\r
2543         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2544                 return;\r
2545         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2546         {\r
2547                 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);\r
2548                 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);\r
2549         }\r
2550 \r
2551         // your block is done, now insert their block\r
2552         int index = m_nSelBlockEnd+1;\r
2553         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2554         {\r
2555                 rightstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2556                 m_pwndRight->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));\r
2557                 m_pwndRight->m_pViewData->SetState(index++, DIFFSTATE_THEIRSADDED);\r
2558         }\r
2559         // adjust line numbers\r
2560         index--;\r
2561         for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)\r
2562         {\r
2563                 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);\r
2564                 if (oldline >= 0)\r
2565                         m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));\r
2566         }\r
2567 \r
2568         // now insert an empty block in the left view\r
2569         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2570         {\r
2571                 leftstate.addedlines.push_back(m_nSelBlockStart);\r
2572                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2573         }\r
2574         RecalcAllVertScrollBars();\r
2575         m_pwndLeft->SetModified();\r
2576         m_pwndRight->SetModified();\r
2577 }\r
2578 \r
2579 void CBaseView::UseBothLeftFirst(viewstate &rightstate, viewstate &leftstate)\r
2580 {\r
2581         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2582                 return;\r
2583         // get line number from just before the block\r
2584         long linenumber = 0;\r
2585         if (m_nSelBlockStart > 0)\r
2586                 linenumber = m_pwndRight->m_pViewData->GetLineNumber(m_nSelBlockStart-1);\r
2587         linenumber++;\r
2588         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2589         {\r
2590                 rightstate.addedlines.push_back(m_nSelBlockStart);\r
2591                 m_pwndRight->m_pViewData->InsertData(i, m_pwndLeft->m_pViewData->GetLine(i), DIFFSTATE_THEIRSADDED, linenumber++, m_pwndLeft->m_pViewData->GetLineEnding(i));\r
2592         }\r
2593         // adjust line numbers\r
2594         for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)\r
2595         {\r
2596                 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);\r
2597                 if (oldline >= 0)\r
2598                         m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(m_nSelBlockEnd-m_nSelBlockStart)+1);\r
2599         }\r
2600 \r
2601         // now insert an empty block in left view\r
2602         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2603         {\r
2604                 leftstate.addedlines.push_back(m_nSelBlockEnd + 1);\r
2605                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd + 1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2606         }\r
2607         RecalcAllVertScrollBars();\r
2608         m_pwndLeft->SetModified();\r
2609         m_pwndRight->SetModified();\r
2610 }\r
2611 \r
2612 void CBaseView::UpdateCaret()\r
2613 {\r
2614         if (m_ptCaretPos.y >= GetLineCount())\r
2615                 m_ptCaretPos.y = GetLineCount()-1;\r
2616         if (m_ptCaretPos.y < 0)\r
2617                 m_ptCaretPos.y = 0;\r
2618         if (m_ptCaretPos.x > GetLineLength(m_ptCaretPos.y))\r
2619                 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);\r
2620         if (m_ptCaretPos.x < 0)\r
2621                 m_ptCaretPos.x = 0;\r
2622 \r
2623         int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);\r
2624 \r
2625         if (m_bFocused && !m_bCaretHidden &&\r
2626                 m_ptCaretPos.y >= m_nTopLine &&\r
2627                 m_ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&\r
2628                 nCaretOffset >= m_nOffsetChar &&\r
2629                 nCaretOffset < (m_nOffsetChar+GetScreenChars()))\r
2630         {\r
2631                 CreateSolidCaret(2, GetLineHeight());\r
2632                 SetCaretPos(TextToClient(m_ptCaretPos));\r
2633                 ShowCaret();\r
2634         }\r
2635         else\r
2636         {\r
2637                 HideCaret();\r
2638         }\r
2639 }\r
2640 \r
2641 void CBaseView::EnsureCaretVisible()\r
2642 {\r
2643         int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);\r
2644 \r
2645         if (m_ptCaretPos.y < m_nTopLine)\r
2646                 ScrollAllToLine(m_ptCaretPos.y);\r
2647         if (m_ptCaretPos.y >= (m_nTopLine+GetScreenLines()))\r
2648                 ScrollAllToLine(m_ptCaretPos.y-GetScreenLines()+1);\r
2649         if (nCaretOffset < m_nOffsetChar)\r
2650                 ScrollToChar(nCaretOffset);\r
2651         if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))\r
2652                 ScrollToChar(nCaretOffset-GetScreenChars()+1);\r
2653 }\r
2654 \r
2655 int CBaseView::CalculateActualOffset(int nLineIndex, int nCharIndex) const\r
2656 {\r
2657         int nLength = GetLineLength(nLineIndex);\r
2658         ASSERT(nCharIndex >= 0);\r
2659         if (nCharIndex > nLength)\r
2660                 nCharIndex = nLength;\r
2661         LPCTSTR pszChars = GetLineChars(nLineIndex);\r
2662         int nOffset = 0;\r
2663         int nTabSize = GetTabSize();\r
2664         for (int I = 0; I < nCharIndex; I ++)\r
2665         {\r
2666                 if (pszChars[I] == _T('\t'))\r
2667                         nOffset += (nTabSize - nOffset % nTabSize);\r
2668                 else\r
2669                         nOffset++;\r
2670         }\r
2671         return nOffset;\r
2672 }\r
2673 \r
2674 int     CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset) const\r
2675 {\r
2676         int nLength = GetLineLength(nLineIndex);\r
2677         LPCTSTR pszLine = GetLineChars(nLineIndex);\r
2678         int nIndex = 0;\r
2679         int nOffset = 0;\r
2680         int nTabSize = GetTabSize();\r
2681         while (nOffset < nActualOffset && nIndex < nLength)\r
2682         {\r
2683                 if (pszLine[nIndex] == _T('\t'))\r
2684                         nOffset += (nTabSize - nOffset % nTabSize);\r
2685                 else\r
2686                         ++nOffset;\r
2687                 ++nIndex;\r
2688         }\r
2689         return nIndex;\r
2690 }\r
2691 \r
2692 POINT CBaseView::TextToClient(const POINT& point)\r
2693 {\r
2694         POINT pt;\r
2695         pt.y = max(0, (point.y - m_nTopLine) * GetLineHeight());\r
2696         pt.x = CalculateActualOffset(point.y, point.x);\r
2697 \r
2698         pt.x = (pt.x - m_nOffsetChar) * GetCharWidth() + GetMarginWidth();\r
2699         pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);\r
2700         return pt;\r
2701 }\r
2702 \r
2703 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)\r
2704 {\r
2705         CView::OnChar(nChar, nRepCnt, nFlags);\r
2706 \r
2707         if (m_bCaretHidden)\r
2708                 return;\r
2709 \r
2710         if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||\r
2711                 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)\r
2712                 return;\r
2713 \r
2714         if ((nChar > 31)||(nChar == VK_TAB))\r
2715         {\r
2716                 RemoveSelectedText();\r
2717                 AddUndoLine(m_ptCaretPos.y);\r
2718                 CString sLine = GetLineChars(m_ptCaretPos.y);\r
2719                 sLine.Insert(m_ptCaretPos.x, (wchar_t)nChar);\r
2720                 m_pViewData->SetLine(m_ptCaretPos.y, sLine);\r
2721                 m_pViewData->SetState(m_ptCaretPos.y, DIFFSTATE_EDITED);\r
2722                 m_ptCaretPos.x++;\r
2723                 UpdateGoalPos();\r
2724         }\r
2725         else if (nChar == VK_RETURN)\r
2726         {\r
2727                 // insert a new, fresh and empty line below the cursor\r
2728                 RemoveSelectedText();\r
2729                 AddUndoLine(m_ptCaretPos.y, true);\r
2730                 // move the cursor to the new line\r
2731                 m_ptCaretPos.y++;\r
2732                 m_ptCaretPos.x = 0;\r
2733                 UpdateGoalPos();\r
2734         }\r
2735         else\r
2736                 return; // Unknown control character -- ignore it.\r
2737         ClearSelection();\r
2738         EnsureCaretVisible();\r
2739         UpdateCaret();\r
2740         SetModified(true);\r
2741         Invalidate(FALSE);\r
2742 }\r
2743 \r
2744 void CBaseView::AddUndoLine(int nLine, bool bAddEmptyLine)\r
2745 {\r
2746         viewstate leftstate;\r
2747         viewstate rightstate;\r
2748         viewstate bottomstate;\r
2749         leftstate.AddLineFormView(m_pwndLeft, nLine, bAddEmptyLine);\r
2750         rightstate.AddLineFormView(m_pwndRight, nLine, bAddEmptyLine);\r
2751         bottomstate.AddLineFormView(m_pwndBottom, nLine, bAddEmptyLine);\r
2752         CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);\r
2753 }\r
2754 \r
2755 void CBaseView::AddEmptyLine(int nLineIndex)\r
2756 {\r
2757         if (m_pViewData == NULL)\r
2758                 return;\r
2759         if (!m_bCaretHidden)\r
2760         {\r
2761                 CString sPartLine = GetLineChars(nLineIndex);\r
2762                 m_pViewData->SetLine(nLineIndex, sPartLine.Left(m_ptCaretPos.x));\r
2763                 sPartLine = sPartLine.Mid(m_ptCaretPos.x);\r
2764                 m_pViewData->InsertData(nLineIndex+1, sPartLine, DIFFSTATE_EDITED, -1, m_pViewData->GetLineEnding(nLineIndex));\r
2765         }\r
2766         else\r
2767                 m_pViewData->InsertData(nLineIndex+1, _T(""), DIFFSTATE_EDITED, -1, m_pViewData->GetLineEnding(nLineIndex));\r
2768         Invalidate(FALSE);\r
2769 }\r
2770 \r
2771 void CBaseView::RemoveLine(int nLineIndex)\r
2772 {\r
2773         if (m_pViewData == NULL)\r
2774                 return;\r
2775         m_pViewData->RemoveData(nLineIndex);\r
2776         if (m_ptCaretPos.y >= GetLineCount())\r
2777                 m_ptCaretPos.y = GetLineCount()-1;\r
2778         Invalidate(FALSE);\r
2779 }\r
2780 \r
2781 void CBaseView::RemoveSelectedText()\r
2782 {\r
2783         if (m_pViewData == NULL)\r
2784                 return;\r
2785         if (!HasTextSelection())\r
2786