OSDN Git Service

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