OSDN Git Service

add TortoisePlink
[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 #if 0\r
1380         while (diff)\r
1381         {\r
1382                 apr_off_t len = diff->original_length;\r
1383 \r
1384                 CString s;\r
1385                 for (int i = 0; i < len; ++i)\r
1386                 {\r
1387                         s += m_svnlinediff.m_line1tokens[lineoffset].c_str();\r
1388                         lineoffset++;\r
1389                 }\r
1390                 bool isModified = diff->type == svn_diff__type_diff_modified;\r
1391                 DrawText(pDC, rc, (LPCTSTR)s, s.GetLength(), nLineIndex, origin, true, isModified);\r
1392                 origin.x += pDC->GetTextExtent(s).cx;\r
1393 \r
1394                 if (isModified && (len < diff->modified_length))\r
1395                         removedPositions.push_back(origin.x - 1);\r
1396 \r
1397                 diff = diff->next;\r
1398         }\r
1399         // Draw vertical bars at removed chunks' positions.\r
1400         for (std::deque<int>::iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)\r
1401                 pDC->FillSolidRect(*it, rc.top, 1, rc.Height(), m_InlineRemovedBk);\r
1402 #endif\r
1403         return true;\r
1404 }\r
1405 \r
1406 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)\r
1407 {\r
1408         if (nLineIndex >= GetLineCount())\r
1409                 nLineIndex = -1;\r
1410         ASSERT(nLineIndex >= -1);\r
1411 \r
1412         if ((nLineIndex == -1) || !m_pViewData)\r
1413         {\r
1414                 // Draw line beyond the text\r
1415                 COLORREF crBkgnd, crText;\r
1416                 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);\r
1417                 pDC->FillSolidRect(rc, crBkgnd);\r
1418                 return;\r
1419         }\r
1420 \r
1421         DiffStates diffState = m_pViewData->GetState(nLineIndex);\r
1422         COLORREF crBkgnd, crText;\r
1423         CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);\r
1424 \r
1425         if (diffState == DIFFSTATE_CONFLICTED)\r
1426         {\r
1427                 // conflicted lines are shown without 'text' on them\r
1428                 CRect rect = rc;\r
1429                 pDC->FillSolidRect(rc, crBkgnd);\r
1430                 // now draw some faint text patterns\r
1431                 pDC->SetTextColor(IntenseColor(130, crBkgnd));\r
1432                 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);\r
1433                 DrawBlockLine(pDC, rc, nLineIndex);\r
1434                 return;\r
1435         }\r
1436 \r
1437         CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);\r
1438         int nLength = GetLineLength(nLineIndex);\r
1439         if (nLength == 0)\r
1440         {\r
1441                 // Draw the empty line\r
1442                 pDC->FillSolidRect(rc, crBkgnd);\r
1443                 DrawBlockLine(pDC, rc, nLineIndex);\r
1444                 DrawLineEnding(pDC, rc, nLineIndex, origin);\r
1445                 return;\r
1446         }\r
1447         LPCTSTR pszChars = GetLineChars(nLineIndex);\r
1448         if (pszChars == NULL)\r
1449                 return;\r
1450 \r
1451         CheckOtherView();\r
1452 \r
1453         // Draw the line\r
1454 \r
1455         pDC->SelectObject(GetFont(FALSE, FALSE, IsLineRemoved(nLineIndex)));\r
1456         CString line;\r
1457         ExpandChars(pszChars, 0, nLength, line);\r
1458 \r
1459         int nWidth = rc.right - origin.x;\r
1460         int savedx = origin.x;\r
1461         bool bInlineDiffDrawn =\r
1462                 nWidth > 0 && diffState != DIFFSTATE_NORMAL &&\r
1463                 DrawInlineDiff(pDC, rc, nLineIndex, line, origin);\r
1464 \r
1465         if (!bInlineDiffDrawn)\r
1466         {\r
1467                 int nCount = min(line.GetLength(), nWidth / GetCharWidth() + 1);\r
1468                 DrawText(pDC, rc, line, nCount, nLineIndex, origin, false, false);\r
1469         }\r
1470 \r
1471         origin.x = savedx + pDC->GetTextExtent(line).cx;\r
1472 \r
1473         // draw white space after the end of line\r
1474         CRect frect = rc;\r
1475         if (origin.x > frect.left)\r
1476                 frect.left = origin.x;\r
1477         if (bInlineDiffDrawn)\r
1478                 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);\r
1479         if (frect.right > frect.left)\r
1480                 pDC->FillSolidRect(frect, crBkgnd);\r
1481         // draw the whitespace chars\r
1482         if (m_bViewWhitespace)\r
1483         {\r
1484                 int xpos = 0;\r
1485                 int y = rc.top + (rc.bottom-rc.top)/2;\r
1486 \r
1487                 int nActualOffset = 0;\r
1488                 while ((nActualOffset < m_nOffsetChar) && (*pszChars))\r
1489                 {\r
1490                         if (*pszChars == _T('\t'))\r
1491                                 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());\r
1492                         else\r
1493                                 nActualOffset++;\r
1494                         pszChars++;\r
1495                 }\r
1496                 if (nActualOffset > m_nOffsetChar)\r
1497                         pszChars--;\r
1498 \r
1499                 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);\r
1500                 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);\r
1501                 while (*pszChars)\r
1502                 {\r
1503                         switch (*pszChars)\r
1504                         {\r
1505                         case _T('\t'):\r
1506                                 {\r
1507                                         // draw an arrow\r
1508                                         CPen * oldPen = pDC->SelectObject(&pen);\r
1509                                         int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();\r
1510                                         pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);\r
1511                                         pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);\r
1512                                         pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);\r
1513                                         pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);\r
1514                                         pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);\r
1515                                         xpos += nSpaces;\r
1516                                         pDC->SelectObject(oldPen);\r
1517                                 }\r
1518                                 break;\r
1519                         case _T(' '):\r
1520                                 {\r
1521                                         // draw a small dot\r
1522                                         CPen * oldPen = pDC->SelectObject(&pen2);\r
1523                                         pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);\r
1524                                         pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);\r
1525                                         xpos++;\r
1526                                         pDC->SelectObject(oldPen);\r
1527                                 }\r
1528                                 break;\r
1529                         default:\r
1530                                 xpos++;\r
1531                                 break;\r
1532                         }\r
1533                         pszChars++;\r
1534                 }\r
1535         }\r
1536         DrawBlockLine(pDC, rc, nLineIndex);\r
1537         DrawLineEnding(pDC, rc, nLineIndex, origin);\r
1538 }\r
1539 \r
1540 void CBaseView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)\r
1541 {\r
1542         if (nCount <= 0)\r
1543         {\r
1544                 line = _T("");\r
1545                 return;\r
1546         }\r
1547 \r
1548         int nTabSize = GetTabSize();\r
1549 \r
1550         int nActualOffset = 0;\r
1551         for (int i=0; i<nOffset; i++)\r
1552         {\r
1553                 if (pszChars[i] == _T('\t'))\r
1554                         nActualOffset += (nTabSize - nActualOffset % nTabSize);\r
1555                 else\r
1556                         nActualOffset ++;\r
1557         }\r
1558 \r
1559         pszChars += nOffset;\r
1560         int nLength = nCount;\r
1561 \r
1562         int nTabCount = 0;\r
1563         for (int i=0; i<nLength; i++)\r
1564         {\r
1565                 if (pszChars[i] == _T('\t'))\r
1566                         nTabCount ++;\r
1567         }\r
1568 \r
1569         LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);\r
1570         int nCurPos = 0;\r
1571         if (nTabCount > 0 || m_bViewWhitespace)\r
1572         {\r
1573                 for (int i=0; i<nLength; i++)\r
1574                 {\r
1575                         if (pszChars[i] == _T('\t'))\r
1576                         {\r
1577                                 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;\r
1578                                 while (nSpaces > 0)\r
1579                                 {\r
1580                                         pszBuf[nCurPos ++] = _T(' ');\r
1581                                         nSpaces --;\r
1582                                 }\r
1583                         }\r
1584                         else\r
1585                         {\r
1586                                 pszBuf[nCurPos] = pszChars[i];\r
1587                                 nCurPos ++;\r
1588                         }\r
1589                 }\r
1590         }\r
1591         else\r
1592         {\r
1593                 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);\r
1594                 nCurPos = nLength;\r
1595         }\r
1596         pszBuf[nCurPos] = 0;\r
1597         line.ReleaseBuffer();\r
1598 }\r
1599 \r
1600 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)\r
1601 {\r
1602         if ((m_pwndLeft)&&(m_pwndRight))\r
1603         {\r
1604                 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1605                 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1606         }\r
1607         else\r
1608         {\r
1609                 if (m_pwndLeft)\r
1610                         m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1611                 if (m_pwndRight)\r
1612                         m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1613         }\r
1614         if (m_pwndBottom)\r
1615                 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);\r
1616         if (m_pwndLocator)\r
1617                 m_pwndLocator->Invalidate();\r
1618 }\r
1619 \r
1620 void CBaseView::GoToLine(int nNewLine, BOOL bAll)\r
1621 {\r
1622         //almost the same as ScrollAllToLine, but try to put the line in the\r
1623         //middle of the view, not on top\r
1624         int nNewTopLine = nNewLine - GetScreenLines()/2;\r
1625         if (nNewTopLine < 0)\r
1626                 nNewTopLine = 0;\r
1627         if (m_pViewData)\r
1628         {\r
1629                 if (nNewTopLine >= m_pViewData->GetCount())\r
1630                         nNewTopLine = m_pViewData->GetCount()-1;\r
1631                 if (bAll)\r
1632                         ScrollAllToLine(nNewTopLine);\r
1633                 else\r
1634                         ScrollToLine(nNewTopLine);\r
1635         }\r
1636 }\r
1637 \r
1638 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)\r
1639 {\r
1640         return TRUE;\r
1641 }\r
1642 \r
1643 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)\r
1644 {\r
1645         if (CView::OnCreate(lpCreateStruct) == -1)\r
1646                 return -1;\r
1647 \r
1648         memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));\r
1649         //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));\r
1650         //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));\r
1651         m_lfBaseFont.lfHeight = 0;\r
1652         m_lfBaseFont.lfWeight = FW_NORMAL;\r
1653         m_lfBaseFont.lfItalic = FALSE;\r
1654         m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;\r
1655         m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;\r
1656         m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
1657         m_lfBaseFont.lfQuality = DEFAULT_QUALITY;\r
1658         m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;\r
1659 \r
1660         return 0;\r
1661 }\r
1662 \r
1663 void CBaseView::OnDestroy()\r
1664 {\r
1665         CView::OnDestroy();\r
1666         for (int i=0; i<MAXFONTS; i++)\r
1667         {\r
1668                 if (m_apFonts[i] != NULL)\r
1669                 {\r
1670                         m_apFonts[i]->DeleteObject();\r
1671                         delete m_apFonts[i];\r
1672                         m_apFonts[i] = NULL;\r
1673                 }\r
1674         }\r
1675         if (m_pCacheBitmap != NULL)\r
1676         {\r
1677                 delete m_pCacheBitmap;\r
1678                 m_pCacheBitmap = NULL;\r
1679         }\r
1680 }\r
1681 \r
1682 void CBaseView::OnSize(UINT nType, int cx, int cy)\r
1683 {\r
1684         if (m_pCacheBitmap != NULL)\r
1685         {\r
1686                 m_pCacheBitmap->DeleteObject();\r
1687                 delete m_pCacheBitmap;\r
1688                 m_pCacheBitmap = NULL;\r
1689         }\r
1690         // make sure the view header is redrawn\r
1691         CRect rcScroll;\r
1692         GetClientRect(&rcScroll);\r
1693         rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;\r
1694         InvalidateRect(&rcScroll, FALSE);\r
1695 \r
1696         m_nScreenLines = -1;\r
1697         m_nScreenChars = -1;\r
1698         RecalcVertScrollBar();\r
1699         RecalcHorzScrollBar();\r
1700         CView::OnSize(nType, cx, cy);\r
1701 }\r
1702 \r
1703 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)\r
1704 {\r
1705         if (m_pwndLeft)\r
1706                 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);\r
1707         if (m_pwndRight)\r
1708                 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);\r
1709         if (m_pwndBottom)\r
1710                 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);\r
1711         if (m_pwndLocator)\r
1712                 m_pwndLocator->Invalidate();\r
1713         return CView::OnMouseWheel(nFlags, zDelta, pt);\r
1714 }\r
1715 \r
1716 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)\r
1717 {\r
1718         if (GetKeyState(VK_CONTROL)&0x8000)\r
1719         {\r
1720                 // Ctrl-Wheel scrolls sideways\r
1721                 ScrollSide(-zDelta/30);\r
1722         }\r
1723         else\r
1724         {\r
1725                 int nLineCount = GetLineCount();\r
1726                 int nTopLine = m_nTopLine;\r
1727                 nTopLine -= (zDelta/30);\r
1728                 if (nTopLine < 0)\r
1729                         nTopLine = 0;\r
1730                 if (nTopLine >= nLineCount)\r
1731                         nTopLine = nLineCount - 1;\r
1732                 ScrollToLine(nTopLine, TRUE);\r
1733         }\r
1734 }\r
1735 \r
1736 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)\r
1737 {\r
1738         if (nHitTest == HTCLIENT)\r
1739         {\r
1740                 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));    // Set To Arrow Cursor\r
1741                 return TRUE;\r
1742         }\r
1743         return CView::OnSetCursor(pWnd, nHitTest, message);\r
1744 }\r
1745 \r
1746 void CBaseView::OnKillFocus(CWnd* pNewWnd)\r
1747 {\r
1748         CView::OnKillFocus(pNewWnd);\r
1749         m_bFocused = FALSE;\r
1750         UpdateCaret();\r
1751         Invalidate();\r
1752 }\r
1753 \r
1754 void CBaseView::OnSetFocus(CWnd* pOldWnd)\r
1755 {\r
1756         CView::OnSetFocus(pOldWnd);\r
1757         m_bFocused = TRUE;\r
1758         UpdateCaret();\r
1759         Invalidate();\r
1760 }\r
1761 \r
1762 int CBaseView::GetLineFromPoint(CPoint point)\r
1763 {\r
1764         ScreenToClient(&point);\r
1765         return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);\r
1766 }\r
1767 \r
1768 bool CBaseView::OnContextMenu(CPoint /*point*/, int /*nLine*/, DiffStates /*state*/)\r
1769 {\r
1770         return false;\r
1771 }\r
1772 \r
1773 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)\r
1774 {\r
1775         int nLine = GetLineFromPoint(point);\r
1776 \r
1777         if (!m_pViewData)\r
1778                 return;\r
1779         if (m_nSelBlockEnd >= GetLineCount())\r
1780                 m_nSelBlockEnd = GetLineCount()-1;\r
1781         if ((nLine <= m_pViewData->GetCount())&&(nLine > m_nTopLine))\r
1782         {\r
1783                 int nIndex = nLine - 1;\r
1784                 DiffStates state = m_pViewData->GetState(nIndex);\r
1785                 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))\r
1786                 {\r
1787                         // if there's nothing selected, or if the selection is outside the window then\r
1788                         // select the diff block under the cursor.\r
1789                         if (((m_nSelBlockStart<0)&&(m_nSelBlockEnd<0))||\r
1790                                 ((m_nSelBlockEnd < m_nTopLine)||(m_nSelBlockStart > m_nTopLine+m_nScreenLines)))\r
1791                         {\r
1792                                 while (nIndex >= 0)\r
1793                                 {\r
1794                                         if (nIndex == 0)\r
1795                                         {\r
1796                                                 nIndex--;\r
1797                                                 break;\r
1798                                         }\r
1799                                         if (state != m_pViewData->GetState(--nIndex))\r
1800                                                 break;\r
1801                                 }\r
1802                                 m_nSelBlockStart = nIndex+1;\r
1803                                 while (nIndex < (m_pViewData->GetCount()-1))\r
1804                                 {\r
1805                                         if (state != m_pViewData->GetState(++nIndex))\r
1806                                                 break;\r
1807                                 }\r
1808                                 if ((nIndex == (m_pViewData->GetCount()-1))&&(state == m_pViewData->GetState(nIndex)))\r
1809                                         m_nSelBlockEnd = nIndex;\r
1810                                 else\r
1811                                         m_nSelBlockEnd = nIndex-1;\r
1812                                 SetupSelection(m_nSelBlockStart, m_nSelBlockEnd);\r
1813                                 m_ptCaretPos.x = 0;\r
1814                                 m_ptCaretPos.y = nLine - 1;\r
1815                                 UpdateCaret();\r
1816                         }\r
1817                 }\r
1818                 if (((state == DIFFSTATE_NORMAL)||(state == DIFFSTATE_UNKNOWN)) &&\r
1819                         (m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))\r
1820                 {\r
1821                         // find a more 'relevant' state in the selection\r
1822                         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; ++i)\r
1823                         {\r
1824                                 state = m_pViewData->GetState(i);\r
1825                                 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))\r
1826                                         break;\r
1827                         }\r
1828                 }\r
1829                 bool bKeepSelection = OnContextMenu(point, nLine, state);\r
1830                 if (! bKeepSelection)\r
1831                         ClearSelection();\r
1832                 RefreshViews();\r
1833         }\r
1834 }\r
1835 \r
1836 void CBaseView::RefreshViews()\r
1837 {\r
1838         if (m_pwndLeft)\r
1839         {\r
1840                 m_pwndLeft->UpdateStatusBar();\r
1841                 m_pwndLeft->Invalidate();\r
1842         }\r
1843         if (m_pwndRight)\r
1844         {\r
1845                 m_pwndRight->UpdateStatusBar();\r
1846                 m_pwndRight->Invalidate();\r
1847         }\r
1848         if (m_pwndBottom)\r
1849         {\r
1850                 m_pwndBottom->UpdateStatusBar();\r
1851                 m_pwndBottom->Invalidate();\r
1852         }\r
1853         if (m_pwndLocator)\r
1854                 m_pwndLocator->Invalidate();\r
1855 }\r
1856 \r
1857 void CBaseView::GoToFirstDifference()\r
1858 {\r
1859         int nCenterPos = 0;\r
1860         if ((m_pViewData)&&(0 < m_pViewData->GetCount()))\r
1861         {\r
1862                 while (nCenterPos < m_pViewData->GetCount())\r
1863                 {\r
1864                         DiffStates linestate = m_pViewData->GetState(nCenterPos);\r
1865                         if ((linestate != DIFFSTATE_NORMAL) &&\r
1866                                 (linestate != DIFFSTATE_UNKNOWN))\r
1867                                 break;\r
1868                         nCenterPos++;\r
1869                 }\r
1870                 if (nCenterPos >= m_pViewData->GetCount())\r
1871                         nCenterPos = m_pViewData->GetCount()-1;\r
1872                 int nTopPos = nCenterPos - (GetScreenLines()/2);\r
1873                 if (nTopPos < 0)\r
1874                         nTopPos = 0;\r
1875                 if (m_pwndLeft)\r
1876                 {\r
1877                         m_pwndLeft->m_ptCaretPos.x = 0;\r
1878                         m_pwndLeft->m_ptCaretPos.y = nCenterPos;\r
1879                         m_pwndLeft->m_nCaretGoalPos = 0;\r
1880                 }\r
1881                 if (m_pwndRight)\r
1882                 {\r
1883                         m_pwndRight->m_ptCaretPos.x = 0;\r
1884                         m_pwndRight->m_ptCaretPos.y = nCenterPos;\r
1885                         m_pwndRight->m_nCaretGoalPos = 0;\r
1886                 }\r
1887                 if (m_pwndBottom)\r
1888                 {\r
1889                         m_pwndBottom->m_ptCaretPos.x = 0;\r
1890                         m_pwndBottom->m_ptCaretPos.y = nCenterPos;\r
1891                         m_pwndBottom->m_nCaretGoalPos = 0;\r
1892                 }\r
1893                 ScrollAllToLine(nTopPos);\r
1894                 RecalcAllVertScrollBars(TRUE);\r
1895         }\r
1896 }\r
1897 \r
1898 void CBaseView::HiglightLines(int start, int end /* = -1 */)\r
1899 {\r
1900         ClearSelection();\r
1901         m_nSelBlockStart = start;\r
1902         if (end < 0)\r
1903                 end = start;\r
1904         m_nSelBlockEnd = end;\r
1905         m_ptCaretPos.x = 0;\r
1906         m_ptCaretPos.y = start;\r
1907         UpdateCaret();\r
1908         Invalidate();\r
1909 }\r
1910 \r
1911 void CBaseView::SetupSelection(int start, int end)\r
1912 {\r
1913         if (IsBottomViewGood())\r
1914         {\r
1915                 m_pwndBottom->m_nSelBlockStart = start;\r
1916                 m_pwndBottom->m_nSelBlockEnd = end;\r
1917                 m_pwndBottom->Invalidate();\r
1918         }\r
1919         if (IsLeftViewGood())\r
1920         {\r
1921                 m_pwndLeft->m_nSelBlockStart = start;\r
1922                 m_pwndLeft->m_nSelBlockEnd = end;\r
1923                 m_pwndLeft->Invalidate();\r
1924         }\r
1925         if (IsRightViewGood())\r
1926         {\r
1927                 m_pwndRight->m_nSelBlockStart = start;\r
1928                 m_pwndRight->m_nSelBlockEnd = end;\r
1929                 m_pwndRight->Invalidate();\r
1930         }\r
1931 }\r
1932 \r
1933 void CBaseView::OnMergePreviousconflict()\r
1934 {\r
1935         SelectNextBlock(-1, true);\r
1936 }\r
1937 \r
1938 void CBaseView::OnMergeNextconflict()\r
1939 {\r
1940         SelectNextBlock(1, true);\r
1941 }\r
1942 \r
1943 void CBaseView::OnMergeNextdifference()\r
1944 {\r
1945         SelectNextBlock(1, false);\r
1946 }\r
1947 \r
1948 void CBaseView::OnMergePreviousdifference()\r
1949 {\r
1950         SelectNextBlock(-1, false);\r
1951 }\r
1952 \r
1953 void CBaseView::SelectNextBlock(int nDirection, bool bConflict)\r
1954 {\r
1955         if (! m_pViewData)\r
1956                 return;\r
1957 \r
1958         if (m_pViewData->GetCount() == 0)\r
1959                 return;\r
1960 \r
1961         int nCenterPos = m_ptCaretPos.y;\r
1962         int nLimit = 0;\r
1963         if (nDirection > 0)\r
1964                 nLimit = m_pViewData->GetCount() - 1;\r
1965 \r
1966         if (nCenterPos >= m_pViewData->GetCount())\r
1967                 nCenterPos = m_pViewData->GetCount()-1;\r
1968 \r
1969         // Find end of current block\r
1970         DiffStates state = m_pViewData->GetState(nCenterPos);\r
1971         while ((nCenterPos != nLimit) && \r
1972                    (m_pViewData->GetState(nCenterPos)==state))\r
1973                 nCenterPos += nDirection;\r
1974 \r
1975         // Find next diff/conflict block\r
1976         while (nCenterPos != nLimit)\r
1977         {\r
1978                 DiffStates linestate = m_pViewData->GetState(nCenterPos);\r
1979                 if (!bConflict &&\r
1980                         (linestate != DIFFSTATE_NORMAL) &&\r
1981                         (linestate != DIFFSTATE_UNKNOWN))\r
1982                         break;\r
1983                 if (bConflict &&\r
1984                         ((linestate == DIFFSTATE_CONFLICTADDED) ||\r
1985                          (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||\r
1986                          (linestate == DIFFSTATE_CONFLICTED) ||\r
1987                          (linestate == DIFFSTATE_CONFLICTEMPTY)))\r
1988                         break;\r
1989 \r
1990                 nCenterPos += nDirection;\r
1991         }\r
1992 \r
1993         // Find end of new block\r
1994         state = m_pViewData->GetState(nCenterPos);\r
1995         int nBlockEnd = nCenterPos;\r
1996         while ((nBlockEnd != nLimit) &&  \r
1997                    (state == m_pViewData->GetState(nBlockEnd + nDirection)))\r
1998                 nBlockEnd += nDirection;\r
1999 \r
2000         int nTopPos = nCenterPos - (GetScreenLines()/2);\r
2001         if (nTopPos < 0)\r
2002                 nTopPos = 0;\r
2003 \r
2004         m_ptCaretPos.x = 0;\r
2005         m_ptCaretPos.y = nCenterPos;\r
2006         ClearSelection();\r
2007         if (nDirection > 0)\r
2008                 SetupSelection(nCenterPos, nBlockEnd);\r
2009         else\r
2010                 SetupSelection(nBlockEnd, nCenterPos);\r
2011 \r
2012         ScrollAllToLine(nTopPos, FALSE);\r
2013         RecalcAllVertScrollBars(TRUE);\r
2014         m_nCaretGoalPos = 0;\r
2015         UpdateCaret();\r
2016         ShowDiffLines(nCenterPos);\r
2017 }\r
2018 \r
2019 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)\r
2020 {\r
2021         // need to handle both ANSI and UNICODE versions of the message\r
2022         TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;\r
2023         TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;\r
2024         CString strTipText;\r
2025         UINT nID = (UINT)pNMHDR->idFrom;\r
2026         if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||\r
2027                 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))\r
2028         {\r
2029                 // idFrom is actually the HWND of the tool\r
2030                 nID = ::GetDlgCtrlID((HWND)nID);\r
2031         }\r
2032 \r
2033         if (pNMHDR->idFrom == (UINT)m_hWnd)\r
2034         {\r
2035                 if (m_sWindowName.Left(2).Compare(_T("* "))==0)\r
2036                 {\r
2037                         strTipText = m_sWindowName.Mid(2) + _T("\r\n") + m_sFullFilePath;\r
2038                 }\r
2039                 else\r
2040                 {\r
2041                         strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;\r
2042                 }\r
2043         }\r
2044         else\r
2045                 return FALSE;\r
2046         \r
2047         *pResult = 0;\r
2048         if (strTipText.IsEmpty())\r
2049                 return TRUE;\r
2050 \r
2051         if (pNMHDR->code == TTN_NEEDTEXTA)\r
2052         {\r
2053                 pTTTA->lpszText = m_szTip;\r
2054                 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);\r
2055         }\r
2056         else\r
2057         {\r
2058                 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);\r
2059                 pTTTW->lpszText = m_wszTip;\r
2060         }\r
2061 \r
2062         return TRUE;    // message was handled\r
2063 }\r
2064 \r
2065 \r
2066 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const\r
2067 {\r
2068         CRect rcClient;\r
2069         GetClientRect(rcClient);\r
2070         CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);\r
2071         if (textrect.PtInRect(point))\r
2072         {\r
2073                 // inside the header part of the view (showing the filename)\r
2074                 pTI->hwnd = this->m_hWnd;\r
2075                 this->GetClientRect(&pTI->rect);\r
2076                 pTI->uFlags  |= TTF_ALWAYSTIP | TTF_IDISHWND;\r
2077                 pTI->uId = (UINT)m_hWnd;\r
2078                 pTI->lpszText = LPSTR_TEXTCALLBACK;\r
2079 \r
2080                 // we want multi line tooltips\r
2081                 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;\r
2082                 if (pToolTip->GetSafeHwnd() != NULL)\r
2083                 {\r
2084                         pToolTip->SetMaxTipWidth(INT_MAX);\r
2085                 }\r
2086 \r
2087                 return 1;\r
2088         }\r
2089         return -1;\r
2090 }\r
2091 \r
2092 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)\r
2093 {\r
2094         bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);\r
2095         bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);\r
2096         switch (nChar)\r
2097         {\r
2098         case VK_PRIOR:\r
2099                 {\r
2100                         m_ptCaretPos.y -= GetScreenLines();\r
2101                         m_ptCaretPos.y = max(m_ptCaretPos.y, 0);\r
2102                         m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);\r
2103                         if (bShift)\r
2104                                 AdjustSelection();\r
2105                         else\r
2106                                 ClearSelection();\r
2107                         UpdateCaret();\r
2108                         EnsureCaretVisible();\r
2109                         ShowDiffLines(m_ptCaretPos.y);\r
2110                 }\r
2111                 break;\r
2112         case VK_NEXT:\r
2113                 {\r
2114                         m_ptCaretPos.y += GetScreenLines();\r
2115                         if (m_ptCaretPos.y >= GetLineCount())\r
2116                                 m_ptCaretPos.y = GetLineCount()-1;\r
2117                         m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);\r
2118                         if (bShift)\r
2119                                 AdjustSelection();\r
2120                         else\r
2121                                 ClearSelection();\r
2122                         UpdateCaret();\r
2123                         EnsureCaretVisible();\r
2124                         ShowDiffLines(m_ptCaretPos.y);\r
2125                 }\r
2126                 break;\r
2127         case VK_HOME:\r
2128                 {\r
2129                         if (bControl)\r
2130                         {\r
2131                                 ScrollAllToLine(0);\r
2132                                 m_ptCaretPos.x = 0;\r
2133                                 m_ptCaretPos.y = 0;\r
2134                                 m_nCaretGoalPos = 0;\r
2135                                 if (bShift)\r
2136                                         AdjustSelection();\r
2137                                 else\r
2138                                         ClearSelection();\r
2139                                 UpdateCaret();\r
2140                         }\r
2141                         else\r
2142                         {\r
2143                                 m_ptCaretPos.x = 0;\r
2144                                 m_nCaretGoalPos = 0;\r
2145                                 if (bShift)\r
2146                                         AdjustSelection();\r
2147                                 else\r
2148                                         ClearSelection();\r
2149                                 EnsureCaretVisible();\r
2150                                 UpdateCaret();\r
2151                         }\r
2152                 }\r
2153                 break;\r
2154         case VK_END:\r
2155                 {\r
2156                         if (bControl)\r
2157                         {\r
2158                                 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());\r
2159                                 m_ptCaretPos.y = GetLineCount()-1;\r
2160                                 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);\r
2161                                 UpdateGoalPos();\r
2162                                 if (bShift)\r
2163                                         AdjustSelection();\r
2164                                 else\r
2165                                         ClearSelection();\r
2166                                 UpdateCaret();\r
2167                         }\r
2168                         else\r
2169                         {\r
2170                                 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);\r
2171                                 UpdateGoalPos();\r
2172                                 if (bShift)\r
2173                                         AdjustSelection();\r
2174                                 else\r
2175                                         ClearSelection();\r
2176                                 EnsureCaretVisible();\r
2177                                 UpdateCaret();\r
2178                         }\r
2179                 }\r
2180                 break;\r
2181         case VK_BACK:\r
2182                 {\r
2183                         if (m_bCaretHidden)\r
2184                                 break;\r
2185 \r
2186                         if (! HasTextSelection()) {\r
2187                                 if (m_ptCaretPos.y == 0 && m_ptCaretPos.x == 0)\r
2188                                         break;\r
2189                                 m_ptSelectionEndPos = m_ptCaretPos;\r
2190                                 MoveCaretLeft();\r
2191                                 m_ptSelectionStartPos = m_ptCaretPos;\r
2192                         }\r
2193                         RemoveSelectedText();\r
2194                 }\r
2195                 break;\r
2196         case VK_DELETE:\r
2197                 {\r
2198                         if (m_bCaretHidden)\r
2199                                 break;\r
2200 \r
2201                         if (! HasTextSelection()) {\r
2202                                 if (! MoveCaretRight())\r
2203                                         break;\r
2204                                 m_ptSelectionEndPos = m_ptCaretPos;\r
2205                                 MoveCaretLeft();\r
2206                                 m_ptSelectionStartPos = m_ptCaretPos;\r
2207                         }\r
2208                         RemoveSelectedText();\r
2209                 }\r
2210                 break;\r
2211         }\r
2212         CView::OnKeyDown(nChar, nRepCnt, nFlags);\r
2213 }\r
2214 \r
2215 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)\r
2216 {\r
2217         int nClickedLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);\r
2218         nClickedLine--;         //we need the index\r
2219         if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))\r
2220         {\r
2221                 m_ptCaretPos.y = nClickedLine;\r
2222                 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());\r
2223                 UpdateGoalPos();\r
2224 \r
2225                 if (nFlags & MK_SHIFT)\r
2226                         AdjustSelection();\r
2227                 else\r
2228                 {\r
2229                         ClearSelection();\r
2230                         SetupSelection(m_ptCaretPos.y, m_ptCaretPos.y);\r
2231                 }\r
2232 \r
2233                 UpdateCaret();\r
2234 \r
2235                 Invalidate();\r
2236         }\r
2237 \r
2238         CView::OnLButtonDown(nFlags, point);\r
2239 }\r
2240 \r
2241 void CBaseView::OnEditCopy()\r
2242 {\r
2243         if ((m_ptSelectionStartPos.x == m_ptSelectionEndPos.x)&&(m_ptSelectionStartPos.y == m_ptSelectionEndPos.y))\r
2244                 return;\r
2245         // first store the selected lines in one CString\r
2246         CString sCopyData;\r
2247         for (int i=m_ptSelectionStartPos.y; i<=m_ptSelectionEndPos.y; i++)\r
2248         {\r
2249                 switch (m_pViewData->GetState(i))\r
2250                 {\r
2251                 case DIFFSTATE_EMPTY:\r
2252                         break;\r
2253                 case DIFFSTATE_UNKNOWN:\r
2254                 case DIFFSTATE_NORMAL:\r
2255                 case DIFFSTATE_REMOVED:\r
2256                 case DIFFSTATE_REMOVEDWHITESPACE:\r
2257                 case DIFFSTATE_ADDED:\r
2258                 case DIFFSTATE_ADDEDWHITESPACE:\r
2259                 case DIFFSTATE_WHITESPACE:\r
2260                 case DIFFSTATE_WHITESPACE_DIFF:\r
2261                 case DIFFSTATE_CONFLICTED:\r
2262                 case DIFFSTATE_CONFLICTED_IGNORED:\r
2263                 case DIFFSTATE_CONFLICTADDED:\r
2264                 case DIFFSTATE_CONFLICTEMPTY:\r
2265                 case DIFFSTATE_CONFLICTRESOLVED:\r
2266                 case DIFFSTATE_IDENTICALREMOVED:\r
2267                 case DIFFSTATE_IDENTICALADDED:\r
2268                 case DIFFSTATE_THEIRSREMOVED:\r
2269                 case DIFFSTATE_THEIRSADDED:\r
2270                 case DIFFSTATE_YOURSREMOVED:\r
2271                 case DIFFSTATE_YOURSADDED:\r
2272                 case DIFFSTATE_EDITED:\r
2273                         sCopyData += m_pViewData->GetLine(i);\r
2274                         sCopyData += _T("\r\n");\r
2275                         break;\r
2276                 }\r
2277         }\r
2278         // remove the last \r\n\r
2279         sCopyData = sCopyData.Left(sCopyData.GetLength()-2);\r
2280         // remove the non-selected chars from the first line\r
2281         sCopyData = sCopyData.Mid(m_ptSelectionStartPos.x);\r
2282         // remove the non-selected chars from the last line\r
2283         int lastLinePos = sCopyData.ReverseFind('\n');\r
2284         lastLinePos += 1;\r
2285         if (lastLinePos == 0)\r
2286                 lastLinePos -= m_ptSelectionStartPos.x;\r
2287         sCopyData = sCopyData.Left(lastLinePos+m_ptSelectionEndPos.x);\r
2288         if (!sCopyData.IsEmpty())\r
2289         {\r
2290                 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);\r
2291         }\r
2292 }\r
2293 \r
2294 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)\r
2295 {\r
2296         int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);\r
2297         nMouseLine--;           //we need the index\r
2298         if (nMouseLine < -1)\r
2299         {\r
2300                 nMouseLine = -1;\r
2301         }\r
2302         ShowDiffLines(nMouseLine);\r
2303 \r
2304         if (nFlags & MK_LBUTTON)\r
2305         {\r
2306                 if (((m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))&&\r
2307                         ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))\r
2308                 {\r
2309                         m_ptCaretPos.y = nMouseLine;\r
2310                         m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());\r
2311                         UpdateGoalPos();\r
2312                         AdjustSelection();\r
2313                         UpdateCaret();\r
2314                         Invalidate();\r
2315                         UpdateWindow();\r
2316                 }\r
2317         }\r
2318 \r
2319         if (!m_bMouseWithin)\r
2320         { \r
2321                 m_bMouseWithin = TRUE;\r
2322                 TRACKMOUSEEVENT tme;\r
2323                 tme.cbSize = sizeof(TRACKMOUSEEVENT);\r
2324                 tme.dwFlags = TME_LEAVE;\r
2325                 tme.hwndTrack = m_hWnd;\r
2326                 _TrackMouseEvent(&tme);\r
2327         }\r
2328 \r
2329         CView::OnMouseMove(nFlags, point);\r
2330 }\r
2331 \r
2332 void CBaseView::OnMouseLeave()\r
2333 {\r
2334         ShowDiffLines(-1);\r
2335         m_bMouseWithin = FALSE;\r
2336 \r
2337         CView::OnMouseLeave();\r
2338 }\r
2339 \r
2340 void CBaseView::SelectLines(int nLine1, int nLine2)\r
2341 {\r
2342         if (nLine2 == -1)\r
2343                 nLine2 = nLine1;\r
2344         m_nSelBlockStart = nLine1;\r
2345         m_nSelBlockEnd = nLine2;\r
2346         Invalidate();\r
2347 }\r
2348 \r
2349 void CBaseView::ShowDiffLines(int nLine)\r
2350 {\r
2351         if ((nLine >= m_nTopLine)&&(nLine < GetLineCount()))\r
2352         {\r
2353                 if ((m_pwndRight)&&(m_pwndRight->m_pViewData)&&(m_pwndLeft)&&(m_pwndLeft->m_pViewData)&&(!m_pMainFrame->m_bOneWay))\r
2354                 {\r
2355                         nLine = (nLine > m_pwndRight->m_pViewData->GetCount() ? -1 : nLine);\r
2356                         nLine = (nLine > m_pwndLeft->m_pViewData->GetCount() ? -1 : nLine);\r
2357 \r
2358                         if (nLine >= 0)\r
2359                         {\r
2360                                 if (nLine != m_nMouseLine)\r
2361                                 {\r
2362                                         m_nMouseLine = nLine;\r
2363                                         if (nLine >= GetLineCount())\r
2364                                                 nLine = -1;\r
2365                                         m_pwndLineDiffBar->ShowLines(nLine);\r
2366                                 }\r
2367                         }\r
2368                 }\r
2369         }\r
2370         else\r
2371         {\r
2372                 m_pwndLineDiffBar->ShowLines(nLine);\r
2373         }\r
2374 }\r
2375 \r
2376 void CBaseView::UseTheirAndYourBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)\r
2377 {\r
2378         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2379                 return;\r
2380         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2381         {\r
2382                 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);\r
2383                 m_pwndBottom->m_pViewData->SetLine(i, m_pwndLeft->m_pViewData->GetLine(i));\r
2384                 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);\r
2385                 m_pwndBottom->m_pViewData->SetState(i, m_pwndLeft->m_pViewData->GetState(i));\r
2386                 if (m_pwndBottom->IsLineConflicted(i))\r
2387                 {\r
2388                         if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2389                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2390                         else\r
2391                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);\r
2392                 }\r
2393         }\r
2394 \r
2395         // your block is done, now insert their block\r
2396         int index = m_nSelBlockEnd+1;\r
2397         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2398         {\r
2399                 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2400                 m_pwndBottom->m_pViewData->InsertData(index, m_pwndRight->m_pViewData->GetData(i));\r
2401                 if (m_pwndBottom->IsLineConflicted(index))\r
2402                 {\r
2403                         if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2404                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2405                         else\r
2406                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);\r
2407                 }\r
2408                 index++;\r
2409         }\r
2410         // adjust line numbers\r
2411         for (int i=m_nSelBlockEnd+1; i<GetLineCount(); ++i)\r
2412         {\r
2413                 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);\r
2414                 if (oldline >= 0)\r
2415                         m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));\r
2416         }\r
2417 \r
2418         // now insert an empty block in both yours and theirs\r
2419         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2420         {\r
2421                 leftstate.addedlines.push_back(m_nSelBlockStart);\r
2422                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2423                 m_pwndRight->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2424                 rightstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2425         }\r
2426         RecalcAllVertScrollBars();\r
2427         m_pwndBottom->SetModified();\r
2428         m_pwndLeft->SetModified();\r
2429         m_pwndRight->SetModified();\r
2430 }\r
2431 \r
2432 void CBaseView::UseYourAndTheirBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)\r
2433 {\r
2434         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2435                 return;\r
2436         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2437         {\r
2438                 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);\r
2439                 m_pwndBottom->m_pViewData->SetLine(i, m_pwndRight->m_pViewData->GetLine(i));\r
2440                 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);\r
2441                 m_pwndBottom->m_pViewData->SetState(i, m_pwndRight->m_pViewData->GetState(i));\r
2442                 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);\r
2443                 if (m_pwndBottom->IsLineConflicted(i))\r
2444                 {\r
2445                         if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2446                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2447                         else\r
2448                                 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);\r
2449                 }\r
2450                 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);\r
2451         }\r
2452 \r
2453         // your block is done, now insert their block\r
2454         int index = m_nSelBlockEnd+1;\r
2455         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2456         {\r
2457                 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2458                 m_pwndBottom->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));\r
2459                 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);\r
2460                 if (m_pwndBottom->IsLineConflicted(index))\r
2461                 {\r
2462                         if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)\r
2463                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);\r
2464                         else\r
2465                                 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);\r
2466                 }\r
2467                 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);\r
2468                 index++;\r
2469         }\r
2470         // adjust line numbers\r
2471         for (int i=m_nSelBlockEnd+1; i<m_pwndBottom->GetLineCount(); ++i)\r
2472         {\r
2473                 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);\r
2474                 if (oldline >= 0)\r
2475                         m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));\r
2476         }\r
2477 \r
2478         // now insert an empty block in both yours and theirs\r
2479         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2480         {\r
2481                 leftstate.addedlines.push_back(m_nSelBlockStart);\r
2482                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2483                 m_pwndRight->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2484                 rightstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2485         }\r
2486 \r
2487         RecalcAllVertScrollBars();\r
2488         m_pwndBottom->SetModified();\r
2489         m_pwndLeft->SetModified();\r
2490         m_pwndRight->SetModified();\r
2491 }\r
2492 \r
2493 void CBaseView::UseBothRightFirst(viewstate &rightstate, viewstate &leftstate)\r
2494 {\r
2495         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2496                 return;\r
2497         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2498         {\r
2499                 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);\r
2500                 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);\r
2501         }\r
2502 \r
2503         // your block is done, now insert their block\r
2504         int index = m_nSelBlockEnd+1;\r
2505         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2506         {\r
2507                 rightstate.addedlines.push_back(m_nSelBlockEnd+1);\r
2508                 m_pwndRight->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));\r
2509                 m_pwndRight->m_pViewData->SetState(index++, DIFFSTATE_THEIRSADDED);\r
2510         }\r
2511         // adjust line numbers\r
2512         index--;\r
2513         for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)\r
2514         {\r
2515                 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);\r
2516                 if (oldline >= 0)\r
2517                         m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));\r
2518         }\r
2519 \r
2520         // now insert an empty block in the left view\r
2521         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2522         {\r
2523                 leftstate.addedlines.push_back(m_nSelBlockStart);\r
2524                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2525         }\r
2526         RecalcAllVertScrollBars();\r
2527         m_pwndLeft->SetModified();\r
2528         m_pwndRight->SetModified();\r
2529 }\r
2530 \r
2531 void CBaseView::UseBothLeftFirst(viewstate &rightstate, viewstate &leftstate)\r
2532 {\r
2533         if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))\r
2534                 return;\r
2535         // get line number from just before the block\r
2536         long linenumber = 0;\r
2537         if (m_nSelBlockStart > 0)\r
2538                 linenumber = m_pwndRight->m_pViewData->GetLineNumber(m_nSelBlockStart-1);\r
2539         linenumber++;\r
2540         for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)\r
2541         {\r
2542                 rightstate.addedlines.push_back(m_nSelBlockStart);\r
2543                 m_pwndRight->m_pViewData->InsertData(i, m_pwndLeft->m_pViewData->GetLine(i), DIFFSTATE_THEIRSADDED, linenumber++, m_pwndLeft->m_pViewData->GetLineEnding(i));\r
2544         }\r
2545         // adjust line numbers\r
2546         for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)\r
2547         {\r
2548                 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);\r
2549                 if (oldline >= 0)\r
2550                         m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(m_nSelBlockEnd-m_nSelBlockStart)+1);\r
2551         }\r
2552 \r
2553         // now insert an empty block in left view\r
2554         for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)\r
2555         {\r
2556                 leftstate.addedlines.push_back(m_nSelBlockEnd + 1);\r
2557                 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd + 1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);\r
2558         }\r
2559         RecalcAllVertScrollBars();\r
2560         m_pwndLeft->SetModified();\r
2561         m_pwndRight->SetModified();\r
2562 }\r
2563 \r
2564 void CBaseView::UpdateCaret()\r
2565 {\r
2566         if (m_ptCaretPos.y >= GetLineCount())\r
2567                 m_ptCaretPos.y = GetLineCount()-1;\r
2568         if (m_ptCaretPos.y < 0)\r
2569                 m_ptCaretPos.y = 0;\r
2570         if (m_ptCaretPos.x > GetLineLength(m_ptCaretPos.y))\r
2571                 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);\r
2572         if (m_ptCaretPos.x < 0)\r
2573                 m_ptCaretPos.x = 0;\r
2574 \r
2575         int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);\r
2576 \r
2577         if (m_bFocused && !m_bCaretHidden &&\r
2578                 m_ptCaretPos.y >= m_nTopLine &&\r
2579                 m_ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&\r
2580                 nCaretOffset >= m_nOffsetChar &&\r
2581                 nCaretOffset < (m_nOffsetChar+GetScreenChars()))\r
2582         {\r
2583                 CreateSolidCaret(2, GetLineHeight());\r
2584                 SetCaretPos(TextToClient(m_ptCaretPos));\r
2585                 ShowCaret();\r
2586         }\r
2587         else\r
2588         {\r
2589                 HideCaret();\r
2590         }\r
2591 }\r
2592 \r
2593 void CBaseView::EnsureCaretVisible()\r
2594 {\r
2595         int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);\r
2596 \r
2597         if (m_ptCaretPos.y < m_nTopLine)\r
2598                 ScrollAllToLine(m_ptCaretPos.y);\r
2599         if (m_ptCaretPos.y >= (m_nTopLine+GetScreenLines()))\r
2600                 ScrollAllToLine(m_ptCaretPos.y-GetScreenLines()+1);\r
2601         if (nCaretOffset < m_nOffsetChar)\r
2602                 ScrollToChar(nCaretOffset);\r
2603         if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))\r
2604                 ScrollToChar(nCaretOffset-GetScreenChars()+1);\r
2605 }\r
2606 \r
2607 int CBaseView::CalculateActualOffset(int nLineIndex, int nCharIndex) const\r
2608 {\r
2609         int nLength = GetLineLength(nLineIndex);\r
2610         ASSERT(nCharIndex >= 0);\r
2611         if (nCharIndex > nLength)\r
2612                 nCharIndex = nLength;\r
2613         LPCTSTR pszChars = GetLineChars(nLineIndex);\r
2614         int nOffset = 0;\r
2615         int nTabSize = GetTabSize();\r
2616         for (int I = 0; I < nCharIndex; I ++)\r
2617         {\r
2618                 if (pszChars[I] == _T('\t'))\r
2619                         nOffset += (nTabSize - nOffset % nTabSize);\r
2620                 else\r
2621                         nOffset++;\r
2622         }\r
2623         return nOffset;\r
2624 }\r
2625 \r
2626 int     CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset) const\r
2627 {\r
2628         int nLength = GetLineLength(nLineIndex);\r
2629         LPCTSTR pszLine = GetLineChars(nLineIndex);\r
2630         int nIndex = 0;\r
2631         int nOffset = 0;\r
2632         int nTabSize = GetTabSize();\r
2633         while (nOffset < nActualOffset && nIndex < nLength)\r
2634         {\r
2635                 if (pszLine[nIndex] == _T('\t'))\r
2636                         nOffset += (nTabSize - nOffset % nTabSize);\r
2637                 else\r
2638                         ++nOffset;\r
2639                 ++nIndex;\r
2640         }\r
2641         return nIndex;\r
2642 }\r
2643 \r
2644 POINT CBaseView::TextToClient(const POINT& point)\r
2645 {\r
2646         POINT pt;\r
2647         pt.y = max(0, (point.y - m_nTopLine) * GetLineHeight());\r
2648         pt.x = CalculateActualOffset(point.y, point.x);\r
2649 \r
2650         pt.x = (pt.x - m_nOffsetChar) * GetCharWidth() + GetMarginWidth();\r
2651         pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);\r
2652         return pt;\r
2653 }\r
2654 \r
2655 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)\r
2656 {\r
2657         CView::OnChar(nChar, nRepCnt, nFlags);\r
2658 \r
2659         if (m_bCaretHidden)\r
2660                 return;\r
2661 \r
2662         if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||\r
2663                 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)\r
2664                 return;\r
2665 \r
2666         if ((nChar > 31)||(nChar == VK_TAB))\r
2667         {\r
2668                 RemoveSelectedText();\r
2669                 AddUndoLine(m_ptCaretPos.y);\r
2670                 CString sLine = GetLineChars(m_ptCaretPos.y);\r
2671                 sLine.Insert(m_ptCaretPos.x, (wchar_t)nChar);\r
2672                 m_pViewData->SetLine(m_ptCaretPos.y, sLine);\r
2673                 m_pViewData->SetState(m_ptCaretPos.y, DIFFSTATE_EDITED);\r
2674                 m_ptCaretPos.x++;\r
2675                 UpdateGoalPos();\r
2676         }\r
2677         else if (nChar == VK_RETURN)\r
2678         {\r
2679                 // insert a new, fresh and empty line below the cursor\r
2680                 RemoveSelectedText();\r
2681                 AddUndoLine(m_ptCaretPos.y, true);\r
2682                 // move the cursor to the new line\r
2683                 m_ptCaretPos.y++;\r
2684                 m_ptCaretPos.x = 0;\r
2685                 UpdateGoalPos();\r
2686         }\r
2687         else\r
2688                 return; // Unknown control character -- ignore it.\r
2689         ClearSelection();\r
2690         EnsureCaretVisible();\r
2691         UpdateCaret();\r
2692         SetModified(true);\r
2693         Invalidate(FALSE);\r
2694 }\r
2695 \r
2696 void CBaseView::AddUndoLine(int nLine, bool bAddEmptyLine)\r
2697 {\r
2698         viewstate leftstate;\r
2699         viewstate rightstate;\r
2700         viewstate bottomstate;\r
2701         leftstate.AddLineFormView(m_pwndLeft, nLine, bAddEmptyLine);\r
2702         rightstate.AddLineFormView(m_pwndRight, nLine, bAddEmptyLine);\r
2703         bottomstate.AddLineFormView(m_pwndBottom, nLine, bAddEmptyLine);\r
2704         CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);\r
2705 }\r
2706 \r
2707 void CBaseView::AddEmptyLine(int nLineIndex)\r
2708 {\r
2709         if (m_pViewData == NULL)\r
2710                 return;\r
2711         if (!m_bCaretHidden)\r
2712         {\r
2713                 CString sPartLine = GetLineChars(nLineIndex);\r
2714                 m_pViewData->SetLine(nLineIndex, sPartLine.Left(m_ptCaretPos.x));\r
2715                 sPartLine = sPartLine.Mid(m_ptCaretPos.x);\r
2716                 m_pViewData->InsertData(nLineIndex+1, sPartLine, DIFFSTATE_EDITED, -1, m_pViewData->GetLineEnding(nLineIndex));\r
2717         }\r
2718         else\r
2719                 m_pViewData->InsertData(nLineIndex+1, _T(""), DIFFSTATE_EDITED, -1, m_pViewData->GetLineEnding(nLineIndex));\r
2720         Invalidate(FALSE);\r
2721 }\r
2722 \r
2723 void CBaseView::RemoveLine(int nLineIndex)\r
2724 {\r
2725         if (m_pViewData == NULL)\r
2726                 return;\r
2727         m_pViewData->RemoveData(nLineIndex);\r
2728         if (m_ptCaretPos.y >= GetLineCount())\r
2729                 m_ptCaretPos.y = GetLineCount()-1;\r
2730         Invalidate(FALSE);\r
2731 }\r
2732 \r
2733 void CBaseView::RemoveSelectedText()\r
2734 {\r
2735         if (m_pViewData == NULL)\r
2736                 return;\r
2737         if (!HasTextSelection())\r
2738                 return;\r
2739 \r
2740         viewstate rightstate;\r
2741         viewstate bottomstate;\r
2742         viewstate leftstate;\r
2743         std::vector<LONG> linestoremove;\r
2744         for (LONG i = m_ptSelectionStartPos.y; i <= m_ptSelectionEndPos.y; ++i)\r
2745         {\r
2746                 if (i == m_ptSelectionStartPos.y)\r
2747                 {\r
2748                         CString sLine = GetLineChars(m_ptSelectionStartPos.y);\r
2749                         CString newLine;\r
2750                         if (i == m_ptSelectionStartPos.y)\r
2751                         {\r
2752                                 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))\r
2753                                 {\r
2754                                         leftstate.difflines[i] = m_pwndLeft->m_pViewData->GetLine(i);\r
2755                                         leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);\r
2756                                 }\r
2757                                 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))\r
2758                                 {\r
2759                                         rightstate.difflines[i] = m_pwndRight->m_pViewData->GetLine(i);\r
2760                                         rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);\r
2761                                 }\r
2762                                 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))\r
2763                                 {\r
2764                                         bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);\r
2765                                         bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);\r
2766                                 }\r
2767                                 newLine = sLine.Left(m_ptSelectionStartPos.x);\r
2768                                 sLine = GetLineChars(m_ptSelectionEndPos.y);\r
2769                                 newLine = newLine + sLine.Mid(m_ptSelectionEndPos.x);\r
2770                         }\r
2771                         m_pViewData->SetLine(i, newLine);\r
2772                         m_pViewData->SetState(i, DIFFSTATE_EDITED);\r
2773                         SetModified();\r
2774                 }\r
2775                 else\r
2776                 {\r
2777                         if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))\r
2778                         {\r
2779                                 leftstate.removedlines[i] = m_pwndLeft->m_pViewData->GetData(i);\r
2780                         }\r
2781                         if ((m_pwndRight)&&(m_pwndRight->m_pViewData))\r
2782                         {\r
2783                                 rightstate.removedlines[i] = m_pwndRight->m_pViewData->GetData(i);\r
2784                         }\r
2785                         if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))\r
2786                         {\r
2787                                 bottomstate.removedlines[i] = m_pwndBottom->m_pViewData->GetData(i);\r
2788                         }\r
2789                         linestoremove.push_back(i);\r
2790                 }\r
2791         }\r
2792         CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);\r
2793         // remove the lines at the end, to avoid problems with line indexes\r
2794         if (linestoremove.size())\r
2795         {\r
2796                 std::vector<LONG>::const_iterator it = linestoremove.begin();\r
2797                 int nLineToRemove = *it;\r
2798                 for ( ; it != linestoremove.end(); ++it)\r
2799                 {\r
2800                         if (m_pwndLeft)\r
2801                                 m_pwndLeft->RemoveLine(nLineToRemove);\r
2802                         if (m_pwndRight)\r
2803                                 m_pwndRight->RemoveLine(nLineToRemove);\r
2804                         if (m_pwndBottom)\r
2805                                 m_pwndBottom->RemoveLine(nLineToRemove);\r
2806                         SetModified();\r
2807                 }\r
2808         }\r
2809         m_ptCaretPos = m_ptSelectionStartPos;\r
2810         UpdateGoalPos();\r
2811         ClearSelection();\r
2812         UpdateCaret();\r
2813         EnsureCaretVisible();\r
2814         Invalidate(FALSE);\r
2815 }\r
2816 \r
2817 void CBaseView::PasteText()\r
2818 {\r
2819         if (!OpenClipboard())\r
2820                 return;\r
2821 \r
2822         CString sClipboardText;\r
2823         HGLOBAL hglb = GetClipboardData(CF_TEXT);\r
2824         if (hglb)\r
2825         {\r
2826                 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);\r
2827                 sClipboardText = CString(lpstr);\r
2828                 GlobalUnlock(hglb); \r
2829         }\r
2830         hglb = GetClipboardData(CF_UNICODETEXT);\r
2831         if (hglb)\r
2832         {\r
2833                 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);\r
2834                 sClipboardText = lpstr;\r
2835                 GlobalUnlock(hglb); \r
2836         }\r
2837         CloseClipboard();\r
2838 \r
2839         if (sClipboardText.IsEmpty())\r
2840                 return;\r
2841 \r
2842         sClipboardText.Replace(_T("\r\n"), _T("\r"));\r
2843         sClipboardText.Replace('\n', '\r');\r
2844         // We want to undo the insertion in a single step.\r
2845         CUndo::GetInstance().BeginGrouping();\r
2846         // use the easy way to insert text:\r
2847         // insert char by char, using the OnChar() method\r
2848         for (int i=0; i<sClipboardText.GetLength(); ++i)\r
2849         {\r
2850                 OnChar(sClipboardText[i], 0, 0);\r
2851         }\r
2852         CUndo::GetInstance().EndGrouping();\r
2853 }\r
2854 \r
2855 void CBaseView::OnCaretDown()\r
2856 {\r
2857         m_ptCaretPos.y++;\r
2858         m_ptCaretPos.y = min(m_ptCaretPos.y, GetLineCount()-1);\r
2859         m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);\r
2860         if (GetKeyState(VK_SHIFT)&0x8000)\r
2861                 AdjustSelection();\r
2862         else\r
2863                 ClearSelection();\r
2864         UpdateCaret();\r
2865         EnsureCaretVisible();\r
2866         ShowDiffLines(m_ptCaretPos.y);\r
2867 }\r
2868 \r
2869 bool CBaseView::MoveCaretLeft()\r
2870 {\r
2871         if (m_ptCaretPos.x == 0)\r
2872         {\r
2873                 if (m_ptCaretPos.y > 0)\r
2874                 {\r
2875                         --m_ptCaretPos.y;\r
2876                         m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);\r
2877                 }\r
2878                 else\r
2879                         return false;\r
2880         }\r
2881         else\r
2882                 --m_ptCaretPos.x;\r
2883 \r
2884         UpdateGoalPos();\r
2885         return true;\r
2886 }\r
2887 \r
2888 bool CBaseView::MoveCaretRight()\r
2889 {\r
2890         if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))\r
2891         {\r
2892                 if (m_ptCaretPos.y < (GetLineCount() - 1))\r
2893                 {\r
2894                         ++m_ptCaretPos.y;\r
2895                         m_ptCaretPos.x = 0;\r
2896                 }\r
2897                 else\r
2898                         return false;\r
2899         }\r
2900         else\r
2901                 ++m_ptCaretPos.x;\r
2902 \r
2903         UpdateGoalPos();\r
2904         return true;\r
2905 }\r
2906 \r
2907 void CBaseView::UpdateGoalPos()\r
2908 {\r
2909         m_nCaretGoalPos = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);\r
2910 }\r
2911 \r
2912 void CBaseView::OnCaretLeft()\r
2913 {\r
2914         MoveCaretLeft();\r
2915         if (GetKeyState(VK_SHIFT)&0x8000)\r
2916                 AdjustSelection();\r
2917         else\r
2918                 ClearSelection();\r
2919         EnsureCaretVisible();\r
2920         UpdateCaret();\r
2921 }\r
2922 \r
2923 void CBaseView::OnCaretRight()\r
2924 {\r
2925         MoveCaretRight();\r
2926         if (GetKeyState(VK_SHIFT)&0x8000)\r
2927                 AdjustSelection();\r
2928         else\r
2929                 ClearSelection();\r
2930         EnsureCaretVisible();\r
2931         UpdateCaret();\r
2932 }\r
2933 \r
2934 void CBaseView::OnCaretUp()\r
2935 {\r
2936         m_ptCaretPos.y--;\r
2937         m_ptCaretPos.y = max(0, m_ptCaretPos.y);\r
2938         m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);\r
2939         if (GetKeyState(VK_SHIFT)&0x8000)\r
2940                 AdjustSelection();\r
2941         else\r
2942                 ClearSelection();\r
2943         UpdateCaret();\r
2944         EnsureCaretVisible();\r
2945         ShowDiffLines(m_ptCaretPos.y);\r
2946 }\r
2947 \r
2948 bool CBaseView::IsWordSeparator(wchar_t ch) const\r
2949 {\r
2950         return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);\r
2951 }\r
2952 \r
2953 bool CBaseView::IsCaretAtWordBoundary() const\r
2954 {\r
2955         LPCTSTR line = GetLineChars(m_ptCaretPos.y);\r
2956         if (!*line)\r
2957                 return false; // no boundary at the empty lines\r
2958         if (m_ptCaretPos.x == 0)\r
2959                 return !IsWordSeparator(line[m_ptCaretPos.x]);\r
2960         if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))\r
2961                 return !IsWordSeparator(line[m_ptCaretPos.x - 1]);\r
2962         return\r
2963                 IsWordSeparator(line[m_ptCaretPos.x]) !=\r
2964                 IsWordSeparator(line[m_ptCaretPos.x - 1]);\r
2965 }\r
2966 \r
2967 void CBaseView::OnCaretWordleft()\r
2968 {\r
2969         while (MoveCaretLeft() && !IsCaretAtWordBoundary())\r
2970         {\r
2971         }\r
2972         if (GetKeyState(VK_SHIFT)&0x8000)\r
2973                 AdjustSelection();\r
2974         else\r
2975                 ClearSelection();\r
2976         EnsureCaretVisible();\r
2977         UpdateCaret();\r
2978 }\r
2979 \r
2980 void CBaseView::OnCaretWordright()\r
2981 {\r
2982         while (MoveCaretRight() && !IsCaretAtWordBoundary())\r
2983         {\r
2984         }\r
2985         if (GetKeyState(VK_SHIFT)&0x8000)\r
2986                 AdjustSelection();\r
2987         else\r
2988                 ClearSelection();\r
2989         EnsureCaretVisible();\r
2990         UpdateCaret();\r
2991 }\r
2992 \r
2993 void CBaseView::ClearCurrentSelection()\r
2994 {\r
2995         m_ptSelectionStartPos = m_ptCaretPos;\r
2996         m_ptSelectionEndPos = m_ptCaretPos;\r
2997         m_ptSelectionOrigin = m_ptCaretPos;\r
2998         m_nSelBlockStart = -1;\r
2999         m_nSelBlockEnd = -1;\r
3000         Invalidate(FALSE);\r
3001 }\r
3002 \r
3003 void CBaseView::ClearSelection()\r
3004 {\r
3005         if (m_pwndLeft)\r
3006                 m_pwndLeft->ClearCurrentSelection();\r
3007         if (m_pwndRight)\r
3008                 m_pwndRight->ClearCurrentSelection();\r
3009         if (m_pwndBottom)\r
3010                 m_pwndBottom->ClearCurrentSelection();\r
3011 }\r
3012 \r
3013 void CBaseView::AdjustSelection()\r
3014 {\r
3015         if ((m_ptCaretPos.y < m_ptSelectionOrigin.y) || \r
3016                 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x <= m_ptSelectionOrigin.x))\r
3017         {\r
3018                 m_ptSelectionStartPos = m_ptCaretPos;\r
3019                 m_ptSelectionEndPos = m_ptSelectionOrigin;\r
3020         }\r
3021 \r
3022         if ((m_ptCaretPos.y > m_ptSelectionOrigin.y) || \r
3023                 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x >= m_ptSelectionOrigin.x))\r
3024         {\r
3025                 m_ptSelectionStartPos = m_ptSelectionOrigin;\r
3026                 m_ptSelectionEndPos = m_ptCaretPos;\r
3027         }\r
3028 \r
3029         SetupSelection(min(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y), max(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y));\r
3030 \r
3031         Invalidate(FALSE);\r
3032 }\r
3033 \r
3034 void CBaseView::OnEditCut()\r
3035 {\r
3036         if (!m_bCaretHidden)\r
3037         {\r
3038                 OnEditCopy();\r
3039                 RemoveSelectedText();\r
3040         }\r
3041 }\r
3042 \r
3043 void CBaseView::OnEditPaste()\r
3044 {\r
3045         if (!m_bCaretHidden)\r
3046         {\r
3047                 PasteText();\r
3048         }\r
3049 }\r
3050 \r