1 // TortoiseMerge - a Diff/Patch program
\r
3 // Copyright (C) 2003-2008 - TortoiseSVN
\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
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
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
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
31 #define new DEBUG_NEW
\r
34 #define MARGINWIDTH 20
\r
35 #define HEADERHEIGHT 10
\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
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
51 IMPLEMENT_DYNCREATE(CBaseView, CView)
\r
53 CBaseView::CBaseView()
\r
55 m_pCacheBitmap = NULL;
\r
57 m_pOtherViewData = NULL;
\r
60 m_nScreenChars = -1;
\r
61 m_nMaxLineLength = -1;
\r
62 m_nScreenLines = -1;
\r
67 m_bMouseWithin = FALSE;
\r
68 m_bIsHidden = FALSE;
\r
69 lineendings = EOL_AUTOLINE;
\r
70 m_bCaretHidden = true;
\r
73 m_nCaretGoalPos = 0;
\r
74 m_ptSelectionStartPos = m_ptCaretPos;
\r
75 m_ptSelectionEndPos = m_ptCaretPos;
\r
76 m_ptSelectionOrigin = m_ptCaretPos;
\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
97 m_apFonts[i] = NULL;
\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
125 CBaseView::~CBaseView()
\r
127 if (m_pCacheBitmap)
\r
129 m_pCacheBitmap->DeleteObject();
\r
130 delete m_pCacheBitmap;
\r
132 for (int i=0; i<MAXFONTS; i++)
\r
134 if (m_apFonts[i] != NULL)
\r
136 m_apFonts[i]->DeleteObject();
\r
137 delete m_apFonts[i];
\r
139 m_apFonts[i] = NULL;
\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
153 BEGIN_MESSAGE_MAP(CBaseView, CView)
\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
170 ON_WM_LBUTTONDOWN()
\r
171 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
\r
173 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
\r
174 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
\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
188 void CBaseView::DocumentUpdated()
\r
190 if (m_pCacheBitmap != NULL)
\r
192 m_pCacheBitmap->DeleteObject();
\r
193 delete m_pCacheBitmap;
\r
194 m_pCacheBitmap = NULL;
\r
196 m_nLineHeight = -1;
\r
198 m_nScreenChars = -1;
\r
199 m_nMaxLineLength = -1;
\r
200 m_nScreenLines = -1;
\r
202 m_bModified = FALSE;
\r
203 m_bOtherDiffChecked = false;
\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
217 if (m_apFonts[i] != NULL)
\r
219 m_apFonts[i]->DeleteObject();
\r
220 delete m_apFonts[i];
\r
222 m_apFonts[i] = NULL;
\r
224 m_nSelBlockStart = -1;
\r
225 m_nSelBlockEnd = -1;
\r
226 RecalcVertScrollBar();
\r
227 RecalcHorzScrollBar();
\r
232 void CBaseView::UpdateStatusBar()
\r
234 int nRemovedLines = 0;
\r
235 int nAddedLines = 0;
\r
236 int nConflictedLines = 0;
\r
240 for (int i=0; i<m_pViewData->GetCount(); i++)
\r
242 DiffStates state = m_pViewData->GetState(i);
\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
252 case DIFFSTATE_IDENTICALREMOVED:
\r
253 case DIFFSTATE_REMOVED:
\r
254 case DIFFSTATE_THEIRSREMOVED:
\r
255 case DIFFSTATE_YOURSREMOVED:
\r
258 case DIFFSTATE_CONFLICTED:
\r
259 case DIFFSTATE_CONFLICTED_IGNORED:
\r
260 nConflictedLines++;
\r
271 case CFileTextLines::ASCII:
\r
272 sBarText = _T("ASCII ");
\r
274 case CFileTextLines::BINARY:
\r
275 sBarText = _T("BINARY ");
\r
277 case CFileTextLines::UNICODE_LE:
\r
278 sBarText = _T("UTF-16LE ");
\r
280 case CFileTextLines::UTF8:
\r
281 sBarText = _T("UTF8 ");
\r
283 case CFileTextLines::UTF8BOM:
\r
284 sBarText = _T("UTF8 BOM ");
\r
288 switch(lineendings)
\r
291 sBarText += _T("LF ");
\r
294 sBarText += _T("CRLF ");
\r
297 sBarText += _T("LFCR ");
\r
300 sBarText += _T("CR ");
\r
304 if (sBarText.IsEmpty())
\r
305 sBarText += _T(" / ");
\r
309 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
\r
310 if (!sBarText.IsEmpty())
\r
311 sBarText += _T(" / ");
\r
316 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
\r
317 if (!sBarText.IsEmpty())
\r
318 sBarText += _T(" / ");
\r
321 if (nConflictedLines)
\r
323 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
\r
324 if (!sBarText.IsEmpty())
\r
325 sBarText += _T(" / ");
\r
328 if (m_pwndStatusBar)
\r
333 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
\r
334 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
\r
336 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
\r
338 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
\r
340 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
\r
341 sBarText = sTemp+sBarText;
\r
343 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
\r
345 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
\r
346 sBarText = sTemp+sBarText;
\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
353 CSize size = pDC->GetTextExtent(sBarText);
\r
354 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
\r
357 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
\r
361 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
\r
363 if (!CView::PreCreateWindow(cs))
\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
371 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
\r
372 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
\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
378 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
\r
382 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/, BOOL bStrikeOut /*= FALSE*/)
\r
391 if (m_apFonts[nIndex] == NULL)
\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
399 m_lfBaseFont.lfStrikeOut = (BYTE)(DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\StrikeOut"), TRUE);
\r
400 CDC * pDC = GetDC();
\r
403 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
\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
409 delete m_apFonts[nIndex];
\r
410 m_apFonts[nIndex] = NULL;
\r
411 return CView::GetFont();
\r
414 return m_apFonts[nIndex];
\r
417 void CBaseView::CalcLineCharDim()
\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
428 pDC->SelectObject(pOldFont);
\r
432 int CBaseView::GetScreenChars()
\r
434 if (m_nScreenChars == -1)
\r
437 GetClientRect(&rect);
\r
438 m_nScreenChars = (rect.Width() - GetMarginWidth()) / GetCharWidth();
\r
440 return m_nScreenChars;
\r
443 int CBaseView::GetAllMinScreenChars() const
\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
455 int CBaseView::GetAllMaxLineLength() const
\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
467 int CBaseView::GetLineHeight()
\r
469 if (m_nLineHeight == -1)
\r
471 if (m_nLineHeight <= 0)
\r
473 return m_nLineHeight;
\r
476 int CBaseView::GetCharWidth()
\r
478 if (m_nCharWidth == -1)
\r
480 if (m_nCharWidth <= 0)
\r
482 return m_nCharWidth;
\r
485 int CBaseView::GetMaxLineLength()
\r
487 if (m_nMaxLineLength == -1)
\r
489 m_nMaxLineLength = 0;
\r
490 int nLineCount = GetLineCount();
\r
491 for (int i=0; i<nLineCount; i++)
\r
493 int nActualLength = GetLineActualLength(i);
\r
494 if (m_nMaxLineLength < nActualLength)
\r
495 m_nMaxLineLength = nActualLength;
\r
498 return m_nMaxLineLength;
\r
501 int CBaseView::GetLineActualLength(int index) const
\r
503 if (m_pViewData == NULL)
\r
506 return CalculateActualOffset(index, GetLineLength(index));
\r
509 int CBaseView::GetLineLength(int index) const
\r
511 if (m_pViewData == NULL)
\r
513 if (m_pViewData->GetCount() == 0)
\r
515 int nLineLength = m_pViewData->GetLine(index).GetLength();
\r
516 ASSERT(nLineLength >= 0);
\r
517 return nLineLength;
\r
520 int CBaseView::GetLineCount() const
\r
522 if (m_pViewData == NULL)
\r
524 int nLineCount = m_pViewData->GetCount();
\r
525 ASSERT(nLineCount >= 0);
\r
529 LPCTSTR CBaseView::GetLineChars(int index) const
\r
531 if (m_pViewData == NULL)
\r
533 if (m_pViewData->GetCount() == 0)
\r
535 return m_pViewData->GetLine(index);
\r
538 void CBaseView::CheckOtherView()
\r
540 if (m_bOtherDiffChecked)
\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
547 if (this == m_pwndRight && IsLeftViewGood())
\r
548 m_pOtherViewData = m_pwndLeft->m_pViewData;
\r
550 m_bOtherDiffChecked = true;
\r
553 CString CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex)
\r
555 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
\r
558 DiffStates origstate = viewData->GetState(nLineIndex);
\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
565 DiffStates state = viewData->GetState(nStartBlock - 1);
\r
566 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
\r
568 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
\r
573 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
\r
575 DiffStates state = viewData->GetState(nEndBlock + 1);
\r
576 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
\r
578 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
\r
585 for (int i = nStartBlock; i <= nEndBlock; ++i)
\r
586 block += viewData->GetLine(i);
\r
590 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical)
\r
592 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
\r
594 if (!m_pOtherViewData)
\r
597 (m_pViewData->GetState(nLineIndex) == DIFFSTATE_NORMAL) &&
\r
598 (m_pOtherViewData->GetLine(nLineIndex) == m_pViewData->GetLine(nLineIndex))
\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
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
615 return (mine == other) && (!mine.IsEmpty());
\r
618 int CBaseView::GetLineNumber(int index) const
\r
620 if (m_pViewData == NULL)
\r
622 if (m_pViewData->GetLineNumber(index)==DIFF_EMPTYLINENUMBER)
\r
624 return m_pViewData->GetLineNumber(index);
\r
627 int CBaseView::GetScreenLines()
\r
629 if (m_nScreenLines == -1)
\r
632 sbi.cbSize = sizeof(sbi);
\r
633 GetScrollBarInfo(OBJID_HSCROLL, &sbi);
\r
634 int scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
\r
637 GetClientRect(&rect);
\r
638 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
\r
640 return m_nScreenLines;
\r
643 int CBaseView::GetAllMinScreenLines() const
\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
655 int CBaseView::GetAllLineCount() const
\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
667 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
\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
677 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
\r
680 si.cbSize = sizeof(si);
\r
683 si.fMask = SIF_POS;
\r
684 si.nPos = m_nTopLine;
\r
688 EnableScrollBarCtrl(SB_VERT, TRUE);
\r
689 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
\r
694 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
\r
696 si.nMax = GetAllLineCount();
\r
697 si.nPage = GetAllMinScreenLines();
\r
698 si.nPos = m_nTopLine;
\r
700 VERIFY(SetScrollInfo(SB_VERT, &si));
\r
703 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
\r
705 CView::OnVScroll(nSBCode, nPos, pScrollBar);
\r
707 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
\r
709 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
\r
711 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
\r
713 m_pwndLocator->Invalidate();
\r
716 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
\r
718 // Note we cannot use nPos because of its 16-bit nature
\r
720 si.cbSize = sizeof(si);
\r
721 si.fMask = SIF_ALL;
\r
722 VERIFY(master->GetScrollInfo(SB_VERT, &si));
\r
724 int nPageLines = GetScreenLines();
\r
725 int nLineCount = GetLineCount();
\r
731 static LONG textwidth = 0;
\r
732 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
\r
739 nNewTopLine = nLineCount - nPageLines + 1;
\r
742 nNewTopLine = m_nTopLine - 1;
\r
745 nNewTopLine = m_nTopLine + 1;
\r
748 nNewTopLine = m_nTopLine - si.nPage + 1;
\r
751 nNewTopLine = m_nTopLine + si.nPage - 1;
\r
753 case SB_THUMBPOSITION:
\r
754 m_ScrollTool.Clear();
\r
755 nNewTopLine = si.nTrackPos;
\r
758 case SB_THUMBTRACK:
\r
759 nNewTopLine = si.nTrackPos;
\r
760 if (GetFocus() == this)
\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
769 CString sTemp = sFormat;
\r
770 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
\r
771 textwidth = m_ScrollTool.GetTextWidth(sTemp);
\r
773 thumbpoint.x -= textwidth;
\r
774 int line = GetLineNumber(nNewTopLine);
\r
776 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
\r
778 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
\r
785 if (nNewTopLine < 0)
\r
787 if (nNewTopLine >= nLineCount)
\r
788 nNewTopLine = nLineCount - 1;
\r
789 ScrollToLine(nNewTopLine);
\r
792 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
\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
802 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
\r
805 si.cbSize = sizeof(si);
\r
808 si.fMask = SIF_POS;
\r
809 si.nPos = m_nOffsetChar;
\r
813 EnableScrollBarCtrl(SB_HORZ, TRUE);
\r
814 if (GetAllMinScreenChars() >= GetAllMaxLineLength() && m_nOffsetChar > 0)
\r
819 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
\r
821 si.nMax = GetAllMaxLineLength() + GetMarginWidth()/GetCharWidth();
\r
822 si.nPage = GetAllMinScreenChars();
\r
823 si.nPos = m_nOffsetChar;
\r
825 VERIFY(SetScrollInfo(SB_HORZ, &si));
\r
828 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
\r
830 CView::OnHScroll(nSBCode, nPos, pScrollBar);
\r
832 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
\r
834 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
\r
836 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
\r
838 m_pwndLocator->Invalidate();
\r
841 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
\r
844 si.cbSize = sizeof(si);
\r
845 si.fMask = SIF_ALL;
\r
846 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
\r
848 int nPageChars = GetScreenChars();
\r
849 int nMaxLineLength = GetMaxLineLength();
\r
858 nNewOffset = nMaxLineLength - nPageChars + 1;
\r
861 nNewOffset = m_nOffsetChar - 1;
\r
864 nNewOffset = m_nOffsetChar + 1;
\r
867 nNewOffset = m_nOffsetChar - si.nPage + 1;
\r
870 nNewOffset = m_nOffsetChar + si.nPage - 1;
\r
872 case SB_THUMBPOSITION:
\r
873 case SB_THUMBTRACK:
\r
874 nNewOffset = si.nTrackPos;
\r
880 if (nNewOffset >= nMaxLineLength)
\r
881 nNewOffset = nMaxLineLength - 1;
\r
882 if (nNewOffset < 0)
\r
884 ScrollToChar(nNewOffset, TRUE);
\r
887 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
\r
889 if (m_nOffsetChar != nNewOffsetChar)
\r
891 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
\r
892 m_nOffsetChar = nNewOffsetChar;
\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
901 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
\r
902 InvalidateRect(&rcScroll, FALSE);
\r
904 if (bTrackScrollBar)
\r
905 RecalcHorzScrollBar(TRUE);
\r
910 void CBaseView::ScrollSide(int delta)
\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
919 ScrollToChar(nNewOffset, TRUE);
\r
920 if (m_pwndLineDiffBar)
\r
921 m_pwndLineDiffBar->Invalidate();
\r
925 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
\r
927 if (m_nTopLine != nNewTopLine)
\r
929 if (nNewTopLine < 0)
\r
931 int nScrollLines = m_nTopLine - nNewTopLine;
\r
932 m_nTopLine = nNewTopLine;
\r
934 GetClientRect(&rcScroll);
\r
935 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
\r
936 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
\r
938 if (bTrackScrollBar)
\r
939 RecalcVertScrollBar(TRUE);
\r
945 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
\r
947 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
\r
949 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
\r
951 DiffStates state = m_pViewData->GetState(nLineIndex);
\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
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
968 case DIFFSTATE_CONFLICTED:
\r
969 icon = m_hConflictedIcon;
\r
971 case DIFFSTATE_CONFLICTED_IGNORED:
\r
972 icon = m_hConflictedIgnoredIcon;
\r
974 case DIFFSTATE_EDITED:
\r
975 icon = m_hEditedIcon;
\r
980 bool bIdentical = false;
\r
981 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))
\r
984 icon = m_hEqualIcon;
\r
986 icon = m_hWhitespaceBlockIcon;
\r
991 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
\r
993 if ((m_bViewLinenumbers)&&(m_nDigits))
\r
995 int nLineNumber = GetLineNumber(nLineIndex);
\r
996 if (nLineNumber >= 0)
\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
1005 pdc->SelectObject(GetFont());
\r
1006 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
\r
1012 int CBaseView::GetMarginWidth()
\r
1014 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
\r
1016 int nWidth = GetCharWidth();
\r
1017 if (m_nDigits <= 0)
\r
1019 int nLength = (int)m_pViewData->GetCount();
\r
1020 // find out how many digits are needed to show the highest line number
\r
1022 while (nLength / 10)
\r
1027 m_nDigits = nDigits;
\r
1029 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
\r
1031 return MARGINWIDTH;
\r
1034 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
\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
1042 pdc->SetBkColor(crBk);
\r
1047 if (this == m_pwndRight)
\r
1049 CDiffColors::GetInstance().GetColors(DIFFSTATE_ADDED, crBk, crFg);
\r
1050 pdc->SetBkColor(crBk);
\r
1054 CDiffColors::GetInstance().GetColors(DIFFSTATE_REMOVED, crBk, crFg);
\r
1055 pdc->SetBkColor(crBk);
\r
1058 pdc->FillSolidRect(textrect, crBk);
\r
1060 pdc->SetTextColor(crFg);
\r
1062 pdc->SelectObject(GetFont(FALSE, TRUE, FALSE));
\r
1065 if (m_sWindowName.Left(2).Compare(_T("* "))!=0)
\r
1066 m_sWindowName = _T("* ") + m_sWindowName;
\r
1070 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
\r
1071 m_sWindowName = m_sWindowName.Mid(2);
\r
1073 CString sViewTitle = m_sWindowName;
\r
1074 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
\r
1075 if (nStringLength > rect.Width())
\r
1077 int offset = min(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
\r
1079 sViewTitle = m_sWindowName.Mid(offset);
\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
1086 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
\r
1089 void CBaseView::OnDraw(CDC * pDC)
\r
1092 GetClientRect(rcClient);
\r
1094 int nLineCount = GetLineCount();
\r
1095 int nLineHeight = GetLineHeight();
\r
1098 VERIFY(cacheDC.CreateCompatibleDC(pDC));
\r
1099 if (m_pCacheBitmap == NULL)
\r
1101 m_pCacheBitmap = new CBitmap;
\r
1102 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
\r
1104 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
\r
1106 DrawHeader(pDC, rcClient);
\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
1115 int nCurrentLine = m_nTopLine;
\r
1116 while (rcLine.top < rcClient.bottom)
\r
1118 if (nCurrentLine < nLineCount)
\r
1120 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
\r
1121 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
\r
1125 DrawMargin(&cacheDC, rcCacheMargin, -1);
\r
1126 DrawSingleLine(&cacheDC, rcCacheLine, -1);
\r
1129 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
\r
1132 rcLine.OffsetRect(0, nLineHeight);
\r
1135 cacheDC.SelectObject(pOldBitmap);
\r
1136 cacheDC.DeleteDC();
\r
1139 BOOL CBaseView::IsLineRemoved(int nLineIndex)
\r
1141 DiffStates state = DIFFSTATE_UNKNOWN;
\r
1143 state = m_pViewData->GetState(nLineIndex);
\r
1147 case DIFFSTATE_REMOVED:
\r
1148 case DIFFSTATE_THEIRSREMOVED:
\r
1149 case DIFFSTATE_YOURSREMOVED:
\r
1150 case DIFFSTATE_IDENTICALREMOVED:
\r
1160 bool CBaseView::IsLineConflicted(int nLineIndex)
\r
1162 DiffStates state = DIFFSTATE_UNKNOWN;
\r
1164 state = m_pViewData->GetState(nLineIndex);
\r
1168 case DIFFSTATE_CONFLICTED:
\r
1169 case DIFFSTATE_CONFLICTED_IGNORED:
\r
1170 case DIFFSTATE_CONFLICTEMPTY:
\r
1171 case DIFFSTATE_CONFLICTADDED:
\r
1181 COLORREF CBaseView::IntenseColor(long scale, COLORREF col)
\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
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
1192 return RGB(red, green, blue);
\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
1198 return RGB(R, G, B);
\r
1201 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
\r
1203 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
\r
1206 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
\r
1208 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < m_pViewData->GetCount())))
\r
1211 EOL ending = m_pViewData->GetLineEnding(nLineIndex);
\r
1214 HICON hEndingIcon = NULL;
\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
1222 if (origin.x < (rc.left-GetCharWidth()))
\r
1224 // If EOL style has changed, color end-of-line markers as inline differences.
\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
1233 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
\r
1236 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
\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
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
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
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
1272 pDC->SelectObject(oldpen);
\r
1276 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
\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
1282 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
\r
1284 if ((nLineIndex == m_nSelBlockEnd) && m_bShowSelection)
\r
1286 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
\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
1293 ASSERT(m_pViewData && (nLineIndex < m_pViewData->GetCount()));
\r
1294 DiffStates diffState = m_pViewData->GetState(nLineIndex);
\r
1296 // first suppose the whole line is selected
\r
1297 int selectedStart = 0, selectedEnd = textlength;
\r
1299 if ((m_ptSelectionStartPos.y > nLineIndex) || (m_ptSelectionEndPos.y < nLineIndex)
\r
1300 || ! m_bShowSelection)
\r
1302 // this line has no selected text
\r
1303 selectedStart = textlength;
\r
1305 else if ((m_ptSelectionStartPos.y == nLineIndex) || (m_ptSelectionEndPos.y == nLineIndex))
\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
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
1316 if (m_ptSelectionEndPos.y == nLineIndex)
\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
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
1329 crBkgnd = InlineDiffColor(nLineIndex);
\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
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
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
1351 bool CBaseView::DrawInlineDiff(CDC *pDC, const CRect &rc, int nLineIndex, const CString &line, CPoint &origin)
\r
1353 if (!m_bShowInlineDiff || line.IsEmpty())
\r
1355 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
\r
1358 LPCTSTR pszDiffChars = NULL;
\r
1359 int nDiffLength = 0;
\r
1360 if (m_pOtherViewData)
\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
1367 if (!pszDiffChars || !*pszDiffChars)
\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
1377 int lineoffset = 0;
\r
1378 std::deque<int> removedPositions;
\r
1382 apr_off_t len = diff->original_length;
\r
1385 for (int i = 0; i < len; ++i)
\r
1387 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
\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
1394 if (isModified && (len < diff->modified_length))
\r
1395 removedPositions.push_back(origin.x - 1);
\r
1397 diff = diff->next;
\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
1406 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
\r
1408 if (nLineIndex >= GetLineCount())
\r
1410 ASSERT(nLineIndex >= -1);
\r
1412 if ((nLineIndex == -1) || !m_pViewData)
\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
1421 DiffStates diffState = m_pViewData->GetState(nLineIndex);
\r
1422 COLORREF crBkgnd, crText;
\r
1423 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
\r
1425 if (diffState == DIFFSTATE_CONFLICTED)
\r
1427 // conflicted lines are shown without 'text' on them
\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
1437 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
\r
1438 int nLength = GetLineLength(nLineIndex);
\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
1447 LPCTSTR pszChars = GetLineChars(nLineIndex);
\r
1448 if (pszChars == NULL)
\r
1455 pDC->SelectObject(GetFont(FALSE, FALSE, IsLineRemoved(nLineIndex)));
\r
1457 ExpandChars(pszChars, 0, nLength, line);
\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
1465 if (!bInlineDiffDrawn)
\r
1467 int nCount = min(line.GetLength(), nWidth / GetCharWidth() + 1);
\r
1468 DrawText(pDC, rc, line, nCount, nLineIndex, origin, false, false);
\r
1471 origin.x = savedx + pDC->GetTextExtent(line).cx;
\r
1473 // draw white space after the end of line
\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
1485 int y = rc.top + (rc.bottom-rc.top)/2;
\r
1487 int nActualOffset = 0;
\r
1488 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
\r
1490 if (*pszChars == _T('\t'))
\r
1491 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
\r
1496 if (nActualOffset > m_nOffsetChar)
\r
1499 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
\r
1500 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
\r
1503 switch (*pszChars)
\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
1516 pDC->SelectObject(oldPen);
\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
1526 pDC->SelectObject(oldPen);
\r
1536 DrawBlockLine(pDC, rc, nLineIndex);
\r
1537 DrawLineEnding(pDC, rc, nLineIndex, origin);
\r
1540 void CBaseView::ExpandChars(LPCTSTR pszChars, int nOffset, int nCount, CString &line)
\r
1548 int nTabSize = GetTabSize();
\r
1550 int nActualOffset = 0;
\r
1551 for (int i=0; i<nOffset; i++)
\r
1553 if (pszChars[i] == _T('\t'))
\r
1554 nActualOffset += (nTabSize - nActualOffset % nTabSize);
\r
1559 pszChars += nOffset;
\r
1560 int nLength = nCount;
\r
1562 int nTabCount = 0;
\r
1563 for (int i=0; i<nLength; i++)
\r
1565 if (pszChars[i] == _T('\t'))
\r
1569 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
\r
1571 if (nTabCount > 0 || m_bViewWhitespace)
\r
1573 for (int i=0; i<nLength; i++)
\r
1575 if (pszChars[i] == _T('\t'))
\r
1577 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
\r
1578 while (nSpaces > 0)
\r
1580 pszBuf[nCurPos ++] = _T(' ');
\r
1586 pszBuf[nCurPos] = pszChars[i];
\r
1593 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
\r
1594 nCurPos = nLength;
\r
1596 pszBuf[nCurPos] = 0;
\r
1597 line.ReleaseBuffer();
\r
1600 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
\r
1602 if ((m_pwndLeft)&&(m_pwndRight))
\r
1604 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
\r
1605 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
\r
1610 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
\r
1612 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
\r
1615 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
\r
1616 if (m_pwndLocator)
\r
1617 m_pwndLocator->Invalidate();
\r
1620 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
\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
1629 if (nNewTopLine >= m_pViewData->GetCount())
\r
1630 nNewTopLine = m_pViewData->GetCount()-1;
\r
1632 ScrollAllToLine(nNewTopLine);
\r
1634 ScrollToLine(nNewTopLine);
\r
1638 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
\r
1643 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
\r
1645 if (CView::OnCreate(lpCreateStruct) == -1)
\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
1663 void CBaseView::OnDestroy()
\r
1665 CView::OnDestroy();
\r
1666 for (int i=0; i<MAXFONTS; i++)
\r
1668 if (m_apFonts[i] != NULL)
\r
1670 m_apFonts[i]->DeleteObject();
\r
1671 delete m_apFonts[i];
\r
1672 m_apFonts[i] = NULL;
\r
1675 if (m_pCacheBitmap != NULL)
\r
1677 delete m_pCacheBitmap;
\r
1678 m_pCacheBitmap = NULL;
\r
1682 void CBaseView::OnSize(UINT nType, int cx, int cy)
\r
1684 if (m_pCacheBitmap != NULL)
\r
1686 m_pCacheBitmap->DeleteObject();
\r
1687 delete m_pCacheBitmap;
\r
1688 m_pCacheBitmap = NULL;
\r
1690 // make sure the view header is redrawn
\r
1692 GetClientRect(&rcScroll);
\r
1693 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
\r
1694 InvalidateRect(&rcScroll, FALSE);
\r
1696 m_nScreenLines = -1;
\r
1697 m_nScreenChars = -1;
\r
1698 RecalcVertScrollBar();
\r
1699 RecalcHorzScrollBar();
\r
1700 CView::OnSize(nType, cx, cy);
\r
1703 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
\r
1706 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
\r
1708 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
\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
1716 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
\r
1718 if (GetKeyState(VK_CONTROL)&0x8000)
\r
1720 // Ctrl-Wheel scrolls sideways
\r
1721 ScrollSide(-zDelta/30);
\r
1725 int nLineCount = GetLineCount();
\r
1726 int nTopLine = m_nTopLine;
\r
1727 nTopLine -= (zDelta/30);
\r
1730 if (nTopLine >= nLineCount)
\r
1731 nTopLine = nLineCount - 1;
\r
1732 ScrollToLine(nTopLine, TRUE);
\r
1736 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
\r
1738 if (nHitTest == HTCLIENT)
\r
1740 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
\r
1743 return CView::OnSetCursor(pWnd, nHitTest, message);
\r
1746 void CBaseView::OnKillFocus(CWnd* pNewWnd)
\r
1748 CView::OnKillFocus(pNewWnd);
\r
1749 m_bFocused = FALSE;
\r
1754 void CBaseView::OnSetFocus(CWnd* pOldWnd)
\r
1756 CView::OnSetFocus(pOldWnd);
\r
1757 m_bFocused = TRUE;
\r
1762 int CBaseView::GetLineFromPoint(CPoint point)
\r
1764 ScreenToClient(&point);
\r
1765 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
\r
1768 bool CBaseView::OnContextMenu(CPoint /*point*/, int /*nLine*/, DiffStates /*state*/)
\r
1773 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
\r
1775 int nLine = GetLineFromPoint(point);
\r
1779 if (m_nSelBlockEnd >= GetLineCount())
\r
1780 m_nSelBlockEnd = GetLineCount()-1;
\r
1781 if ((nLine <= m_pViewData->GetCount())&&(nLine > m_nTopLine))
\r
1783 int nIndex = nLine - 1;
\r
1784 DiffStates state = m_pViewData->GetState(nIndex);
\r
1785 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
\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
1792 while (nIndex >= 0)
\r
1799 if (state != m_pViewData->GetState(--nIndex))
\r
1802 m_nSelBlockStart = nIndex+1;
\r
1803 while (nIndex < (m_pViewData->GetCount()-1))
\r
1805 if (state != m_pViewData->GetState(++nIndex))
\r
1808 if ((nIndex == (m_pViewData->GetCount()-1))&&(state == m_pViewData->GetState(nIndex)))
\r
1809 m_nSelBlockEnd = nIndex;
\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
1818 if (((state == DIFFSTATE_NORMAL)||(state == DIFFSTATE_UNKNOWN)) &&
\r
1819 (m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))
\r
1821 // find a more 'relevant' state in the selection
\r
1822 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; ++i)
\r
1824 state = m_pViewData->GetState(i);
\r
1825 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
\r
1829 bool bKeepSelection = OnContextMenu(point, nLine, state);
\r
1830 if (! bKeepSelection)
\r
1836 void CBaseView::RefreshViews()
\r
1840 m_pwndLeft->UpdateStatusBar();
\r
1841 m_pwndLeft->Invalidate();
\r
1845 m_pwndRight->UpdateStatusBar();
\r
1846 m_pwndRight->Invalidate();
\r
1850 m_pwndBottom->UpdateStatusBar();
\r
1851 m_pwndBottom->Invalidate();
\r
1853 if (m_pwndLocator)
\r
1854 m_pwndLocator->Invalidate();
\r
1857 void CBaseView::GoToFirstDifference()
\r
1859 int nCenterPos = 0;
\r
1860 if ((m_pViewData)&&(0 < m_pViewData->GetCount()))
\r
1862 while (nCenterPos < m_pViewData->GetCount())
\r
1864 DiffStates linestate = m_pViewData->GetState(nCenterPos);
\r
1865 if ((linestate != DIFFSTATE_NORMAL) &&
\r
1866 (linestate != DIFFSTATE_UNKNOWN))
\r
1870 if (nCenterPos >= m_pViewData->GetCount())
\r
1871 nCenterPos = m_pViewData->GetCount()-1;
\r
1872 int nTopPos = nCenterPos - (GetScreenLines()/2);
\r
1877 m_pwndLeft->m_ptCaretPos.x = 0;
\r
1878 m_pwndLeft->m_ptCaretPos.y = nCenterPos;
\r
1879 m_pwndLeft->m_nCaretGoalPos = 0;
\r
1883 m_pwndRight->m_ptCaretPos.x = 0;
\r
1884 m_pwndRight->m_ptCaretPos.y = nCenterPos;
\r
1885 m_pwndRight->m_nCaretGoalPos = 0;
\r
1889 m_pwndBottom->m_ptCaretPos.x = 0;
\r
1890 m_pwndBottom->m_ptCaretPos.y = nCenterPos;
\r
1891 m_pwndBottom->m_nCaretGoalPos = 0;
\r
1893 ScrollAllToLine(nTopPos);
\r
1894 RecalcAllVertScrollBars(TRUE);
\r
1898 void CBaseView::HiglightLines(int start, int end /* = -1 */)
\r
1901 m_nSelBlockStart = start;
\r
1904 m_nSelBlockEnd = end;
\r
1905 m_ptCaretPos.x = 0;
\r
1906 m_ptCaretPos.y = start;
\r
1911 void CBaseView::SetupSelection(int start, int end)
\r
1913 if (IsBottomViewGood())
\r
1915 m_pwndBottom->m_nSelBlockStart = start;
\r
1916 m_pwndBottom->m_nSelBlockEnd = end;
\r
1917 m_pwndBottom->Invalidate();
\r
1919 if (IsLeftViewGood())
\r
1921 m_pwndLeft->m_nSelBlockStart = start;
\r
1922 m_pwndLeft->m_nSelBlockEnd = end;
\r
1923 m_pwndLeft->Invalidate();
\r
1925 if (IsRightViewGood())
\r
1927 m_pwndRight->m_nSelBlockStart = start;
\r
1928 m_pwndRight->m_nSelBlockEnd = end;
\r
1929 m_pwndRight->Invalidate();
\r
1933 void CBaseView::OnMergePreviousconflict()
\r
1935 SelectNextBlock(-1, true);
\r
1938 void CBaseView::OnMergeNextconflict()
\r
1940 SelectNextBlock(1, true);
\r
1943 void CBaseView::OnMergeNextdifference()
\r
1945 SelectNextBlock(1, false);
\r
1948 void CBaseView::OnMergePreviousdifference()
\r
1950 SelectNextBlock(-1, false);
\r
1953 void CBaseView::SelectNextBlock(int nDirection, bool bConflict)
\r
1955 if (! m_pViewData)
\r
1958 if (m_pViewData->GetCount() == 0)
\r
1961 int nCenterPos = m_ptCaretPos.y;
\r
1963 if (nDirection > 0)
\r
1964 nLimit = m_pViewData->GetCount() - 1;
\r
1966 if (nCenterPos >= m_pViewData->GetCount())
\r
1967 nCenterPos = m_pViewData->GetCount()-1;
\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
1975 // Find next diff/conflict block
\r
1976 while (nCenterPos != nLimit)
\r
1978 DiffStates linestate = m_pViewData->GetState(nCenterPos);
\r
1980 (linestate != DIFFSTATE_NORMAL) &&
\r
1981 (linestate != DIFFSTATE_UNKNOWN))
\r
1984 ((linestate == DIFFSTATE_CONFLICTADDED) ||
\r
1985 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
\r
1986 (linestate == DIFFSTATE_CONFLICTED) ||
\r
1987 (linestate == DIFFSTATE_CONFLICTEMPTY)))
\r
1990 nCenterPos += nDirection;
\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
2000 int nTopPos = nCenterPos - (GetScreenLines()/2);
\r
2004 m_ptCaretPos.x = 0;
\r
2005 m_ptCaretPos.y = nCenterPos;
\r
2007 if (nDirection > 0)
\r
2008 SetupSelection(nCenterPos, nBlockEnd);
\r
2010 SetupSelection(nBlockEnd, nCenterPos);
\r
2012 ScrollAllToLine(nTopPos, FALSE);
\r
2013 RecalcAllVertScrollBars(TRUE);
\r
2014 m_nCaretGoalPos = 0;
\r
2016 ShowDiffLines(nCenterPos);
\r
2019 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
\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
2029 // idFrom is actually the HWND of the tool
\r
2030 nID = ::GetDlgCtrlID((HWND)nID);
\r
2033 if (pNMHDR->idFrom == (UINT)m_hWnd)
\r
2035 if (m_sWindowName.Left(2).Compare(_T("* "))==0)
\r
2037 strTipText = m_sWindowName.Mid(2) + _T("\r\n") + m_sFullFilePath;
\r
2041 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
\r
2048 if (strTipText.IsEmpty())
\r
2051 if (pNMHDR->code == TTN_NEEDTEXTA)
\r
2053 pTTTA->lpszText = m_szTip;
\r
2054 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
\r
2058 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
\r
2059 pTTTW->lpszText = m_wszTip;
\r
2062 return TRUE; // message was handled
\r
2066 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
\r
2069 GetClientRect(rcClient);
\r
2070 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
\r
2071 if (textrect.PtInRect(point))
\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
2080 // we want multi line tooltips
\r
2081 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
\r
2082 if (pToolTip->GetSafeHwnd() != NULL)
\r
2084 pToolTip->SetMaxTipWidth(INT_MAX);
\r
2092 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
\r
2094 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
\r
2095 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
\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
2104 AdjustSelection();
\r
2108 EnsureCaretVisible();
\r
2109 ShowDiffLines(m_ptCaretPos.y);
\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
2119 AdjustSelection();
\r
2123 EnsureCaretVisible();
\r
2124 ShowDiffLines(m_ptCaretPos.y);
\r
2131 ScrollAllToLine(0);
\r
2132 m_ptCaretPos.x = 0;
\r
2133 m_ptCaretPos.y = 0;
\r
2134 m_nCaretGoalPos = 0;
\r
2136 AdjustSelection();
\r
2143 m_ptCaretPos.x = 0;
\r
2144 m_nCaretGoalPos = 0;
\r
2146 AdjustSelection();
\r
2149 EnsureCaretVisible();
\r
2158 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
\r
2159 m_ptCaretPos.y = GetLineCount()-1;
\r
2160 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
\r
2163 AdjustSelection();
\r
2170 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
\r
2173 AdjustSelection();
\r
2176 EnsureCaretVisible();
\r
2183 if (m_bCaretHidden)
\r
2186 if (! HasTextSelection()) {
\r
2187 if (m_ptCaretPos.y == 0 && m_ptCaretPos.x == 0)
\r
2189 m_ptSelectionEndPos = m_ptCaretPos;
\r
2191 m_ptSelectionStartPos = m_ptCaretPos;
\r
2193 RemoveSelectedText();
\r
2198 if (m_bCaretHidden)
\r
2201 if (! HasTextSelection()) {
\r
2202 if (! MoveCaretRight())
\r
2204 m_ptSelectionEndPos = m_ptCaretPos;
\r
2206 m_ptSelectionStartPos = m_ptCaretPos;
\r
2208 RemoveSelectedText();
\r
2212 CView::OnKeyDown(nChar, nRepCnt, nFlags);
\r
2215 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
\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
2221 m_ptCaretPos.y = nClickedLine;
\r
2222 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
\r
2225 if (nFlags & MK_SHIFT)
\r
2226 AdjustSelection();
\r
2230 SetupSelection(m_ptCaretPos.y, m_ptCaretPos.y);
\r
2238 CView::OnLButtonDown(nFlags, point);
\r
2241 void CBaseView::OnEditCopy()
\r
2243 if ((m_ptSelectionStartPos.x == m_ptSelectionEndPos.x)&&(m_ptSelectionStartPos.y == m_ptSelectionEndPos.y))
\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
2249 switch (m_pViewData->GetState(i))
\r
2251 case DIFFSTATE_EMPTY:
\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
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
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
2290 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
\r
2294 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
\r
2296 int nMouseLine = (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
\r
2297 nMouseLine--; //we need the index
\r
2298 if (nMouseLine < -1)
\r
2302 ShowDiffLines(nMouseLine);
\r
2304 if (nFlags & MK_LBUTTON)
\r
2306 if (((m_nSelBlockStart >= 0)&&(m_nSelBlockEnd >= 0))&&
\r
2307 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
\r
2309 m_ptCaretPos.y = nMouseLine;
\r
2310 m_ptCaretPos.x = CalculateCharIndex(m_ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
\r
2312 AdjustSelection();
\r
2319 if (!m_bMouseWithin)
\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
2329 CView::OnMouseMove(nFlags, point);
\r
2332 void CBaseView::OnMouseLeave()
\r
2334 ShowDiffLines(-1);
\r
2335 m_bMouseWithin = FALSE;
\r
2337 CView::OnMouseLeave();
\r
2340 void CBaseView::SelectLines(int nLine1, int nLine2)
\r
2344 m_nSelBlockStart = nLine1;
\r
2345 m_nSelBlockEnd = nLine2;
\r
2349 void CBaseView::ShowDiffLines(int nLine)
\r
2351 if ((nLine >= m_nTopLine)&&(nLine < GetLineCount()))
\r
2353 if ((m_pwndRight)&&(m_pwndRight->m_pViewData)&&(m_pwndLeft)&&(m_pwndLeft->m_pViewData)&&(!m_pMainFrame->m_bOneWay))
\r
2355 nLine = (nLine > m_pwndRight->m_pViewData->GetCount() ? -1 : nLine);
\r
2356 nLine = (nLine > m_pwndLeft->m_pViewData->GetCount() ? -1 : nLine);
\r
2360 if (nLine != m_nMouseLine)
\r
2362 m_nMouseLine = nLine;
\r
2363 if (nLine >= GetLineCount())
\r
2365 m_pwndLineDiffBar->ShowLines(nLine);
\r
2372 m_pwndLineDiffBar->ShowLines(nLine);
\r
2376 void CBaseView::UseTheirAndYourBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
\r
2378 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
\r
2380 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
\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
2388 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
\r
2389 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
\r
2391 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
\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
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
2403 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
\r
2404 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
\r
2406 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
\r
2410 // adjust line numbers
\r
2411 for (int i=m_nSelBlockEnd+1; i<GetLineCount(); ++i)
\r
2413 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
\r
2415 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
\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
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
2426 RecalcAllVertScrollBars();
\r
2427 m_pwndBottom->SetModified();
\r
2428 m_pwndLeft->SetModified();
\r
2429 m_pwndRight->SetModified();
\r
2432 void CBaseView::UseYourAndTheirBlock(viewstate &rightstate, viewstate &bottomstate, viewstate &leftstate)
\r
2434 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
\r
2436 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
\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
2445 if (m_pwndRight->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
\r
2446 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
\r
2448 m_pwndBottom->m_pViewData->SetState(i, DIFFSTATE_CONFLICTRESOLVED);
\r
2450 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
\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
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
2462 if (m_pwndLeft->m_pViewData->GetState(i) == DIFFSTATE_CONFLICTEMPTY)
\r
2463 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVEDEMPTY);
\r
2465 m_pwndBottom->m_pViewData->SetState(index, DIFFSTATE_CONFLICTRESOLVED);
\r
2467 m_pwndLeft->m_pViewData->SetState(i, DIFFSTATE_THEIRSADDED);
\r
2470 // adjust line numbers
\r
2471 for (int i=m_nSelBlockEnd+1; i<m_pwndBottom->GetLineCount(); ++i)
\r
2473 long oldline = (long)m_pwndBottom->m_pViewData->GetLineNumber(i);
\r
2475 m_pwndBottom->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
\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
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
2487 RecalcAllVertScrollBars();
\r
2488 m_pwndBottom->SetModified();
\r
2489 m_pwndLeft->SetModified();
\r
2490 m_pwndRight->SetModified();
\r
2493 void CBaseView::UseBothRightFirst(viewstate &rightstate, viewstate &leftstate)
\r
2495 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
\r
2497 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
\r
2499 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
\r
2500 m_pwndRight->m_pViewData->SetState(i, DIFFSTATE_YOURSADDED);
\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
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
2511 // adjust line numbers
\r
2513 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
\r
2515 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
\r
2517 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(index-m_nSelBlockEnd));
\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
2523 leftstate.addedlines.push_back(m_nSelBlockStart);
\r
2524 m_pwndLeft->m_pViewData->InsertData(m_nSelBlockStart, _T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING);
\r
2526 RecalcAllVertScrollBars();
\r
2527 m_pwndLeft->SetModified();
\r
2528 m_pwndRight->SetModified();
\r
2531 void CBaseView::UseBothLeftFirst(viewstate &rightstate, viewstate &leftstate)
\r
2533 if ((m_nSelBlockStart == -1)||(m_nSelBlockEnd == -1))
\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
2540 for (int i=m_nSelBlockStart; i<=m_nSelBlockEnd; i++)
\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
2545 // adjust line numbers
\r
2546 for (int i=m_nSelBlockEnd+1; i<m_pwndRight->GetLineCount(); ++i)
\r
2548 long oldline = (long)m_pwndRight->m_pViewData->GetLineNumber(i);
\r
2550 m_pwndRight->m_pViewData->SetLineNumber(i, oldline+(m_nSelBlockEnd-m_nSelBlockStart)+1);
\r
2553 // now insert an empty block in left view
\r
2554 for (int emptyblocks=0; emptyblocks < m_nSelBlockEnd-m_nSelBlockStart+1; ++emptyblocks)
\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
2559 RecalcAllVertScrollBars();
\r
2560 m_pwndLeft->SetModified();
\r
2561 m_pwndRight->SetModified();
\r
2564 void CBaseView::UpdateCaret()
\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
2575 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
\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
2583 CreateSolidCaret(2, GetLineHeight());
\r
2584 SetCaretPos(TextToClient(m_ptCaretPos));
\r
2593 void CBaseView::EnsureCaretVisible()
\r
2595 int nCaretOffset = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
\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
2607 int CBaseView::CalculateActualOffset(int nLineIndex, int nCharIndex) const
\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
2615 int nTabSize = GetTabSize();
\r
2616 for (int I = 0; I < nCharIndex; I ++)
\r
2618 if (pszChars[I] == _T('\t'))
\r
2619 nOffset += (nTabSize - nOffset % nTabSize);
\r
2626 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset) const
\r
2628 int nLength = GetLineLength(nLineIndex);
\r
2629 LPCTSTR pszLine = GetLineChars(nLineIndex);
\r
2632 int nTabSize = GetTabSize();
\r
2633 while (nOffset < nActualOffset && nIndex < nLength)
\r
2635 if (pszLine[nIndex] == _T('\t'))
\r
2636 nOffset += (nTabSize - nOffset % nTabSize);
\r
2644 POINT CBaseView::TextToClient(const POINT& point)
\r
2647 pt.y = max(0, (point.y - m_nTopLine) * GetLineHeight());
\r
2648 pt.x = CalculateActualOffset(point.y, point.x);
\r
2650 pt.x = (pt.x - m_nOffsetChar) * GetCharWidth() + GetMarginWidth();
\r
2651 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
\r
2655 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
\r
2657 CView::OnChar(nChar, nRepCnt, nFlags);
\r
2659 if (m_bCaretHidden)
\r
2662 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
\r
2663 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
\r
2666 if ((nChar > 31)||(nChar == VK_TAB))
\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
2677 else if (nChar == VK_RETURN)
\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
2684 m_ptCaretPos.x = 0;
\r
2688 return; // Unknown control character -- ignore it.
\r
2690 EnsureCaretVisible();
\r
2692 SetModified(true);
\r
2693 Invalidate(FALSE);
\r
2696 void CBaseView::AddUndoLine(int nLine, bool bAddEmptyLine)
\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
2707 void CBaseView::AddEmptyLine(int nLineIndex)
\r
2709 if (m_pViewData == NULL)
\r
2711 if (!m_bCaretHidden)
\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
2719 m_pViewData->InsertData(nLineIndex+1, _T(""), DIFFSTATE_EDITED, -1, m_pViewData->GetLineEnding(nLineIndex));
\r
2720 Invalidate(FALSE);
\r
2723 void CBaseView::RemoveLine(int nLineIndex)
\r
2725 if (m_pViewData == NULL)
\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
2733 void CBaseView::RemoveSelectedText()
\r
2735 if (m_pViewData == NULL)
\r
2737 if (!HasTextSelection())
\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
2746 if (i == m_ptSelectionStartPos.y)
\r
2748 CString sLine = GetLineChars(m_ptSelectionStartPos.y);
\r
2750 if (i == m_ptSelectionStartPos.y)
\r
2752 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
\r
2754 leftstate.difflines[i] = m_pwndLeft->m_pViewData->GetLine(i);
\r
2755 leftstate.linestates[i] = m_pwndLeft->m_pViewData->GetState(i);
\r
2757 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
\r
2759 rightstate.difflines[i] = m_pwndRight->m_pViewData->GetLine(i);
\r
2760 rightstate.linestates[i] = m_pwndRight->m_pViewData->GetState(i);
\r
2762 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
\r
2764 bottomstate.difflines[i] = m_pwndBottom->m_pViewData->GetLine(i);
\r
2765 bottomstate.linestates[i] = m_pwndBottom->m_pViewData->GetState(i);
\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
2771 m_pViewData->SetLine(i, newLine);
\r
2772 m_pViewData->SetState(i, DIFFSTATE_EDITED);
\r
2777 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
\r
2779 leftstate.removedlines[i] = m_pwndLeft->m_pViewData->GetData(i);
\r
2781 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
\r
2783 rightstate.removedlines[i] = m_pwndRight->m_pViewData->GetData(i);
\r
2785 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
\r
2787 bottomstate.removedlines[i] = m_pwndBottom->m_pViewData->GetData(i);
\r
2789 linestoremove.push_back(i);
\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
2796 std::vector<LONG>::const_iterator it = linestoremove.begin();
\r
2797 int nLineToRemove = *it;
\r
2798 for ( ; it != linestoremove.end(); ++it)
\r
2801 m_pwndLeft->RemoveLine(nLineToRemove);
\r
2803 m_pwndRight->RemoveLine(nLineToRemove);
\r
2805 m_pwndBottom->RemoveLine(nLineToRemove);
\r
2809 m_ptCaretPos = m_ptSelectionStartPos;
\r
2813 EnsureCaretVisible();
\r
2814 Invalidate(FALSE);
\r
2817 void CBaseView::PasteText()
\r
2819 if (!OpenClipboard())
\r
2822 CString sClipboardText;
\r
2823 HGLOBAL hglb = GetClipboardData(CF_TEXT);
\r
2826 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
\r
2827 sClipboardText = CString(lpstr);
\r
2828 GlobalUnlock(hglb);
\r
2830 hglb = GetClipboardData(CF_UNICODETEXT);
\r
2833 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
\r
2834 sClipboardText = lpstr;
\r
2835 GlobalUnlock(hglb);
\r
2839 if (sClipboardText.IsEmpty())
\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
2850 OnChar(sClipboardText[i], 0, 0);
\r
2852 CUndo::GetInstance().EndGrouping();
\r
2855 void CBaseView::OnCaretDown()
\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
2865 EnsureCaretVisible();
\r
2866 ShowDiffLines(m_ptCaretPos.y);
\r
2869 bool CBaseView::MoveCaretLeft()
\r
2871 if (m_ptCaretPos.x == 0)
\r
2873 if (m_ptCaretPos.y > 0)
\r
2876 m_ptCaretPos.x = GetLineLength(m_ptCaretPos.y);
\r
2888 bool CBaseView::MoveCaretRight()
\r
2890 if (m_ptCaretPos.x >= GetLineLength(m_ptCaretPos.y))
\r
2892 if (m_ptCaretPos.y < (GetLineCount() - 1))
\r
2895 m_ptCaretPos.x = 0;
\r
2907 void CBaseView::UpdateGoalPos()
\r
2909 m_nCaretGoalPos = CalculateActualOffset(m_ptCaretPos.y, m_ptCaretPos.x);
\r
2912 void CBaseView::OnCaretLeft()
\r
2915 if (GetKeyState(VK_SHIFT)&0x8000)
\r
2916 AdjustSelection();
\r
2919 EnsureCaretVisible();
\r
2923 void CBaseView::OnCaretRight()
\r
2926 if (GetKeyState(VK_SHIFT)&0x8000)
\r
2927 AdjustSelection();
\r
2930 EnsureCaretVisible();
\r
2934 void CBaseView::OnCaretUp()
\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
2944 EnsureCaretVisible();
\r
2945 ShowDiffLines(m_ptCaretPos.y);
\r
2948 bool CBaseView::IsWordSeparator(wchar_t ch) const
\r
2950 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
\r
2953 bool CBaseView::IsCaretAtWordBoundary() const
\r
2955 LPCTSTR line = GetLineChars(m_ptCaretPos.y);
\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
2963 IsWordSeparator(line[m_ptCaretPos.x]) !=
\r
2964 IsWordSeparator(line[m_ptCaretPos.x - 1]);
\r
2967 void CBaseView::OnCaretWordleft()
\r
2969 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
\r
2972 if (GetKeyState(VK_SHIFT)&0x8000)
\r
2973 AdjustSelection();
\r
2976 EnsureCaretVisible();
\r
2980 void CBaseView::OnCaretWordright()
\r
2982 while (MoveCaretRight() && !IsCaretAtWordBoundary())
\r
2985 if (GetKeyState(VK_SHIFT)&0x8000)
\r
2986 AdjustSelection();
\r
2989 EnsureCaretVisible();
\r
2993 void CBaseView::ClearCurrentSelection()
\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
3003 void CBaseView::ClearSelection()
\r
3006 m_pwndLeft->ClearCurrentSelection();
\r
3008 m_pwndRight->ClearCurrentSelection();
\r
3010 m_pwndBottom->ClearCurrentSelection();
\r
3013 void CBaseView::AdjustSelection()
\r
3015 if ((m_ptCaretPos.y < m_ptSelectionOrigin.y) ||
\r
3016 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x <= m_ptSelectionOrigin.x))
\r
3018 m_ptSelectionStartPos = m_ptCaretPos;
\r
3019 m_ptSelectionEndPos = m_ptSelectionOrigin;
\r
3022 if ((m_ptCaretPos.y > m_ptSelectionOrigin.y) ||
\r
3023 (m_ptCaretPos.y == m_ptSelectionOrigin.y && m_ptCaretPos.x >= m_ptSelectionOrigin.x))
\r
3025 m_ptSelectionStartPos = m_ptSelectionOrigin;
\r
3026 m_ptSelectionEndPos = m_ptCaretPos;
\r
3029 SetupSelection(min(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y), max(m_ptSelectionStartPos.y, m_ptSelectionEndPos.y));
\r
3031 Invalidate(FALSE);
\r
3034 void CBaseView::OnEditCut()
\r
3036 if (!m_bCaretHidden)
\r
3039 RemoveSelectedText();
\r
3043 void CBaseView::OnEditPaste()
\r
3045 if (!m_bCaretHidden)
\r