Fix Issue #392: refresh branch info when press "F5"
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob24fdfc20ac79e34e4c7f742f9fc38e984cef01dd
1 // TortoiseMerge - a Diff/Patch program
3 // Copyright (C) 2003-2009 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "registry.h"
22 #include "TortoiseMerge.h"
23 #include "MainFrm.h"
24 #include "BaseView.h"
25 #include "DiffColors.h"
26 #include "StringUtils.h"
28 #include <deque>
30 #ifdef _DEBUG
31 #define new DEBUG_NEW
32 #endif
34 #define MARGINWIDTH 20
35 #define HEADERHEIGHT 10
37 #define MAXFONTS 8
39 #define INLINEADDED_COLOR RGB(255, 255, 150)
40 #define INLINEREMOVED_COLOR RGB(200, 100, 100)
41 #define MODIFIED_COLOR RGB(220, 220, 255)
43 #define IDT_SCROLLTIMER 101
45 CBaseView * CBaseView::m_pwndLeft = NULL;
46 CBaseView * CBaseView::m_pwndRight = NULL;
47 CBaseView * CBaseView::m_pwndBottom = NULL;
48 CLocatorBar * CBaseView::m_pwndLocator = NULL;
49 CLineDiffBar * CBaseView::m_pwndLineDiffBar = NULL;
50 CMFCStatusBar * CBaseView::m_pwndStatusBar = NULL;
51 CMainFrame * CBaseView::m_pMainFrame = NULL;
53 IMPLEMENT_DYNCREATE(CBaseView, CView)
55 CBaseView::CBaseView()
57 m_pCacheBitmap = NULL;
58 m_pViewData = NULL;
59 m_pOtherViewData = NULL;
60 m_nLineHeight = -1;
61 m_nCharWidth = -1;
62 m_nScreenChars = -1;
63 m_nMaxLineLength = -1;
64 m_nScreenLines = -1;
65 m_nTopLine = 0;
66 m_nOffsetChar = 0;
67 m_nDigits = 0;
68 m_nMouseLine = -1;
69 m_bMouseWithin = FALSE;
70 m_bIsHidden = FALSE;
71 lineendings = EOL_AUTOLINE;
72 m_bCaretHidden = true;
73 m_ptCaretPos.x = 0;
74 m_ptCaretPos.y = 0;
75 m_nCaretGoalPos = 0;
76 m_ptSelectionStartPos = m_ptCaretPos;
77 m_ptSelectionEndPos = m_ptCaretPos;
78 m_ptSelectionOrigin = m_ptCaretPos;
79 m_bFocused = FALSE;
80 m_bShowSelection = true;
81 texttype = CFileTextLines::AUTOTYPE;
82 m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseMerge\\ViewWhitespaces"), 1);
83 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);
84 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE);
85 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR);
86 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
87 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
88 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
89 m_sWordSeparators = CRegString(_T("Software\\TortoiseMerge\\WordSeparators"), _T("[]();.,{}!@#$%^&*-+=|/\\<>'`~"));;
90 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);
91 m_nSelBlockStart = -1;
92 m_nSelBlockEnd = -1;
93 m_bModified = FALSE;
94 m_bOtherDiffChecked = false;
95 m_bInlineWordDiff = true;
96 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);
97 for (int i=0; i<MAXFONTS; i++)
99 m_apFonts[i] = NULL;
101 m_hConflictedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDLINE),
102 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
103 m_hConflictedIgnoredIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_CONFLICTEDIGNOREDLINE),
104 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
105 m_hRemovedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_REMOVEDLINE),
106 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
107 m_hAddedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_ADDEDLINE),
108 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
109 m_hWhitespaceBlockIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_WHITESPACELINE),
110 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
111 m_hEqualIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_EQUALLINE),
112 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
113 m_hLineEndingCR = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCR),
114 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
115 m_hLineEndingCRLF = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGCRLF),
116 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
117 m_hLineEndingLF = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEENDINGLF),
118 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
119 m_hEditedIcon = (HICON)::LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDI_LINEEDITED),
120 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
121 for (int i=0; i<1024; ++i)
122 m_sConflictedText += _T("??");
123 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
124 EnableToolTips();
127 CBaseView::~CBaseView()
129 if (m_pCacheBitmap)
131 m_pCacheBitmap->DeleteObject();
132 delete m_pCacheBitmap;
134 for (int i=0; i<MAXFONTS; i++)
136 if (m_apFonts[i] != NULL)
138 m_apFonts[i]->DeleteObject();
139 delete m_apFonts[i];
141 m_apFonts[i] = NULL;
143 DestroyIcon(m_hAddedIcon);
144 DestroyIcon(m_hRemovedIcon);
145 DestroyIcon(m_hConflictedIcon);
146 DestroyIcon(m_hConflictedIgnoredIcon);
147 DestroyIcon(m_hWhitespaceBlockIcon);
148 DestroyIcon(m_hEqualIcon);
149 DestroyIcon(m_hLineEndingCR);
150 DestroyIcon(m_hLineEndingCRLF);
151 DestroyIcon(m_hLineEndingLF);
152 DestroyIcon(m_hEditedIcon);
155 BEGIN_MESSAGE_MAP(CBaseView, CView)
156 ON_WM_VSCROLL()
157 ON_WM_HSCROLL()
158 ON_WM_ERASEBKGND()
159 ON_WM_CREATE()
160 ON_WM_DESTROY()
161 ON_WM_SIZE()
162 ON_WM_MOUSEWHEEL()
163 ON_WM_SETCURSOR()
164 ON_WM_KILLFOCUS()
165 ON_WM_SETFOCUS()
166 ON_WM_CONTEXTMENU()
167 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
168 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
169 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
170 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
171 ON_WM_KEYDOWN()
172 ON_WM_LBUTTONDOWN()
173 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
174 ON_WM_MOUSEMOVE()
175 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
176 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
177 ON_WM_CHAR()
178 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
179 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
180 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
181 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
182 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
183 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
184 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
185 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
186 ON_WM_MOUSELEAVE()
187 ON_WM_TIMER()
188 END_MESSAGE_MAP()
191 void CBaseView::DocumentUpdated()
193 if (m_pCacheBitmap != NULL)
195 m_pCacheBitmap->DeleteObject();
196 delete m_pCacheBitmap;
197 m_pCacheBitmap = NULL;
199 m_nLineHeight = -1;
200 m_nCharWidth = -1;
201 m_nScreenChars = -1;
202 m_nMaxLineLength = -1;
203 m_nScreenLines = -1;
204 m_nTopLine = 0;
205 m_bModified = FALSE;
206 m_bOtherDiffChecked = false;
207 m_nDigits = 0;
208 m_nMouseLine = -1;
210 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\TabSize"), 4);
211 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseMerge\\ViewLinenumbers"), 1);
212 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseMerge\\DisplayBinDiff"), TRUE);
213 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineAdded"), INLINEADDED_COLOR);
214 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
215 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
216 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
217 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseMerge\\IconLFs"), 0);
218 for (int i=0; i<MAXFONTS; i++)
220 if (m_apFonts[i] != NULL)
222 m_apFonts[i]->DeleteObject();
223 delete m_apFonts[i];
225 m_apFonts[i] = NULL;
227 m_nSelBlockStart = -1;
228 m_nSelBlockEnd = -1;
229 RecalcVertScrollBar();
230 RecalcHorzScrollBar();
231 UpdateStatusBar();
232 Invalidate();
235 void CBaseView::UpdateStatusBar()
237 int nRemovedLines = 0;
238 int nAddedLines = 0;
239 int nConflictedLines = 0;
241 if (m_pViewData)
243 for (int i=0; i<m_pViewData->GetCount(); i++)
245 DiffStates state = m_pViewData->GetState(i);
246 switch (state)
248 case DIFFSTATE_ADDED:
249 case DIFFSTATE_IDENTICALADDED:
250 case DIFFSTATE_THEIRSADDED:
251 case DIFFSTATE_YOURSADDED:
252 case DIFFSTATE_CONFLICTADDED:
253 nAddedLines++;
254 break;
255 case DIFFSTATE_IDENTICALREMOVED:
256 case DIFFSTATE_REMOVED:
257 case DIFFSTATE_THEIRSREMOVED:
258 case DIFFSTATE_YOURSREMOVED:
259 nRemovedLines++;
260 break;
261 case DIFFSTATE_CONFLICTED:
262 case DIFFSTATE_CONFLICTED_IGNORED:
263 nConflictedLines++;
264 break;
269 CString sBarText;
270 CString sTemp;
272 switch (texttype)
274 case CFileTextLines::ASCII:
275 sBarText = _T("ASCII ");
276 break;
277 case CFileTextLines::BINARY:
278 sBarText = _T("BINARY ");
279 break;
280 case CFileTextLines::UNICODE_LE:
281 sBarText = _T("UTF-16LE ");
282 break;
283 case CFileTextLines::UTF8:
284 sBarText = _T("UTF8 ");
285 break;
286 case CFileTextLines::UTF8BOM:
287 sBarText = _T("UTF8 BOM ");
288 break;
291 switch(lineendings)
293 case EOL_LF:
294 sBarText += _T("LF ");
295 break;
296 case EOL_CRLF:
297 sBarText += _T("CRLF ");
298 break;
299 case EOL_LFCR:
300 sBarText += _T("LFCR ");
301 break;
302 case EOL_CR:
303 sBarText += _T("CR ");
304 break;
307 if (sBarText.IsEmpty())
308 sBarText += _T(" / ");
310 if (nRemovedLines)
312 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
313 if (!sBarText.IsEmpty())
314 sBarText += _T(" / ");
315 sBarText += sTemp;
317 if (nAddedLines)
319 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
320 if (!sBarText.IsEmpty())
321 sBarText += _T(" / ");
322 sBarText += sTemp;
324 if (nConflictedLines)
326 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
327 if (!sBarText.IsEmpty())
328 sBarText += _T(" / ");
329 sBarText += sTemp;
331 if (m_pwndStatusBar)
333 UINT nID;
334 UINT nStyle;
335 int cxWidth;
336 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
337 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
339 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
341 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
343 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
344 sBarText = sTemp+sBarText;
346 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
348 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
349 sBarText = sTemp+sBarText;
351 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
352 //calculate the width of the text
353 CDC * pDC = m_pwndStatusBar->GetDC();
354 if (pDC)
356 CSize size = pDC->GetTextExtent(sBarText);
357 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
358 ReleaseDC(pDC);
360 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
364 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
366 if (!CView::PreCreateWindow(cs))
367 return FALSE;
369 cs.dwExStyle |= WS_EX_CLIENTEDGE;
370 cs.style &= ~WS_BORDER;
371 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
372 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
374 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
375 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
377 // View must always create its own scrollbars,
378 // if only it's not used within splitter
379 cs.style |= (WS_HSCROLL | WS_VSCROLL);
381 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
382 return TRUE;
385 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/, BOOL bStrikeOut /*= FALSE*/)
387 int nIndex = 0;
388 if (bBold)
389 nIndex |= 1;
390 if (bItalic)
391 nIndex |= 2;
392 if (bStrikeOut)
393 nIndex |= 4;
394 if (m_apFonts[nIndex] == NULL)
396 m_apFonts[nIndex] = new CFont;
397 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
398 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
399 m_lfBaseFont.lfItalic = (BYTE) bItalic;
400 m_lfBaseFont.lfStrikeOut = (BYTE) bStrikeOut;
401 if (bStrikeOut)
402 m_lfBaseFont.lfStrikeOut = (BYTE)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE);
403 CDC * pDC = GetDC();
404 if (pDC)
406 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
407 ReleaseDC(pDC);
409 _tcsncpy_s(m_lfBaseFont.lfFaceName, 32, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseMerge\\LogFontName"), _T("Courier New")), 32);
410 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
412 delete m_apFonts[nIndex];
413 m_apFonts[nIndex] = NULL;
414 return CView::GetFont();
417 return m_apFonts[nIndex];
420 void CBaseView::CalcLineCharDim()
422 CDC *pDC = GetDC();
423 CFont *pOldFont = pDC->SelectObject(GetFont());
424 CSize szCharExt = pDC->GetTextExtent(_T("X"));
425 m_nLineHeight = szCharExt.cy;
426 if (m_nLineHeight <= 0)
427 m_nLineHeight = -1;
428 m_nCharWidth = szCharExt.cx;
429 if (m_nCharWidth <= 0)
430 m_nCharWidth = -1;
431 pDC->SelectObject(pOldFont);
432 ReleaseDC(pDC);
435 int CBaseView::GetScreenChars()
437 if (m_nScreenChars == -1)
439 CRect rect;
440 GetClientRect(&rect);
441 m_nScreenChars = (rect.Width() - GetMarginWidth()) / GetCharWidth();
443 return m_nScreenChars;
446 int CBaseView::GetAllMinScreenChars() const
448 int nChars = 0;
449 if (IsLeftViewGood())
450 nChars = m_pwndLeft->GetScreenChars();
451 if (IsRightViewGood())
452 nChars = (nChars < m_pwndRight->GetScreenChars() ? nChars : m_pwndRight->GetScreenChars());
453 if (IsBottomViewGood())
454 nChars = (nChars < m_pwndBottom->GetScreenChars() ? nChars : m_pwndBottom->GetScreenChars());
455 return nChars;
458 int CBaseView::GetAllMaxLineLength() const
460 int nLength = 0;
461 if (IsLeftViewGood())
462 nLength = m_pwndLeft->GetMaxLineLength();
463 if (IsRightViewGood())
464 nLength = (nLength > m_pwndRight->GetMaxLineLength() ? nLength : m_pwndRight->GetMaxLineLength());
465 if (IsBottomViewGood())
466 nLength = (nLength > m_pwndBottom->GetMaxLineLength() ? nLength : m_pwndBottom->GetMaxLineLength());
467 return nLength;
470 int CBaseView::GetLineHeight()
472 if (m_nLineHeight == -1)
473 CalcLineCharDim();
474 if (m_nLineHeight <= 0)
475 return 1;
476 return m_nLineHeight;
479 int CBaseView::GetCharWidth()
481 if (m_nCharWidth == -1)
482 CalcLineCharDim();
483 if (m_nCharWidth <= 0)
484 return 1;
485 return m_nCharWidth;
488 int CBaseView::GetMaxLineLength()
490 if (m_nMaxLineLength == -1)
492 m_nMaxLineLength = 0;
493 int nLineCount = GetLineCount();
494 for (int i=0; i<nLineCount; i++)
496 int nActualLength = GetLineActualLength(i);
497 if (m_nMaxLineLength < nActualLength)
498 m_nMaxLineLength = nActualLength;
501 return m_nMaxLineLength;
504 int CBaseView::GetLineActualLength(int index) const
506 if (m_pViewData == NULL)
507 return 0;
509 return CalculateActualOffset(index, GetLineLength(index));
512 int CBaseView::GetLineLength(int index) const
514 if (m_pViewData == NULL)
515 return 0;
516 if (m_pViewData->GetCount() == 0)
517 return 0;
518 int nLineLength = m_pViewData->GetLine(index).GetLength();
519 ASSERT(nLineLength >= 0);
520 return nLineLength;
523 int CBaseView::GetLineCount() const
525 if (m_pViewData == NULL)
526 return 1;
527 int nLineCount = m_pViewData->GetCount();
528 ASSERT(nLineCount >= 0);
529 return nLineCount;
532 LPCTSTR CBaseView::GetLineChars(int index) const
534 if (m_pViewData == NULL)
535 return 0;
536 if (m_pViewData->GetCount() == 0)
537 return 0;
538 return m_pViewData->GetLine(index);
541 void CBaseView::CheckOtherView()
543 if (m_bOtherDiffChecked)
544 return;
545 // find out what the 'other' file is
546 m_pOtherViewData = NULL;
547 if (this == m_pwndLeft && IsRightViewGood())
548 m_pOtherViewData = m_pwndRight->m_pViewData;
550 if (this == m_pwndRight && IsLeftViewGood())
551 m_pOtherViewData = m_pwndLeft->m_pViewData;
553 m_bOtherDiffChecked = true;
556 CString CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex)
558 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
559 ASSERT(viewData);
561 DiffStates origstate = viewData->GetState(nLineIndex);
563 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
564 int nStartBlock = nLineIndex;
565 int nEndBlock = nLineIndex;
566 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
568 DiffStates state = viewData->GetState(nStartBlock - 1);
569 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
570 origstate = state;
571 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
572 nStartBlock--;
573 else
574 break;
576 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
578 DiffStates state = viewData->GetState(nEndBlock + 1);
579 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
580 origstate = state;
581 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
582 nEndBlock++;
583 else
584 break;
587 CString block;
588 for (int i = nStartBlock; i <= nEndBlock; ++i)
589 block += viewData->GetLine(i);
590 return block;
593 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical)
595 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
596 CheckOtherView();
597 if (!m_pOtherViewData)
598 return false;
599 if (
600 (m_pViewData->GetState(nLineIndex) == DIFFSTATE_NORMAL) &&
601 (m_pOtherViewData->GetLine(nLineIndex) == m_pViewData->GetLine(nLineIndex))
603 return false;
605 CString mine = GetWhitespaceBlock(m_pViewData, nLineIndex);
606 CString other = GetWhitespaceBlock(m_pOtherViewData, min(nLineIndex, m_pOtherViewData->GetCount() - 1));
607 bIdentical = mine == other;
609 mine.Replace(_T(" "), _T(""));
610 mine.Replace(_T("\t"), _T(""));
611 mine.Replace(_T("\r"), _T(""));
612 mine.Replace(_T("\n"), _T(""));
613 other.Replace(_T(" "), _T(""));
614 other.Replace(_T("\t"), _T(""));
615 other.Replace(_T("\r"), _T(""));
616 other.Replace(_T("\n"), _T(""));
618 return (mine == other) && (!mine.IsEmpty());
621 int CBaseView::GetLineNumber(int index) const
623 if (m_pViewData == NULL)
624 return -1;
625 if (m_pViewData->GetLineNumber(index)==DIFF_EMPTYLINENUMBER)
626 return -1;
627 return m_pViewData->GetLineNumber(index);
630 int CBaseView::GetScreenLines()
632 if (m_nScreenLines == -1)
634 SCROLLBARINFO sbi;
635 sbi.cbSize = sizeof(sbi);
636 GetScrollBarInfo(OBJID_HSCROLL, &sbi);
637 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
639 CRect rect;
640 GetClientRect(&rect);
641 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
643 return m_nScreenLines;
646 int CBaseView::GetAllMinScreenLines() const
648 int nLines = 0;
649 if (IsLeftViewGood())
650 nLines = m_pwndLeft->GetScreenLines();
651 if (IsRightViewGood())
652 nLines = (nLines < m_pwndRight->GetScreenLines() ? nLines : m_pwndRight->GetScreenLines());
653 if (IsBottomViewGood())
654 nLines = (nLines < m_pwndBottom->GetScreenLines() ? nLines : m_pwndBottom->GetScreenLines());
655 return nLines;
658 int CBaseView::GetAllLineCount() const
660 int nLines = 0;
661 if (IsLeftViewGood())
662 nLines = m_pwndLeft->GetLineCount();
663 if (IsRightViewGood())
664 nLines = (nLines > m_pwndRight->GetLineCount() ? nLines : m_pwndRight->GetLineCount());
665 if (IsBottomViewGood())
666 nLines = (nLines > m_pwndBottom->GetLineCount() ? nLines : m_pwndBottom->GetLineCount());
667 return nLines;
670 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
672 if (IsLeftViewGood())
673 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
674 if (IsRightViewGood())
675 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
676 if (IsBottomViewGood())
677 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
680 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
682 SCROLLINFO si;
683 si.cbSize = sizeof(si);
684 if (bPositionOnly)
686 si.fMask = SIF_POS;
687 si.nPos = m_nTopLine;
689 else
691 EnableScrollBarCtrl(SB_VERT, TRUE);
692 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
694 m_nTopLine = 0;
695 Invalidate();
697 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
698 si.nMin = 0;
699 si.nMax = GetAllLineCount();
700 si.nPage = GetAllMinScreenLines();
701 si.nPos = m_nTopLine;
703 VERIFY(SetScrollInfo(SB_VERT, &si));
706 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
708 CView::OnVScroll(nSBCode, nPos, pScrollBar);
709 if (m_pwndLeft)
710 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
711 if (m_pwndRight)
712 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
713 if (m_pwndBottom)
714 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
715 if (m_pwndLocator)
716 m_pwndLocator->Invalidate();
719 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
721 // Note we cannot use nPos because of its 16-bit nature
722 SCROLLINFO si;
723 si.cbSize = sizeof(si);
724 si.fMask = SIF_ALL;
725 VERIFY(master->GetScrollInfo(SB_VERT, &si));
727 int nPageLines = GetScreenLines();
728 int nLineCount = GetLineCount();
730 RECT thumbrect;
731 POINT thumbpoint;
732 int nNewTopLine;
734 static LONG textwidth = 0;
735 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
736 switch (nSBCode)
738 case SB_TOP:
739 nNewTopLine = 0;
740 break;
741 case SB_BOTTOM:
742 nNewTopLine = nLineCount - nPageLines + 1;
743 break;
744 case SB_LINEUP:
745 nNewTopLine = m_nTopLine - 1;
746 break;
747 case SB_LINEDOWN:
748 nNewTopLine = m_nTopLine + 1;
749 break;
750 case SB_PAGEUP:
751 nNewTopLine = m_nTopLine - si.nPage + 1;
752 break;
753 case SB_PAGEDOWN:
754 nNewTopLine = m_nTopLine + si.nPage - 1;
755 break;
756 case SB_THUMBPOSITION:
757 m_ScrollTool.Clear();
758 nNewTopLine = si.nTrackPos;
759 textwidth = 0;
760 break;
761 case SB_THUMBTRACK:
762 nNewTopLine = si.nTrackPos;
763 if (GetFocus() == this)
765 GetClientRect(&thumbrect);
766 ClientToScreen(&thumbrect);
767 thumbpoint.x = thumbrect.right;
768 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
769 m_ScrollTool.Init(&thumbpoint);
770 if (textwidth == 0)
772 CString sTemp = sFormat;
773 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
774 textwidth = m_ScrollTool.GetTextWidth(sTemp);
776 thumbpoint.x -= textwidth;
777 int line = GetLineNumber(nNewTopLine);
778 if (line >= 0)
779 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
780 else
781 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
783 break;
784 default:
785 return;
788 if (nNewTopLine < 0)
789 nNewTopLine = 0;
790 if (nNewTopLine >= nLineCount)
791 nNewTopLine = nLineCount - 1;
792 ScrollToLine(nNewTopLine);
795 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
797 if (IsLeftViewGood())
798 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
799 if (IsRightViewGood())
800 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
801 if (IsBottomViewGood())
802 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
805 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
807 SCROLLINFO si;
808 si.cbSize = sizeof(si);
809 if (bPositionOnly)
811 si.fMask = SIF_POS;
812 si.nPos = m_nOffsetChar;
814 else
816 EnableScrollBarCtrl(SB_HORZ, TRUE);
817 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar > 0)
819 m_nOffsetChar = 0;
820 Invalidate();
822 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
823 si.nMin = 0;
824 si.nMax = GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();
825 si.nPage = GetAllMinScreenChars();
826 si.nPos = m_nOffsetChar;
828 VERIFY(SetScrollInfo(SB_HORZ, &si));
831 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
833 CView::OnHScroll(nSBCode, nPos, pScrollBar);
834 if (m_pwndLeft)
835 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
836 if (m_pwndRight)
837 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
838 if (m_pwndBottom)
839 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
840 if (m_pwndLocator)
841 m_pwndLocator->Invalidate();
844 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
846 SCROLLINFO si;
847 si.cbSize = sizeof(si);
848 si.fMask = SIF_ALL;
849 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
851 int nPageChars = GetScreenChars();
852 int nMaxLineLength = GetMaxLineLength();
854 int nNewOffset;
855 switch (nSBCode)
857 case SB_LEFT:
858 nNewOffset = 0;
859 break;
860 case SB_BOTTOM:
861 nNewOffset = nMaxLineLength - nPageChars + 1;
862 break;
863 case SB_LINEUP:
864 nNewOffset = m_nOffsetChar - 1;
865 break;
866 case SB_LINEDOWN:
867 nNewOffset = m_nOffsetChar + 1;
868 break;
869 case SB_PAGEUP:
870 nNewOffset = m_nOffsetChar - si.nPage + 1;
871 break;
872 case SB_PAGEDOWN:
873 nNewOffset = m_nOffsetChar + si.nPage - 1;
874 break;
875 case SB_THUMBPOSITION:
876 case SB_THUMBTRACK:
877 nNewOffset = si.nTrackPos;
878 break;
879 default:
880 return;
883 if (nNewOffset >= nMaxLineLength)
884 nNewOffset = nMaxLineLength - 1;
885 if (nNewOffset < 0)
886 nNewOffset = 0;
887 ScrollToChar(nNewOffset, TRUE);
890 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
892 if (m_nOffsetChar != nNewOffsetChar)
894 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
895 m_nOffsetChar = nNewOffsetChar;
896 CRect rcScroll;
897 GetClientRect(&rcScroll);
898 rcScroll.left += GetMarginWidth();
899 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
900 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
901 // update the view header
902 rcScroll.left = 0;
903 rcScroll.top = 0;
904 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
905 InvalidateRect(&rcScroll, FALSE);
906 UpdateWindow();
907 if (bTrackScrollBar)
908 RecalcHorzScrollBar(TRUE);
909 UpdateCaret();
913 void CBaseView::ScrollSide(int delta)
915 int nNewOffset = m_nOffsetChar;
916 nNewOffset += delta;
917 int nMaxLineLength = GetMaxLineLength();
918 if (nNewOffset >= nMaxLineLength)
919 nNewOffset = nMaxLineLength - 1;
920 if (nNewOffset < 0)
921 nNewOffset = 0;
922 ScrollToChar(nNewOffset, TRUE);
923 if (m_pwndLineDiffBar)
924 m_pwndLineDiffBar->Invalidate();
925 UpdateCaret();
928 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
930 if (m_nTopLine != nNewTopLine)
932 if (nNewTopLine < 0)
933 nNewTopLine = 0;
934 int nScrollLines = m_nTopLine - nNewTopLine;
935 m_nTopLine = nNewTopLine;
936 CRect rcScroll;
937 GetClientRect(&rcScroll);
938 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
939 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
940 UpdateWindow();
941 if (bTrackScrollBar)
942 RecalcVertScrollBar(TRUE);
943 UpdateCaret();
948 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
950 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
952 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
954 DiffStates state = m_pViewData->GetState(nLineIndex);
955 HICON icon = NULL;
956 switch (state)
958 case DIFFSTATE_ADDED:
959 case DIFFSTATE_THEIRSADDED:
960 case DIFFSTATE_YOURSADDED:
961 case DIFFSTATE_IDENTICALADDED:
962 case DIFFSTATE_CONFLICTADDED:
963 icon = m_hAddedIcon;
964 break;
965 case DIFFSTATE_REMOVED:
966 case DIFFSTATE_THEIRSREMOVED:
967 case DIFFSTATE_YOURSREMOVED:
968 case DIFFSTATE_IDENTICALREMOVED:
969 icon = m_hRemovedIcon;
970 break;
971 case DIFFSTATE_CONFLICTED:
972 icon = m_hConflictedIcon;
973 break;
974 case DIFFSTATE_CONFLICTED_IGNORED:
975 icon = m_hConflictedIgnoredIcon;
976 break;
977 case DIFFSTATE_EDITED:
978 icon = m_hEditedIcon;
979 break;
980 default:
981 break;
983 bool bIdentical = false;
984 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))
986 if (bIdentical)
987 icon = m_hEqualIcon;
988 else
989 icon = m_hWhitespaceBlockIcon;
992 if (icon)
994 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
996 if ((m_bViewLinenumbers)&&(m_nDigits))
998 int nLineNumber = GetLineNumber(nLineIndex);
999 if (nLineNumber >= 0)
1001 CString sLinenumberFormat;
1002 CString sLinenumber;
1003 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1004 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1005 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1006 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1008 pdc->SelectObject(GetFont());
1009 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1015 int CBaseView::GetMarginWidth()
1017 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1019 int nWidth = GetCharWidth();
1020 if (m_nDigits <= 0)
1022 int nLength = (int)m_pViewData->GetCount();
1023 // find out how many digits are needed to show the highest line number
1024 int nDigits = 1;
1025 while (nLength / 10)
1027 nDigits++;
1028 nLength /= 10;
1030 m_nDigits = nDigits;
1032 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1034 return MARGINWIDTH;
1037 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1039 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1040 COLORREF crBk, crFg;
1041 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1042 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1043 if (IsBottomViewGood())
1045 pdc->SetBkColor(crBk);
1047 else
1050 if (this == m_pwndRight)
1052 CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED, crBk, crFg);
1053 pdc->SetBkColor(crBk);
1055 else
1057 CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED, crBk, crFg);
1058 pdc->SetBkColor(crBk);
1061 pdc->FillSolidRect(textrect, crBk);
1063 pdc->SetTextColor(crFg);
1065 pdc->SelectObject(GetFont(FALSE, TRUE, FALSE));
1066 if (IsModified())
1068 if (m_sWindowName.Left(2).Compare(_T("* "))!=0)
1069 m_sWindowName = _T("* ") + m_sWindowName;
1071 else
1073 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
1074 m_sWindowName = m_sWindowName.Mid(2);
1076 CString sViewTitle = m_sWindowName;
1077 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1078 if (nStringLength > rect.Width())
1080 int offset = min(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1082 sViewTitle = m_sWindowName.Mid(offset);
1084 pdc->ExtTextOut(max(rect.left + (rect.Width()-nStringLength)/2, 1),
1085 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1086 if (this->GetFocus() == this)
1087 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1088 else
1089 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1092 void CBaseView::OnDraw(CDC * pDC)
1094 CRect rcClient;
1095 GetClientRect(rcClient);
1097 int nLineCount = GetLineCount();
1098 int nLineHeight = GetLineHeight();
1100 CDC cacheDC;
1101 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1102 if (m_pCacheBitmap == NULL)
1104 m_pCacheBitmap = new CBitmap;
1105 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1107 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1109 DrawHeader(pDC, rcClient);
1111 CRect rcLine;
1112 rcLine = rcClient;
1113 rcLine.top += nLineHeight+HEADERHEIGHT;
1114 rcLine.bottom = rcLine.top + nLineHeight;
1115 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1116 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1118 int nCurrentLine = m_nTopLine;
1119 while (rcLine.top < rcClient.bottom)
1121 if (nCurrentLine < nLineCount)
1123 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1124 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1126 else
1128 DrawMargin(&cacheDC, rcCacheMargin, -1);
1129 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1132 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1134 nCurrentLine ++;
1135 rcLine.OffsetRect(0, nLineHeight);
1138 cacheDC.SelectObject(pOldBitmap);
1139 cacheDC.DeleteDC();
1142 BOOL CBaseView::IsLineRemoved(int nLineIndex)
1144 DiffStates state = DIFFSTATE_UNKNOWN;
1145 if (m_pViewData)
1146 state = m_pViewData->GetState(nLineIndex);
1147 BOOL ret = FALSE;
1148 switch (state)
1150 case DIFFSTATE_REMOVED:
1151 case DIFFSTATE_THEIRSREMOVED:
1152 case DIFFSTATE_YOURSREMOVED:
1153 case DIFFSTATE_IDENTICALREMOVED:
1154 ret = TRUE;
1155 break;
1156 default:
1157 ret = FALSE;
1158 break;
1160 return ret;
1163 bool CBaseView::IsLineConflicted(int nLineIndex)
1165 DiffStates state = DIFFSTATE_UNKNOWN;
1166 if (m_pViewData)
1167 state = m_pViewData->GetState(nLineIndex);
1168 bool ret = false;
1169 switch (state)
1171 case DIFFSTATE_CONFLICTED:
1172 case DIFFSTATE_CONFLICTED_IGNORED:
1173 case DIFFSTATE_CONFLICTEMPTY:
1174 case DIFFSTATE_CONFLICTADDED:
1175 ret = true;
1176 break;
1177 default:
1178 ret = false;
1179 break;
1181 return ret;
1184 COLORREF CBaseView::IntenseColor(long scale, COLORREF col)
1186 // if the color is already dark (gray scale below 127),
1187 // then lighten the color by 'scale', otherwise darken it
1188 int Gray = (((int)GetRValue(col)) + GetGValue(col) + GetBValue(col))/3;
1189 if (Gray > 127)
1191 long red = MulDiv(GetRValue(col),(255-scale),255);
1192 long green = MulDiv(GetGValue(col),(255-scale),255);
1193 long blue = MulDiv(GetBValue(col),(255-scale),255);
1195 return RGB(red, green, blue);
1197 long R = MulDiv(255-GetRValue(col),scale,255)+GetRValue(col);
1198 long G = MulDiv(255-GetGValue(col),scale,255)+GetGValue(col);
1199 long B = MulDiv(255-GetBValue(col),scale,255)+GetBValue(col);
1201 return RGB(R, G, B);
1204 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1206 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1209 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1211 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < m_pViewData->GetCount())))
1212 return;
1214 EOL ending = m_pViewData->GetLineEnding(nLineIndex);
1215 if (m_bIconLFs)
1217 HICON hEndingIcon = NULL;
1218 switch (ending)
1220 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1221 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1222 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1223 default: return;
1225 if (origin.x < (rc.left-GetCharWidth()))
1226 return;
1227 // If EOL style has changed, color end-of-line markers as inline differences.
1229 m_bShowInlineDiff && m_pOtherViewData &&
1230 (nLineIndex < m_pOtherViewData->GetCount()) &&
1231 (ending != EOL_NOENDING) &&
1232 (ending != m_pOtherViewData->GetLineEnding(nLineIndex) &&
1233 (m_pOtherViewData->GetLineEnding(nLineIndex) != EOL_NOENDING))
1236 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1239 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1241 else
1243 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1244 CPen * oldpen = pDC->SelectObject(&pen);
1245 int yMiddle = origin.y + rc.Height()/2;
1246 int xMiddle = origin.x+GetCharWidth()/2;
1247 switch (ending)
1249 case EOL_CR:
1250 // arrow from right to left
1251 pDC->MoveTo(origin.x+GetCharWidth(), yMiddle);
1252 pDC->LineTo(origin.x, yMiddle);
1253 pDC->LineTo(origin.x+4, yMiddle+4);
1254 pDC->MoveTo(origin.x, yMiddle);
1255 pDC->LineTo(origin.x+4, yMiddle-4);
1256 break;
1257 case EOL_CRLF:
1258 // arrow from top to middle+2, then left
1259 pDC->MoveTo(origin.x+GetCharWidth(), rc.top);
1260 pDC->LineTo(origin.x+GetCharWidth(), yMiddle);
1261 pDC->LineTo(origin.x, yMiddle);
1262 pDC->LineTo(origin.x+4, yMiddle+4);
1263 pDC->MoveTo(origin.x, yMiddle);
1264 pDC->LineTo(origin.x+4, yMiddle-4);
1265 break;
1266 case EOL_LF:
1267 // arrow from top to bottom
1268 pDC->MoveTo(xMiddle, rc.top);
1269 pDC->LineTo(xMiddle, rc.bottom-1);
1270 pDC->LineTo(xMiddle+4, rc.bottom-5);
1271 pDC->MoveTo(xMiddle, rc.bottom-1);
1272 pDC->LineTo(xMiddle-4, rc.bottom-5);
1273 break;
1275 pDC->SelectObject(oldpen);
1279 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1281 const int THICKNESS = 2;
1282 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1283 if ((nLineIndex == m_nSelBlockStart) && m_bShowSelection)
1285 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1287 if ((nLineIndex == m_nSelBlockEnd) && m_bShowSelection)
1289 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1293 void CBaseView::DrawText(
1294 CDC * pDC, const CRect &rc, LPCTSTR text, int textlength, int nLineIndex, POINT coords, bool bModified, bool bInlineDiff)
1296 ASSERT(m_pViewData && (nLineIndex < m_pViewData->GetCount()));
1297 DiffStates diffState = m_pViewData->GetState(nLineIndex);
1299 // first suppose the whole line is selected
1300 int selectedStart = 0, selectedEnd = textlength;
1302 if ((m_ptSelectionStartPos.y > nLineIndex) || (m_ptSelectionEndPos.y < nLineIndex)
1303 || ! m_bShowSelection)
1305 // this line has no selected text
1306 selectedStart = textlength;
1308 else if ((m_ptSelectionStartPos.y == nLineIndex) || (m_ptSelectionEndPos.y == nLineIndex))
1310 // the line is partially selected
1311 int xoffs = m_nOffsetChar + (coords.x - GetMarginWidth()) / GetCharWidth();
1312 if (m_ptSelectionStartPos.y == nLineIndex)
1314 // the first line of selection
1315 int nSelectionStartOffset = CalculateActualOffset(m_ptSelectionStartPos.y, m_ptSelectionStartPos.x);
1316 selectedStart = max(min(nSelectionStartOffset - xoffs, textlength), 0);
1319 if (m_ptSelectionEndPos.y == nLineIndex)
1321 // the last line of selection
1322 int nSelectionEndOffset = CalculateActualOffset(m_ptSelectionEndPos.y, m_ptSelectionEndPos.x);
1323 selectedEnd = max(min(nSelectionEndOffset - xoffs, textlength), 0);
1327 COLORREF crBkgnd, crText;
1328 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1329 if (bModified || (diffState == DIFFSTATE_EDITED))
1330 crBkgnd = m_ModifiedBk;
1331 if (bInlineDiff)
1332 crBkgnd = InlineDiffColor(nLineIndex);
1334 pDC->SetBkColor(crBkgnd);
1335 pDC->SetTextColor(crText);
1336 if (selectedStart>=0)
1337 VERIFY(pDC->ExtTextOut(coords.x, coords.y, ETO_CLIPPED, &rc, text, selectedStart, NULL));
1339 long intenseColorScale = m_bFocused ? 70 : 30;
1340 pDC->SetBkColor(IntenseColor(intenseColorScale, crBkgnd));
1341 pDC->SetTextColor(IntenseColor(intenseColorScale, crText));
1342 VERIFY(pDC->ExtTextOut(
1343 coords.x + selectedStart * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,
1344 text + selectedStart, selectedEnd - selectedStart, NULL));
1346 pDC->SetBkColor(crBkgnd);
1347 pDC->SetTextColor(crText);
1348 if (textlength - selectedEnd >= 0)
1349 VERIFY(pDC->ExtTextOut(
1350 coords.x + selectedEnd * GetCharWidth(), coords.y, ETO_CLIPPED, &rc,
1351 text + selectedEnd, textlength - selectedEnd, NULL));
1354 bool CBaseView::DrawInlineDiff(CDC *pDC, const CRect &rc, int nLineIndex, const CString &line, CPoint &origin)
1356 if (!m_bShowInlineDiff || line.IsEmpty())
1357 return false;
1358 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
1359 return false;
1361 LPCTSTR pszDiffChars = NULL;
1362 int nDiffLength = 0;
1363 if (m_pOtherViewData)
1365 int index = min(nLineIndex, m_pOtherViewData->GetCount() - 1);
1366 pszDiffChars = m_pOtherViewData->GetLine(index);
1367 nDiffLength = m_pOtherViewData->GetLine(index).GetLength();
1370 if (!pszDiffChars || !*pszDiffChars)
1371 return false;
1373 CString diffline;
1374 ExpandChars(pszDiffChars, 0, nDiffLength, diffline);
1375 svn_diff_t * diff = NULL;
1376 m_svnlinediff.Diff(&diff, line, line.GetLength(), diffline, diffline.GetLength(), m_bInlineWordDiff);
1377 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
1378 return false;
1380 int lineoffset = 0;
1381 std::deque<int> removedPositions;
1382 while (diff)
1384 apr_off_t len = diff->original_length;
1386 CString s;
1387 for (int i = 0; i < len; ++i)
1389 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
1390 lineoffset++;
1392 bool isModified = diff->type == svn_diff__type_diff_modified;
1393 DrawText(pDC, rc, (LPCTSTR)s, s.GetLength(), nLineIndex, origin, true, isModified);
1394 origin.x += pDC->GetTextExtent(s).cx;
1396 if (isModified && (len < diff->modified_length))
1397 removedPositions.push_back(origin.x - 1);
1399 diff = diff->next;
1401 // Draw vertical bars at removed chunks' positions.
1402 for (std::deque<int>::iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
1403 pDC->FillSolidRect(*it, rc.top, 1, rc.Height(), m_InlineRemovedBk);
1404 return true;
1407 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1409 if (nLineIndex >= GetLineCount())
1410 nLineIndex = -1;
1411 ASSERT(nLineIndex >= -1);
1413 if ((nLineIndex == -1) || !m_pViewData)
1415 // Draw line beyond the text
1416 COLORREF crBkgnd, crText;
1417 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1418 pDC->FillSolidRect(rc, crBkgnd);
1419 return;
1422 DiffStates diffState = m_pViewData->GetState(nLineIndex);
1423 COLORREF crBkgnd, crText;
1424 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1426 if (diffState == DIFFSTATE_CONFLICTED)
1428 // conflicted lines are shown without 'text' on them
1429 CRect rect = rc;
1430 pDC->FillSolidRect(rc, crBkgnd);
1431 // now draw some faint text patterns
1432 pDC->SetTextColor(IntenseColor(130, crBkgnd));
1433 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1434 DrawBlockLine(pDC, rc, nLineIndex);
1435 return;
1438 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1439 int nLength = GetLineLength(nLineIndex);
1440 if (nLength == 0)
1442 // Draw the empty line
1443 pDC->FillSolidRect(rc, crBkgnd);
1444 DrawBlockLine(pDC, rc, nLineIndex);
1445 DrawLineEnding(pDC, rc, nLineIndex, origin);
1446 return;
1448 LPCTSTR pszChars = GetLineChars(nLineIndex);
1449 if (pszChars == NULL)
1450 return;
1452 CheckOtherView();
1454 // Draw the line
1456 pDC->SelectObject(GetFont(FALSE, FALSE, IsLineRemoved(nLineIndex)));
1457 CString line;
1458 ExpandChars(pszChars, 0, nLength, line);
1460 int nWidth = rc.right - origin.x;
1461 int savedx = origin.x;
1462 bool bInlineDiffDrawn =
1463 nWidth > 0 && diffState != DIFFSTATE_NORMAL &&
1464 DrawInlineDiff(pDC, rc, nLineIndex, line, origin);
1466 if (!bInlineDiffDrawn)
1468 int nCount = min(line.GetLength(), nWidth / GetCharWidth() + 1);
1469 DrawText(pDC, rc, line, nCount, nLineIndex, origin, false, false);
1472 origin.x = savedx + pDC->GetTextExtent(line).cx;
1474 // draw white space after the end of line
1475 CRect frect = rc;
1476 if (origin.x > frect.left)
1477 frect.left = origin.x;
1478 if (bInlineDiffDrawn)
1479 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1480 if (frect.right > frect.left)
1481 pDC->FillSolidRect(frect, crBkgnd);
1482 // draw the whitespace chars
1483 if (m_bViewWhitespace)
1485 int xpos = 0;
1486 int y = rc.top + (rc.bottom-rc.top)/2;
1488 int nActualOffset = 0;
1489 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1491 if (*pszChars == _T('\t'))
1492 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1493 else
1494 nActualOffset++;
1495 pszChars++;
1497 if (nActualOffset > m_nOffsetChar)
1498 pszChars--;
1500 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1501 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1502 while (*pszChars)
1504 switch (*pszChars)
1506 case _T('\t'):
1508 // draw an arrow
1509 CPen * oldPen = pDC->SelectObject(&pen);
1510 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1511 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1512 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1513 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1514 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1515 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1516 xpos += nSpaces;
1517 pDC->SelectObject(oldPen);
1519 break;
1520 case _T(' '):
1522 // draw a small dot
1523 CPen * oldPen = pDC->SelectObject(&pen2);
1524 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1525 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1526 xpos++;
1527 pDC->SelectObject(oldPen);
1529 break;
1530 default:
1531 xpos++;
1532 break;
1534 pszChars++;
1537 DrawBlockLine(pDC, rc, nLineIndex);
1538 DrawLineEnding(pDC, rc, nLineIndex, origin);
1541 void CBaseView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)
1543 if (nCount <= 0)
1545 line = _T("");
1546 return;
1549 int nTabSize = GetTabSize();
1551 int nActualOffset = 0;
1552 for (int i=0; i<nOffset; i++)
1554 if (pszChars[i] == _T('\t'))
1555 nActualOffset += (nTabSize - nActualOffset % nTabSize);
1556 else
1557 nActualOffset ++;
1560 pszChars += nOffset;
1561 int nLength = nCount;
1563 int nTabCount = 0;
1564 for (int i=0; i<nLength; i++)
1566 if (pszChars[i] == _T('\t'))
1567 nTabCount ++;
1570 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
1571 int nCurPos = 0;
1572 if (nTabCount > 0 || m_bViewWhitespace)
1574 for (int i=0; i<nLength; i++)
1576 if (pszChars[i] == _T('\t'))
1578 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
1579 while (nSpaces > 0)
1581 pszBuf[nCurPos ++] = _T(' ');
1582 nSpaces --;
1585 else
1587 pszBuf[nCurPos] = pszChars[i];
1588 nCurPos ++;
1592 else
1594 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
1595 nCurPos = nLength;
1597 pszBuf[nCurPos] = 0;
1598 line.ReleaseBuffer();
1601 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
1603 if ((m_pwndLeft)&&(m_pwndRight))
1605 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
1606 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
1608 else
1610 if (m_pwndLeft)
1611 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
1612 if (m_pwndRight)
1613 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
1615 if (m_pwndBottom)
1616 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
1617 if (m_pwndLocator)
1618 m_pwndLocator->Invalidate();
1621 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
1623 //almost the same as ScrollAllToLine, but try to put the line in the
1624 //middle of the view, not on top
1625 int nNewTopLine = nNewLine - GetScreenLines()/2;
1626 if (nNewTopLine < 0)
1627 nNewTopLine = 0;
1628 if (m_pViewData)
1630 if (nNewTopLine >= m_pViewData->GetCount())
1631 nNewTopLine = m_pViewData->GetCount()-1;
1632 if (bAll)
1633 ScrollAllToLine(nNewTopLine);
1634 else
1635 ScrollToLine(nNewTopLine);
1639 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
1641 return TRUE;
1644 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
1646 if (CView::OnCreate(lpCreateStruct) == -1)
1647 return -1;
1649 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
1650 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
1651 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
1652 m_lfBaseFont.lfHeight = 0;
1653 m_lfBaseFont.lfWeight = FW_NORMAL;
1654 m_lfBaseFont.lfItalic = FALSE;
1655 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
1656 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
1657 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1658 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
1659 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
1661 return 0;
1664 void CBaseView::OnDestroy()
1666 CView::OnDestroy();
1667 for (int i=0; i<MAXFONTS; i++)
1669 if (m_apFonts[i] != NULL)
1671 m_apFonts[i]->DeleteObject();
1672 delete m_apFonts[i];
1673 m_apFonts[i] = NULL;
1676 if (m_pCacheBitmap != NULL)
1678 delete m_pCacheBitmap;
1679 m_pCacheBitmap = NULL;
1683 void CBaseView::OnSize(UINT nType, int cx, int cy)
1685 if (m_pCacheBitmap != NULL)
1687 m_pCacheBitmap->DeleteObject();
1688 delete m_pCacheBitmap;
1689 m_pCacheBitmap = NULL;
1691 // make sure the view header is redrawn
1692 CRect rcScroll;
1693 GetClientRect(&rcScroll);
1694 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1695 InvalidateRect(&rcScroll, FALSE);
1697 m_nScreenLines = -1;
1698 m_nScreenChars = -1;
1699 RecalcVertScrollBar();
1700 RecalcHorzScrollBar();
1701 CView::OnSize(nType, cx, cy);
1704 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
1706 if (m_pwndLeft)
1707 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
1708 if (m_pwndRight)
1709 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
1710 if (m_pwndBottom)
1711 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
1712 if (m_pwndLocator)
1713 m_pwndLocator->Invalidate();
1714 return CView::OnMouseWheel(nFlags, zDelta, pt);
1717 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
1719 if (GetKeyState(VK_CONTROL)&0x8000)
1721 // Ctrl-Wheel scrolls sideways
1722 ScrollSide(-zDelta/30);
1724 else
1726 int nLineCount = GetLineCount();
1727 int nTopLine = m_nTopLine;
1728 nTopLine -= (zDelta/30);
1729 if (nTopLine < 0)
1730 nTopLine = 0;
1731 if (nTopLine >= nLineCount)
1732 nTopLine = nLineCount - 1;
1733 ScrollToLine(nTopLine, TRUE);
1737 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
1739 if (nHitTest == HTCLIENT)
1741 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
1742 return TRUE;
1744 return CView::OnSetCursor(pWnd, nHitTest, message);
1747 void CBaseView::OnKillFocus(CWnd* pNewWnd)
1749 CView::OnKillFocus(pNewWnd);
1750 m_bFocused = FALSE;
1751 UpdateCaret();
1752 Invalidate();
1755 void CBaseView::OnSetFocus(CWnd* pOldWnd)
1757 CView::OnSetFocus(pOldWnd);
1758 m_bFocused = TRUE;
1759 UpdateCaret();
1760 Invalidate();
1763 int CBaseView::GetLineFromPoint(CPoint point)
1765 ScreenToClient(&point);
1766 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
1769 bool CBaseView::OnContextMenu(CPoint /*point*/, int /*nLine*/, DiffStates /*state*/)
1771 return false;
1774 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
1776 int nLine = GetLineFromPoint(point);
1778 if (!m_pViewData)
1779 return;
1780 if (m_nSelBlockEnd >= GetLineCount())
1781 m_nSelBlockEnd = GetLineCount()-1;
1782 if ((nLine <= m_pViewData->GetCount())&&(nLine > m_nTopLine))
1784 int nIndex = nLine - 1;
1785 DiffStates state = m_pViewData->GetState(nIndex);
1786 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
1788 // if there's nothing selected, or if the selection is outside the window then
1789 // select the diff block under the cursor.
1790 if (((m_nSelBlockStart<0)&&(m_nSelBlockEnd<0))||
1791 ((m_nSelBlockEnd < m_nTopLine)||(m_nSelBlockStart > m_nTopLine+m_nScreenLines)))
1793 while (nIndex >= 0)
1795 if (nIndex == 0)
1797 nIndex--;
1798 break;
1800 if (state != m_pViewData->GetState(--nIndex))
1801 break;
1803 m_nSelBlockStart = nIndex+1;
1804 while (nIndex < (m_pViewData->GetCount()-1))
1806 if (state != m_pViewData->GetState(++nIndex))
1807 break;
1809 if ((nIndex == (m_pViewData->GetCount()-1))&&(state == m_pViewData->GetState(nIndex)))
1810 m_nSelBlockEnd = nIndex;
1811 else
1812 m_nSelBlockEnd = nIndex-1;
1813 SetupSelection(m_nSelBlockStart, m_nSelBlockEnd);
1814 m_ptCaretPos.x = 0;
1815 m_ptCaretPos.y = nLine - 1;
1816 UpdateCaret();
1819 if (((state == DIFFSTATE_NORMAL)||(state == DIFFSTATE_UNKNOWN)) &&
1820 (m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))
1822 // find a more 'relevant' state in the selection
1823 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; ++i)
1825 state = m_pViewData->GetState(i);
1826 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
1827 break;
1830 bool bKeepSelection = OnContextMenu(point, nLine, state);
1831 if (! bKeepSelection)
1832 ClearSelection();
1833 RefreshViews();
1837 void CBaseView::RefreshViews()
1839 if (m_pwndLeft)
1841 m_pwndLeft->UpdateStatusBar();
1842 m_pwndLeft->Invalidate();
1844 if (m_pwndRight)
1846 m_pwndRight->UpdateStatusBar();
1847 m_pwndRight->Invalidate();
1849 if (m_pwndBottom)
1851 m_pwndBottom->UpdateStatusBar();
1852 m_pwndBottom->Invalidate();
1854 if (m_pwndLocator)
1855 m_pwndLocator->Invalidate();
1858 void CBaseView::GoToFirstDifference()
1860 m_ptCaretPos.y = 0;
1861 SelectNextBlock(1, false, false);
1864 void CBaseView::HiglightLines(int start, int end /* = -1 */)
1866 ClearSelection();
1867 m_nSelBlockStart = start;
1868 if (end < 0)
1869 end = start;
1870 m_nSelBlockEnd = end;
1871 m_ptCaretPos.x = 0;
1872 m_ptCaretPos.y = start;
1873 UpdateCaret();
1874 Invalidate();
1877 void CBaseView::SetupSelection(int start, int end)
1879 if (IsBottomViewGood())
1881 m_pwndBottom->m_nSelBlockStart = start;
1882 m_pwndBottom->m_nSelBlockEnd = end;
1883 m_pwndBottom->Invalidate();
1885 if (IsLeftViewGood())
1887 m_pwndLeft->m_nSelBlockStart = start;
1888 m_pwndLeft->m_nSelBlockEnd = end;
1889 m_pwndLeft->Invalidate();
1891 if (IsRightViewGood())
1893 m_pwndRight->m_nSelBlockStart = start;
1894 m_pwndRight->m_nSelBlockEnd = end;
1895 m_pwndRight->Invalidate();
1899 void CBaseView::OnMergePreviousconflict()
1901 SelectNextBlock(-1, true);
1904 void CBaseView::OnMergeNextconflict()
1906 SelectNextBlock(1, true);
1909 void CBaseView::OnMergeNextdifference()
1911 SelectNextBlock(1, false);
1914 void CBaseView::OnMergePreviousdifference()
1916 SelectNextBlock(-1, false);
1919 void CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */)
1921 if (! m_pViewData)
1922 return;
1924 if (m_pViewData->GetCount() == 0)
1925 return;
1927 int nCenterPos = m_ptCaretPos.y;
1928 int nLimit = 0;
1929 if (nDirection > 0)
1930 nLimit = m_pViewData->GetCount() - 1;
1932 if (nCenterPos >= m_pViewData->GetCount())
1933 nCenterPos = m_pViewData->GetCount()-1;
1935 if (bSkipEndOfCurrentBlock)
1937 // Find end of current block
1938 DiffStates state = m_pViewData->GetState(nCenterPos);
1939 while ((nCenterPos != nLimit) &&
1940 (m_pViewData->GetState(nCenterPos)==state))
1941 nCenterPos += nDirection;
1944 // Find next diff/conflict block
1945 while (nCenterPos != nLimit)
1947 DiffStates linestate = m_pViewData->GetState(nCenterPos);
1948 if (!bConflict &&
1949 (linestate != DIFFSTATE_NORMAL) &&
1950 (linestate != DIFFSTATE_UNKNOWN))
1951 break;
1952 if (bConflict &&
1953 ((linestate == DIFFSTATE_CONFLICTADDED) ||
1954 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
1955 (linestate == DIFFSTATE_CONFLICTED) ||
1956 (linestate == DIFFSTATE_CONFLICTEMPTY)))
1957 break;
1959 nCenterPos += nDirection;
1962 // Find end of new block
1963 DiffStates state = m_pViewData->GetState(nCenterPos);
1964 int nBlockEnd = nCenterPos;
1965 while ((nBlockEnd != nLimit) &&
1966 (state == m_pViewData->GetState(nBlockEnd + nDirection)))
1967 nBlockEnd += nDirection;
1969 int nTopPos = nCenterPos - (GetScreenLines()/2);
1970 if (nTopPos < 0)
1971 nTopPos = 0;
1973 m_ptCaretPos.x = 0;
1974 m_ptCaretPos.y = nCenterPos;
1975 ClearSelection();
1976 if (nDirection > 0)
1977 SetupSelection(nCenterPos, nBlockEnd);
1978 else
1979 SetupSelection(nBlockEnd, nCenterPos);
1981 ScrollAllToLine(nTopPos, FALSE);
1982 RecalcAllVertScrollBars(TRUE);
1983 m_nCaretGoalPos = 0;
1984 UpdateCaret();
1985 ShowDiffLines(nCenterPos);
1988 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
1990 // need to handle both ANSI and UNICODE versions of the message
1991 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
1992 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
1993 CString strTipText;
1994 UINT nID = (UINT)pNMHDR->idFrom;
1995 if (pNMHDR->code == TTN_NEEDTEXTA && (pTTTA->uFlags & TTF_IDISHWND) ||
1996 pNMHDR->code == TTN_NEEDTEXTW && (pTTTW->uFlags & TTF_IDISHWND))
1998 // idFrom is actually the HWND of the tool
1999 nID = ::GetDlgCtrlID((HWND)nID);
2002 if (pNMHDR->idFrom == (UINT)m_hWnd)
2004 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
2006 strTipText = m_sWindowName.Mid(2) + _T("\r\n") + m_sFullFilePath;
2008 else
2010 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2013 else
2014 return FALSE;
2016 *pResult = 0;
2017 if (strTipText.IsEmpty())
2018 return TRUE;
2020 if (pNMHDR->code == TTN_NEEDTEXTA)
2022 pTTTA->lpszText = m_szTip;
2023 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2025 else
2027 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2028 pTTTW->lpszText = m_wszTip;
2031 return TRUE; // message was handled
2035 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2037 CRect rcClient;
2038 GetClientRect(rcClient);
2039 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2040 if (textrect.PtInRect(point))
2042 // inside the header part of the view (showing the filename)
2043 pTI->hwnd = this->m_hWnd;
2044 this->GetClientRect(&pTI->rect);
2045 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2046 pTI->uId = (UINT)m_hWnd;
2047 pTI->lpszText = LPSTR_TEXTCALLBACK;
2049 // we want multi line tooltips
2050 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2051 if (pToolTip->GetSafeHwnd() != NULL)
2053 pToolTip->SetMaxTipWidth(INT_MAX);
2056 return 1;
2058 return -1;
2061 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2063 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2064 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2065 switch (nChar)
2067 case VK_PRIOR:
2069 m_ptCaretPos.y -= GetScreenLines();
2070 m_ptCaretPos.y = max(m_ptCaretPos.y, 0);
2071 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2072 if (bShift)
2073 AdjustSelection();
2074 else
2075 ClearSelection();
2076 UpdateCaret();
2077 EnsureCaretVisible();
2078 ShowDiffLines(m_ptCaretPos.y);
2080 break;
2081 case VK_NEXT:
2083 m_ptCaretPos.y += GetScreenLines();
2084 if (m_ptCaretPos.y >= GetLineCount())
2085 m_ptCaretPos.y = GetLineCount()-1;
2086 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2087 if (bShift)
2088 AdjustSelection();
2089 else
2090 ClearSelection();
2091 UpdateCaret();
2092 EnsureCaretVisible();
2093 ShowDiffLines(m_ptCaretPos.y);
2095 break;
2096 case VK_HOME:
2098 if (bControl)
2100 ScrollAllToLine(0);
2101 m_ptCaretPos.x = 0;
2102 m_ptCaretPos.y = 0;
2103 m_nCaretGoalPos = 0;
2104 if (bShift)
2105 AdjustSelection();
2106 else
2107 ClearSelection();
2108 UpdateCaret();
2110 else
2112 m_ptCaretPos.x = 0;
2113 m_nCaretGoalPos = 0;
2114 if (bShift)
2115 AdjustSelection();
2116 else
2117 ClearSelection();
2118 EnsureCaretVisible();
2119 UpdateCaret();
2122 break;
2123 case VK_END:
2125 if (bControl)
2127 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2128 m_ptCaretPos.y = GetLineCount()-1;
2129 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2130 UpdateGoalPos();
2131 if (bShift)
2132 AdjustSelection();
2133 else
2134 ClearSelection();
2135 UpdateCaret();
2137 else
2139 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2140 UpdateGoalPos();
2141 if (bShift)
2142 AdjustSelection();
2143 else
2144 ClearSelection();
2145 EnsureCaretVisible();
2146 UpdateCaret();
2149 break;
2150 case VK_BACK:
2152 if (m_bCaretHidden)
2153 break;
2155 if (! HasTextSelection()) {
2156 if (m_ptCaretPos.y == 0 && m_ptCaretPos.x == 0)
2157 break;
2158 m_ptSelectionEndPos = m_ptCaretPos;
2159 MoveCaretLeft();
2160 m_ptSelectionStartPos = m_ptCaretPos;
2162 RemoveSelectedText();
2164 break;
2165 case VK_DELETE:
2167 if (m_bCaretHidden)
2168 break;
2170 if (! HasTextSelection()) {
2171 if (! MoveCaretRight())
2172 break;
2173 m_ptSelectionEndPos = m_ptCaretPos;
2174 MoveCaretLeft();
2175 m_ptSelectionStartPos = m_ptCaretPos;
2177 RemoveSelectedText();
2179 break;
2181 CView::OnKeyDown(nChar, nRepCnt, nFlags);
2184 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
2186 int nClickedLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2187 nClickedLine--; //we need the index
2188 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
2190 m_ptCaretPos.y = nClickedLine;
2191 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2192 UpdateGoalPos();
2194 if (nFlags & MK_SHIFT)
2195 AdjustSelection();
2196 else
2198 ClearSelection();
2199 SetupSelection(m_ptCaretPos.y, m_ptCaretPos.y);
2202 UpdateCaret();
2204 Invalidate();
2207 CView::OnLButtonDown(nFlags, point);
2210 void CBaseView::OnEditCopy()
2212 if ((m_ptSelectionStartPos.x == m_ptSelectionEndPos.x)&&(m_ptSelectionStartPos.y == m_ptSelectionEndPos.y))
2213 return;
2214 // first store the selected lines in one CString
2215 CString sCopyData;
2216 for (int i=m_ptSelectionStartPos.y; i<=m_ptSelectionEndPos.y; i++)
2218 switch (m_pViewData->GetState(i))
2220 case DIFFSTATE_EMPTY:
2221 break;
2222 case DIFFSTATE_UNKNOWN:
2223 case DIFFSTATE_NORMAL:
2224 case DIFFSTATE_REMOVED:
2225 case DIFFSTATE_REMOVEDWHITESPACE:
2226 case DIFFSTATE_ADDED:
2227 case DIFFSTATE_ADDEDWHITESPACE:
2228 case DIFFSTATE_WHITESPACE:
2229 case DIFFSTATE_WHITESPACE_DIFF:
2230 case DIFFSTATE_CONFLICTED:
2231 case DIFFSTATE_CONFLICTED_IGNORED:
2232 case DIFFSTATE_CONFLICTADDED:
2233 case DIFFSTATE_CONFLICTEMPTY:
2234 case DIFFSTATE_CONFLICTRESOLVED:
2235 case DIFFSTATE_IDENTICALREMOVED:
2236 case DIFFSTATE_IDENTICALADDED:
2237 case DIFFSTATE_THEIRSREMOVED:
2238 case DIFFSTATE_THEIRSADDED:
2239 case DIFFSTATE_YOURSREMOVED:
2240 case DIFFSTATE_YOURSADDED:
2241 case DIFFSTATE_EDITED:
2242 sCopyData += m_pViewData->GetLine(i);
2243 sCopyData += _T("\r\n");
2244 break;
2247 // remove the last \r\n
2248 sCopyData = sCopyData.Left(sCopyData.GetLength()-2);
2249 // remove the non-selected chars from the first line
2250 sCopyData = sCopyData.Mid(m_ptSelectionStartPos.x);
2251 // remove the non-selected chars from the last line
2252 int lastLinePos = sCopyData.ReverseFind('\n');
2253 lastLinePos += 1;
2254 if (lastLinePos == 0)
2255 lastLinePos -= m_ptSelectionStartPos.x;
2256 sCopyData = sCopyData.Left(lastLinePos+m_ptSelectionEndPos.x);
2257 if (!sCopyData.IsEmpty())
2259 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
2263 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
2265 if (m_pMainFrame->m_nMoveMovesToIgnore > 0) {
2266 --m_pMainFrame->m_nMoveMovesToIgnore;
2267 CView::OnMouseMove(nFlags, point);
2268 return;
2271 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2272 nMouseLine--; //we need the index
2273 if (nMouseLine < -1)
2275 nMouseLine = -1;
2277 ShowDiffLines(nMouseLine);
2279 KillTimer(IDT_SCROLLTIMER);
2280 if (nFlags & MK_LBUTTON)
2282 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
2283 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
2284 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2285 if (((m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))&&
2286 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
2288 m_ptCaretPos.y = nMouseLine;
2289 m_ptCaretPos.x = charIndex;
2290 UpdateGoalPos();
2291 AdjustSelection();
2292 UpdateCaret();
2293 Invalidate();
2294 UpdateWindow();
2296 if (nMouseLine < m_nTopLine)
2298 ScrollToLine(m_nTopLine-1, TRUE);
2299 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2301 if (nMouseLine >= m_nTopLine + GetScreenLines())
2303 ScrollToLine(m_nTopLine+1, TRUE);
2304 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2306 if (charIndex <= m_nOffsetChar)
2308 ScrollSide(-1);
2309 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2311 if (charIndex >= (GetScreenChars()+m_nOffsetChar))
2313 ScrollSide(1);
2314 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2318 if (!m_bMouseWithin)
2320 m_bMouseWithin = TRUE;
2321 TRACKMOUSEEVENT tme;
2322 tme.cbSize = sizeof(TRACKMOUSEEVENT);
2323 tme.dwFlags = TME_LEAVE;
2324 tme.hwndTrack = m_hWnd;
2325 _TrackMouseEvent(&tme);
2328 CView::OnMouseMove(nFlags, point);
2331 void CBaseView::OnMouseLeave()
2333 ShowDiffLines(-1);
2334 m_bMouseWithin = FALSE;
2335 KillTimer(IDT_SCROLLTIMER);
2336 CView::OnMouseLeave();
2339 void CBaseView::OnTimer(UINT_PTR nIDEvent)
2341 if (nIDEvent == IDT_SCROLLTIMER)
2343 POINT point;
2344 GetCursorPos(&point);
2345 ScreenToClient(&point);
2346 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2347 nMouseLine--; //we need the index
2348 if (nMouseLine < -1)
2350 nMouseLine = -1;
2352 if (GetKeyState(VK_LBUTTON)&0x8000)
2354 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
2355 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
2356 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2357 if (nMouseLine < m_nTopLine)
2359 ScrollToLine(m_nTopLine-1, TRUE);
2360 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2362 if (nMouseLine >= m_nTopLine + GetScreenLines())
2364 ScrollToLine(m_nTopLine+1, TRUE);
2365 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2367 if (charIndex <= m_nOffsetChar)
2369 ScrollSide(-1);
2370 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2372 if (charIndex >= GetScreenChars())
2374 ScrollSide(1);
2375 SetTimer(IDT_SCROLLTIMER, 20, NULL);
2381 CView::OnTimer(nIDEvent);
2384 void CBaseView::SelectLines(int nLine1, int nLine2)
2386 if (nLine2 == -1)
2387 nLine2 = nLine1;
2388 m_nSelBlockStart = nLine1;
2389 m_nSelBlockEnd = nLine2;
2390 Invalidate();
2393 void CBaseView::ShowDiffLines(int nLine)
2395 if ((nLine >= m_nTopLine)&&(nLine < GetLineCount()))
2397 if ((m_pwndRight)&&(m_pwndRight->m_pViewData)&&(m_pwndLeft)&&(m_pwndLeft->m_pViewData)&&(!m_pMainFrame->m_bOneWay))
2399 nLine = (nLine > m_pwndRight->m_pViewData->GetCount() ? -1 : nLine);
2400 nLine = (nLine > m_pwndLeft->m_pViewData->GetCount() ? -1 : nLine);
2402 if (nLine >= 0)
2404 if (nLine != m_nMouseLine)
2406 m_nMouseLine = nLine;
2407 if (nLine >= GetLineCount())
2408 nLine = -1;
2409 m_pwndLineDiffBar->ShowLines(nLine);
2414 else
2416 m_pwndLineDiffBar->ShowLines(nLine);
2420 void CBaseView::UseTheirAndYourBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
2422 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2423 return;
2424 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2426 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2427 m_pwndBottom->m_pViewData->SetLine(i, m_pwndLeft->m_pViewData->GetLine(i));
2428 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2429 m_pwndBottom->m_pViewData->SetState(i, m_pwndLeft->m_pViewData->GetState(i));
2430 m_pwndBottom->m_pViewData->SetLineEnding(i, EOL_AUTOLINE);
2431 if (m_pwndBottom->IsLineConflicted(i))
2433 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2434 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2435 else
2436 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
2438 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2441 // your block is done, now insert their block
2442 int index = m_nSelBlockEnd+1;
2443 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2445 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);
2446 m_pwndBottom->m_pViewData->InsertData(index, m_pwndRight->m_pViewData->GetData(i));
2447 if (m_pwndBottom->IsLineConflicted(index))
2449 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2450 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2451 else
2452 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
2454 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);
2455 index++;
2457 // adjust line numbers
2458 for (int i=m_nSelBlockEnd+1; i<GetLineCount(); ++i)
2460 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
2461 if (oldline >= 0)
2462 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2465 // now insert an empty block in both yours and theirs
2466 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2468 rightstate.addedlines.push_back(m_nSelBlockStart);
2469 m_pwndRight->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2470 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2471 leftstate.addedlines.push_back(m_nSelBlockEnd+1);
2473 RecalcAllVertScrollBars();
2474 m_pwndBottom->SetModified();
2475 m_pwndLeft->SetModified();
2476 m_pwndRight->SetModified();
2479 void CBaseView::UseYourAndTheirBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
2481 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2482 return;
2483 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2485 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2486 m_pwndBottom->m_pViewData->SetLine(i, m_pwndRight->m_pViewData->GetLine(i));
2487 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2488 m_pwndBottom->m_pViewData->SetState(i, m_pwndRight->m_pViewData->GetState(i));
2489 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2490 m_pwndBottom->m_pViewData->SetLineEnding(i, EOL_AUTOLINE);
2491 if (m_pwndBottom->IsLineConflicted(i))
2493 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2494 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2495 else
2496 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
2498 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2501 // your block is done, now insert their block
2502 int index = m_nSelBlockEnd+1;
2503 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2505 bottomstate.addedlines.push_back(m_nSelBlockEnd+1);
2506 m_pwndBottom->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));
2507 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);
2508 if (m_pwndBottom->IsLineConflicted(index))
2510 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
2511 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
2512 else
2513 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
2515 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);
2516 index++;
2518 // adjust line numbers
2519 for (int i=m_nSelBlockEnd+1; i<m_pwndBottom->GetLineCount(); ++i)
2521 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
2522 if (oldline >= 0)
2523 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2526 // now insert an empty block in both yours and theirs
2527 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2529 leftstate.addedlines.push_back(m_nSelBlockStart);
2530 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2531 m_pwndRight->m_pViewData->InsertData(m_nSelBlockEnd+1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2532 rightstate.addedlines.push_back(m_nSelBlockEnd+1);
2535 RecalcAllVertScrollBars();
2536 m_pwndBottom->SetModified();
2537 m_pwndLeft->SetModified();
2538 m_pwndRight->SetModified();
2541 void CBaseView::UseBothRightFirst(viewstate &rightstate, viewstate &leftstate)
2543 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2544 return;
2545 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2547 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2548 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
2551 // your block is done, now insert their block
2552 int index = m_nSelBlockEnd+1;
2553 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2555 rightstate.addedlines.push_back(m_nSelBlockEnd+1);
2556 m_pwndRight->m_pViewData->InsertData(index, m_pwndLeft->m_pViewData->GetData(i));
2557 m_pwndRight->m_pViewData->SetState(index++, DIFFSTATE_THEIRSADDED);
2559 // adjust line numbers
2560 index--;
2561 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
2563 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
2564 if (oldline >= 0)
2565 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
2568 // now insert an empty block in the left view
2569 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2571 leftstate.addedlines.push_back(m_nSelBlockStart);
2572 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2574 RecalcAllVertScrollBars();
2575 m_pwndLeft->SetModified();
2576 m_pwndRight->SetModified();
2579 void CBaseView::UseBothLeftFirst(viewstate &rightstate, viewstate &leftstate)
2581 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
2582 return;
2583 // get line number from just before the block
2584 long linenumber = 0;
2585 if (m_nSelBlockStart > 0)
2586 linenumber = m_pwndRight->m_pViewData->GetLineNumber(m_nSelBlockStart-1);
2587 linenumber++;
2588 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
2590 rightstate.addedlines.push_back(m_nSelBlockStart);
2591 m_pwndRight->m_pViewData->InsertData(i, m_pwndLeft->m_pViewData->GetLine(i), DIFFSTATE_THEIRSADDED, linenumber++, m_pwndLeft->m_pViewData->GetLineEnding(i));
2593 // adjust line numbers
2594 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
2596 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
2597 if (oldline >= 0)
2598 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(m_nSelBlockEnd-m_nSelBlockStart)+1);
2601 // now insert an empty block in left view
2602 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
2604 leftstate.addedlines.push_back(m_nSelBlockEnd + 1);
2605 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockEnd + 1, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
2607 RecalcAllVertScrollBars();
2608 m_pwndLeft->SetModified();
2609 m_pwndRight->SetModified();
2612 void CBaseView::UpdateCaret()
2614 if (m_ptCaretPos.y >= GetLineCount())
2615 m_ptCaretPos.y = GetLineCount()-1;
2616 if (m_ptCaretPos.y < 0)
2617 m_ptCaretPos.y = 0;
2618 if (m_ptCaretPos.x > GetLineLength(m_ptCaretPos.y))
2619 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2620 if (m_ptCaretPos.x < 0)
2621 m_ptCaretPos.x = 0;
2623 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2625 if (m_bFocused && !m_bCaretHidden &&
2626 m_ptCaretPos.y >= m_nTopLine &&
2627 m_ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
2628 nCaretOffset >= m_nOffsetChar &&
2629 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
2631 CreateSolidCaret(2, GetLineHeight());
2632 SetCaretPos(TextToClient(m_ptCaretPos));
2633 ShowCaret();
2635 else
2637 HideCaret();
2641 void CBaseView::EnsureCaretVisible()
2643 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2645 if (m_ptCaretPos.y < m_nTopLine)
2646 ScrollAllToLine(m_ptCaretPos.y);
2647 if (m_ptCaretPos.y >= (m_nTopLine+GetScreenLines()))
2648 ScrollAllToLine(m_ptCaretPos.y-GetScreenLines()+1);
2649 if (nCaretOffset < m_nOffsetChar)
2650 ScrollToChar(nCaretOffset);
2651 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
2652 ScrollToChar(nCaretOffset-GetScreenChars()+1);
2655 int CBaseView::CalculateActualOffset(int nLineIndex, int nCharIndex) const
2657 int nLength = GetLineLength(nLineIndex);
2658 ASSERT(nCharIndex >= 0);
2659 if (nCharIndex > nLength)
2660 nCharIndex = nLength;
2661 LPCTSTR pszChars = GetLineChars(nLineIndex);
2662 int nOffset = 0;
2663 int nTabSize = GetTabSize();
2664 for (int I = 0; I < nCharIndex; I ++)
2666 if (pszChars[I] == _T('\t'))
2667 nOffset += (nTabSize - nOffset % nTabSize);
2668 else
2669 nOffset++;
2671 return nOffset;
2674 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset) const
2676 int nLength = GetLineLength(nLineIndex);
2677 LPCTSTR pszLine = GetLineChars(nLineIndex);
2678 int nIndex = 0;
2679 int nOffset = 0;
2680 int nTabSize = GetTabSize();
2681 while (nOffset < nActualOffset && nIndex < nLength)
2683 if (pszLine[nIndex] == _T('\t'))
2684 nOffset += (nTabSize - nOffset % nTabSize);
2685 else
2686 ++nOffset;
2687 ++nIndex;
2689 return nIndex;
2692 POINT CBaseView::TextToClient(const POINT& point)
2694 POINT pt;
2695 pt.y = max(0, (point.y - m_nTopLine) * GetLineHeight());
2696 pt.x = CalculateActualOffset(point.y, point.x);
2698 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth() + GetMarginWidth();
2699 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
2700 return pt;
2703 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
2705 CView::OnChar(nChar, nRepCnt, nFlags);
2707 if (m_bCaretHidden)
2708 return;
2710 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
2711 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
2712 return;
2714 if ((nChar > 31)||(nChar == VK_TAB))
2716 RemoveSelectedText();
2717 AddUndoLine(m_ptCaretPos.y);
2718 CString sLine = GetLineChars(m_ptCaretPos.y);
2719 sLine.Insert(m_ptCaretPos.x, (wchar_t)nChar);
2720 m_pViewData->SetLine(m_ptCaretPos.y, sLine);
2721 m_pViewData->SetState(m_ptCaretPos.y, DIFFSTATE_EDITED);
2722 m_ptCaretPos.x++;
2723 UpdateGoalPos();
2725 else if (nChar == VK_RETURN)
2727 // insert a new, fresh and empty line below the cursor
2728 RemoveSelectedText();
2729 AddUndoLine(m_ptCaretPos.y, true);
2730 // move the cursor to the new line
2731 m_ptCaretPos.y++;
2732 m_ptCaretPos.x = 0;
2733 UpdateGoalPos();
2735 else
2736 return; // Unknown control character -- ignore it.
2737 ClearSelection();
2738 EnsureCaretVisible();
2739 UpdateCaret();
2740 SetModified(true);
2741 Invalidate(FALSE);
2744 void CBaseView::AddUndoLine(int nLine, bool bAddEmptyLine)
2746 viewstate leftstate;
2747 viewstate rightstate;
2748 viewstate bottomstate;
2749 leftstate.AddLineFormView(m_pwndLeft, nLine, bAddEmptyLine);
2750 rightstate.AddLineFormView(m_pwndRight, nLine, bAddEmptyLine);
2751 bottomstate.AddLineFormView(m_pwndBottom, nLine, bAddEmptyLine);
2752 CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);
2755 void CBaseView::AddEmptyLine(int nLineIndex)
2757 if (m_pViewData == NULL)
2758 return;
2759 if (!m_bCaretHidden)
2761 CString sPartLine = GetLineChars(nLineIndex);
2762 m_pViewData->SetLine(nLineIndex, sPartLine.Left(m_ptCaretPos.x));
2763 sPartLine = sPartLine.Mid(m_ptCaretPos.x);
2764 m_pViewData->InsertData(nLineIndex+1, sPartLine, DIFFSTATE_EDITED, -1, m_pViewData->GetLineEnding(nLineIndex));
2766 else
2767 m_pViewData->InsertData(nLineIndex+1, _T(""), DIFFSTATE_EDITED, -1, m_pViewData->GetLineEnding(nLineIndex));
2768 Invalidate(FALSE);
2771 void CBaseView::RemoveLine(int nLineIndex)
2773 if (m_pViewData == NULL)
2774 return;
2775 m_pViewData->RemoveData(nLineIndex);
2776 if (m_ptCaretPos.y >= GetLineCount())
2777 m_ptCaretPos.y = GetLineCount()-1;
2778 Invalidate(FALSE);
2781 void CBaseView::RemoveSelectedText()
2783 if (m_pViewData == NULL)
2784 return;
2785 if (!HasTextSelection())
2786 return;
2788 viewstate rightstate;
2789 viewstate bottomstate;
2790 viewstate leftstate;
2791 std::vector<LONG> linestoremove;
2792 for (LONG i = m_ptSelectionStartPos.y; i <= m_ptSelectionEndPos.y; ++i)
2794 if (i == m_ptSelectionStartPos.y)
2796 CString sLine = GetLineChars(m_ptSelectionStartPos.y);
2797 CString newLine;
2798 if (i == m_ptSelectionStartPos.y)
2800 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
2802 leftstate.difflines[i] = m_pwndLeft->m_pViewData->GetLine(i);
2803 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);
2805 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
2807 rightstate.difflines[i] = m_pwndRight->m_pViewData->GetLine(i);
2808 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
2810 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
2812 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
2813 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
2815 newLine = sLine.Left(m_ptSelectionStartPos.x);
2816 sLine = GetLineChars(m_ptSelectionEndPos.y);
2817 newLine = newLine + sLine.Mid(m_ptSelectionEndPos.x);
2819 m_pViewData->SetLine(i, newLine);
2820 m_pViewData->SetState(i, DIFFSTATE_EDITED);
2821 SetModified();
2823 else
2825 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
2827 leftstate.removedlines[i] = m_pwndLeft->m_pViewData->GetData(i);
2829 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
2831 rightstate.removedlines[i] = m_pwndRight->m_pViewData->GetData(i);
2833 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
2835 bottomstate.removedlines[i] = m_pwndBottom->m_pViewData->GetData(i);
2837 linestoremove.push_back(i);
2840 CUndo::GetInstance().AddState(leftstate, rightstate, bottomstate, m_ptCaretPos);
2841 // remove the lines at the end, to avoid problems with line indexes
2842 if (linestoremove.size())
2844 std::vector<LONG>::const_iterator it = linestoremove.begin();
2845 int nLineToRemove = *it;
2846 for ( ; it != linestoremove.end(); ++it)
2848 if (m_pwndLeft)
2849 m_pwndLeft->RemoveLine(nLineToRemove);
2850 if (m_pwndRight)
2851 m_pwndRight->RemoveLine(nLineToRemove);
2852 if (m_pwndBottom)
2853 m_pwndBottom->RemoveLine(nLineToRemove);
2854 SetModified();
2857 m_ptCaretPos = m_ptSelectionStartPos;
2858 UpdateGoalPos();
2859 ClearSelection();
2860 UpdateCaret();
2861 EnsureCaretVisible();
2862 Invalidate(FALSE);
2865 void CBaseView::PasteText()
2867 if (!OpenClipboard())
2868 return;
2870 CString sClipboardText;
2871 HGLOBAL hglb = GetClipboardData(CF_TEXT);
2872 if (hglb)
2874 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
2875 sClipboardText = CString(lpstr);
2876 GlobalUnlock(hglb);
2878 hglb = GetClipboardData(CF_UNICODETEXT);
2879 if (hglb)
2881 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
2882 sClipboardText = lpstr;
2883 GlobalUnlock(hglb);
2885 CloseClipboard();
2887 if (sClipboardText.IsEmpty())
2888 return;
2890 sClipboardText.Replace(_T("\r\n"), _T("\r"));
2891 sClipboardText.Replace('\n', '\r');
2892 // We want to undo the insertion in a single step.
2893 CUndo::GetInstance().BeginGrouping();
2894 // use the easy way to insert text:
2895 // insert char by char, using the OnChar() method
2896 for (int i=0; i<sClipboardText.GetLength(); ++i)
2898 OnChar(sClipboardText[i], 0, 0);
2900 CUndo::GetInstance().EndGrouping();
2903 void CBaseView::OnCaretDown()
2905 m_ptCaretPos.y++;
2906 m_ptCaretPos.y = min(m_ptCaretPos.y, GetLineCount()-1);
2907 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2908 if (GetKeyState(VK_SHIFT)&0x8000)
2909 AdjustSelection();
2910 else
2911 ClearSelection();
2912 UpdateCaret();
2913 EnsureCaretVisible();
2914 ShowDiffLines(m_ptCaretPos.y);
2917 bool CBaseView::MoveCaretLeft()
2919 if (m_ptCaretPos.x == 0)
2921 if (m_ptCaretPos.y > 0)
2923 --m_ptCaretPos.y;
2924 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
2926 else
2927 return false;
2929 else
2930 --m_ptCaretPos.x;
2932 UpdateGoalPos();
2933 return true;
2936 bool CBaseView::MoveCaretRight()
2938 if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))
2940 if (m_ptCaretPos.y < (GetLineCount() - 1))
2942 ++m_ptCaretPos.y;
2943 m_ptCaretPos.x = 0;
2945 else
2946 return false;
2948 else
2949 ++m_ptCaretPos.x;
2951 UpdateGoalPos();
2952 return true;
2955 void CBaseView::UpdateGoalPos()
2957 m_nCaretGoalPos = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
2960 void CBaseView::OnCaretLeft()
2962 MoveCaretLeft();
2963 if (GetKeyState(VK_SHIFT)&0x8000)
2964 AdjustSelection();
2965 else
2966 ClearSelection();
2967 EnsureCaretVisible();
2968 UpdateCaret();
2971 void CBaseView::OnCaretRight()
2973 MoveCaretRight();
2974 if (GetKeyState(VK_SHIFT)&0x8000)
2975 AdjustSelection();
2976 else
2977 ClearSelection();
2978 EnsureCaretVisible();
2979 UpdateCaret();
2982 void CBaseView::OnCaretUp()
2984 m_ptCaretPos.y--;
2985 m_ptCaretPos.y = max(0, m_ptCaretPos.y);
2986 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nCaretGoalPos);
2987 if (GetKeyState(VK_SHIFT)&0x8000)
2988 AdjustSelection();
2989 else
2990 ClearSelection();
2991 UpdateCaret();
2992 EnsureCaretVisible();
2993 ShowDiffLines(m_ptCaretPos.y);
2996 bool CBaseView::IsWordSeparator(wchar_t ch) const
2998 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
3001 bool CBaseView::IsCaretAtWordBoundary() const
3003 LPCTSTR line = GetLineChars(m_ptCaretPos.y);
3004 if (!*line)
3005 return false; // no boundary at the empty lines
3006 if (m_ptCaretPos.x == 0)
3007 return !IsWordSeparator(line[m_ptCaretPos.x]);
3008 if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))
3009 return !IsWordSeparator(line[m_ptCaretPos.x - 1]);
3010 return
3011 IsWordSeparator(line[m_ptCaretPos.x]) !=
3012 IsWordSeparator(line[m_ptCaretPos.x - 1]);
3015 void CBaseView::OnCaretWordleft()
3017 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
3020 if (GetKeyState(VK_SHIFT)&0x8000)
3021 AdjustSelection();
3022 else
3023 ClearSelection();
3024 EnsureCaretVisible();
3025 UpdateCaret();
3028 void CBaseView::OnCaretWordright()
3030 while (MoveCaretRight() && !IsCaretAtWordBoundary())
3033 if (GetKeyState(VK_SHIFT)&0x8000)
3034 AdjustSelection();
3035 else
3036 ClearSelection();
3037 EnsureCaretVisible();
3038 UpdateCaret();
3041 void CBaseView::ClearCurrentSelection()
3043 m_ptSelectionStartPos = m_ptCaretPos;
3044 m_ptSelectionEndPos = m_ptCaretPos;
3045 m_ptSelectionOrigin = m_ptCaretPos;
3046 m_nSelBlockStart = -1;
3047 m_nSelBlockEnd = -1;
3048 Invalidate(FALSE);
3051 void CBaseView::ClearSelection()
3053 if (m_pwndLeft)
3054 m_pwndLeft->ClearCurrentSelection();
3055 if (m_pwndRight)
3056 m_pwndRight->ClearCurrentSelection();
3057 if (m_pwndBottom)
3058 m_pwndBottom->ClearCurrentSelection();
3061 void CBaseView::AdjustSelection()
3063 if ((m_ptCaretPos.y < m_ptSelectionOrigin.y) ||
3064 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x <= m_ptSelectionOrigin.x))
3066 m_ptSelectionStartPos = m_ptCaretPos;
3067 m_ptSelectionEndPos = m_ptSelectionOrigin;
3070 if ((m_ptCaretPos.y > m_ptSelectionOrigin.y) ||
3071 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x >= m_ptSelectionOrigin.x))
3073 m_ptSelectionStartPos = m_ptSelectionOrigin;
3074 m_ptSelectionEndPos = m_ptCaretPos;
3077 SetupSelection(min(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y), max(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y));
3079 Invalidate(FALSE);
3082 void CBaseView::OnEditCut()
3084 if (!m_bCaretHidden)
3086 OnEditCopy();
3087 RemoveSelectedText();
3091 void CBaseView::OnEditPaste()
3093 if (!m_bCaretHidden)
3095 PasteText();