Correctly deallocate buffer
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blobeb2d83a1c46861222adabe507c068b69dddfd9ae
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2012 - TortoiseSVN
4 // Copyright (C) 2011-2012 Sven Strickroth <email@cs-ware.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include "registry.h"
23 #include "TortoiseMerge.h"
24 #include "MainFrm.h"
25 #include "BaseView.h"
26 #include "DiffColors.h"
27 #include "StringUtils.h"
28 #include "AppUtils.h"
29 #include "GotoLineDlg.h"
31 // Note about lines:
32 // We use three different kind of lines here:
33 // 1. The real lines of the original files.
34 // These are shown in the view margin and are not used elsewhere, they're only for user information.
35 // 2. Screen lines.
36 // The lines actually shown on screen. All methods use screen lines as parameters/outputs if not explicitly specified otherwise.
37 // 3. View lines.
38 // These are the lines of the diff data. If unmodified sections are collapsed, not all of those lines are shown.
40 // Basically view lines are the line data, while screen lines are shown lines.
43 #ifdef _DEBUG
44 #define new DEBUG_NEW
45 #endif
47 #define MARGINWIDTH 20
48 #define HEADERHEIGHT 10
50 #define IDT_SCROLLTIMER 101
52 CBaseView * CBaseView::m_pwndLeft = NULL;
53 CBaseView * CBaseView::m_pwndRight = NULL;
54 CBaseView * CBaseView::m_pwndBottom = NULL;
55 CLocatorBar * CBaseView::m_pwndLocator = NULL;
56 CLineDiffBar * CBaseView::m_pwndLineDiffBar = NULL;
57 CMFCStatusBar * CBaseView::m_pwndStatusBar = NULL;
58 CMainFrame * CBaseView::m_pMainFrame = NULL;
59 CBaseView::Screen2View CBaseView::m_Screen2View;
60 const UINT CBaseView::m_FindDialogMessage = RegisterWindowMessage(FINDMSGSTRING);
62 allviewstate CBaseView::m_AllState;
64 IMPLEMENT_DYNCREATE(CBaseView, CView)
66 CBaseView::CBaseView()
67 : m_pCacheBitmap(NULL)
68 , m_pViewData(NULL)
69 , m_pOtherViewData(NULL)
70 , m_pOtherView(NULL)
71 , m_nLineHeight(-1)
72 , m_nCharWidth(-1)
73 , m_nScreenChars(-1)
74 , m_nLastScreenChars(-1)
75 , m_nMaxLineLength(-1)
76 , m_nScreenLines(-1)
77 , m_nTopLine(0)
78 , m_nOffsetChar(0)
79 , m_nDigits(0)
80 , m_nMouseLine(-1)
81 , m_mouseInMargin(false)
82 , m_bIsHidden(FALSE)
83 , lineendings(EOL_AUTOLINE)
84 , m_bReadonly(true)
85 , m_bTarget(false)
86 , m_nCaretGoalPos(0)
87 , m_nSelViewBlockStart(-1)
88 , m_nSelViewBlockEnd(-1)
89 , m_bFocused(FALSE)
90 , m_bShowSelection(true)
91 , texttype(CFileTextLines::AUTOTYPE)
92 , m_bModified(FALSE)
93 , m_bOtherDiffChecked(false)
94 , m_bInlineWordDiff(true)
95 , m_bWhitespaceInlineDiffs(false)
96 , m_pState(NULL)
97 , m_pFindDialog(NULL)
98 , m_nStatusBarID(0)
99 , m_bMatchCase(false)
100 , m_bLimitToDiff(true)
101 , m_bWholeWord(false)
102 , m_pDC(NULL)
104 m_ptCaretViewPos.x = 0;
105 m_ptCaretViewPos.y = 0;
106 m_ptSelectionViewPosStart = m_ptCaretViewPos;
107 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
108 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosEnd;
109 m_bViewWhitespace = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewWhitespaces"), 1);
110 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
111 m_bShowInlineDiff = CRegDWORD(_T("Software\\TortoiseGitMerge\\DisplayBinDiff"), TRUE);
112 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
113 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
114 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
115 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
116 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
117 m_sWordSeparators = CRegString(_T("Software\\TortoiseGitMerge\\WordSeparators"), _T("[]();:.,{}!@#$%^&*-+=|/\\<>'`~\""));
118 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
119 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
120 std::fill_n(m_apFonts, fontsCount, (CFont*)NULL);
121 m_hConflictedIcon = LoadIcon(IDI_CONFLICTEDLINE);
122 m_hConflictedIgnoredIcon = LoadIcon(IDI_CONFLICTEDIGNOREDLINE);
123 m_hRemovedIcon = LoadIcon(IDI_REMOVEDLINE);
124 m_hAddedIcon = LoadIcon(IDI_ADDEDLINE);
125 m_hWhitespaceBlockIcon = LoadIcon(IDI_WHITESPACELINE);
126 m_hEqualIcon = LoadIcon(IDI_EQUALLINE);
127 m_hLineEndingCR = LoadIcon(IDI_LINEENDINGCR);
128 m_hLineEndingCRLF = LoadIcon(IDI_LINEENDINGCRLF);
129 m_hLineEndingLF = LoadIcon(IDI_LINEENDINGLF);
130 m_hEditedIcon = LoadIcon(IDI_LINEEDITED);
131 m_hMovedIcon = LoadIcon(IDI_MOVEDLINE);
132 m_margincursor = (HCURSOR)LoadImage(AfxGetResourceHandle(), MAKEINTRESOURCE(IDC_MARGINCURSOR), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
134 for (int i=0; i<1024; ++i)
135 m_sConflictedText += _T("??");
136 m_sNoLineNr.LoadString(IDS_EMPTYLINETT);
138 m_szTip[0] = 0;
139 m_wszTip[0] = 0;
140 SecureZeroMemory(&m_lfBaseFont, sizeof(m_lfBaseFont));
141 EnableToolTips();
144 CBaseView::~CBaseView()
146 ReleaseBitmap();
147 DeleteFonts();
148 DestroyIcon(m_hAddedIcon);
149 DestroyIcon(m_hRemovedIcon);
150 DestroyIcon(m_hConflictedIcon);
151 DestroyIcon(m_hConflictedIgnoredIcon);
152 DestroyIcon(m_hWhitespaceBlockIcon);
153 DestroyIcon(m_hEqualIcon);
154 DestroyIcon(m_hLineEndingCR);
155 DestroyIcon(m_hLineEndingCRLF);
156 DestroyIcon(m_hLineEndingLF);
157 DestroyIcon(m_hEditedIcon);
158 DestroyIcon(m_hMovedIcon);
159 DestroyCursor(m_margincursor);
162 BEGIN_MESSAGE_MAP(CBaseView, CView)
163 ON_WM_VSCROLL()
164 ON_WM_HSCROLL()
165 ON_WM_ERASEBKGND()
166 ON_WM_CREATE()
167 ON_WM_DESTROY()
168 ON_WM_SIZE()
169 ON_WM_MOUSEWHEEL()
170 ON_WM_MOUSEHWHEEL()
171 ON_WM_SETCURSOR()
172 ON_WM_KILLFOCUS()
173 ON_WM_SETFOCUS()
174 ON_WM_CONTEXTMENU()
175 ON_COMMAND(ID_NAVIGATE_NEXTDIFFERENCE, OnMergeNextdifference)
176 ON_COMMAND(ID_NAVIGATE_PREVIOUSDIFFERENCE, OnMergePreviousdifference)
177 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipNotify)
178 ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipNotify)
179 ON_WM_KEYDOWN()
180 ON_WM_LBUTTONDOWN()
181 ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
182 ON_WM_MOUSEMOVE()
183 ON_COMMAND(ID_NAVIGATE_PREVIOUSCONFLICT, OnMergePreviousconflict)
184 ON_COMMAND(ID_NAVIGATE_NEXTCONFLICT, OnMergeNextconflict)
185 ON_WM_CHAR()
186 ON_COMMAND(ID_CARET_DOWN, &CBaseView::OnCaretDown)
187 ON_COMMAND(ID_CARET_LEFT, &CBaseView::OnCaretLeft)
188 ON_COMMAND(ID_CARET_RIGHT, &CBaseView::OnCaretRight)
189 ON_COMMAND(ID_CARET_UP, &CBaseView::OnCaretUp)
190 ON_COMMAND(ID_CARET_WORDLEFT, &CBaseView::OnCaretWordleft)
191 ON_COMMAND(ID_CARET_WORDRIGHT, &CBaseView::OnCaretWordright)
192 ON_COMMAND(ID_EDIT_CUT, &CBaseView::OnEditCut)
193 ON_COMMAND(ID_EDIT_PASTE, &CBaseView::OnEditPaste)
194 ON_WM_TIMER()
195 ON_WM_LBUTTONDBLCLK()
196 ON_COMMAND(ID_NAVIGATE_NEXTINLINEDIFF, &CBaseView::OnNavigateNextinlinediff)
197 ON_COMMAND(ID_NAVIGATE_PREVINLINEDIFF, &CBaseView::OnNavigatePrevinlinediff)
198 ON_COMMAND(ID_EDIT_SELECTALL, &CBaseView::OnEditSelectall)
199 ON_COMMAND(ID_EDIT_FIND, OnEditFind)
200 ON_REGISTERED_MESSAGE(m_FindDialogMessage, OnFindDialogMessage)
201 ON_COMMAND(ID_EDIT_FINDNEXT, OnEditFindnext)
202 ON_COMMAND(ID_EDIT_FINDPREV, OnEditFindprev)
203 ON_COMMAND(ID_EDIT_FINDNEXTSTART, OnEditFindnextStart)
204 ON_COMMAND(ID_EDIT_FINDPREVSTART, OnEditFindprevStart)
205 ON_COMMAND(ID_EDIT_GOTOLINE, &CBaseView::OnEditGotoline)
206 ON_WM_LBUTTONUP()
207 END_MESSAGE_MAP()
210 void CBaseView::DocumentUpdated()
212 ReleaseBitmap();
213 m_nLineHeight = -1;
214 m_nCharWidth = -1;
215 m_nScreenChars = -1;
216 m_nLastScreenChars = -1;
217 m_nMaxLineLength = -1;
218 m_nScreenLines = -1;
219 m_nTopLine = 0;
220 m_bModified = FALSE;
221 m_bOtherDiffChecked = false;
222 m_nDigits = 0;
223 m_nMouseLine = -1;
224 m_nTabSize = (int)(DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\TabSize"), 4);
225 m_bViewLinenumbers = CRegDWORD(_T("Software\\TortoiseGitMerge\\ViewLinenumbers"), 1);
226 m_InlineAddedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineAdded"), INLINEADDED_COLOR);
227 m_InlineRemovedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineRemoved"), INLINEREMOVED_COLOR);
228 m_ModifiedBk = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\ColorModifiedB"), MODIFIED_COLOR);
229 m_WhiteSpaceFg = CRegDWORD(_T("Software\\TortoiseGitMerge\\Colors\\Whitespace"), GetSysColor(COLOR_GRAYTEXT));
230 m_bIconLFs = CRegDWORD(_T("Software\\TortoiseGitMerge\\IconLFs"), 0);
231 m_nInlineDiffMaxLineLength = CRegDWORD(_T("Software\\TortoiseGitMerge\\InlineDiffMaxLineLength"), 3000);
232 DeleteFonts();
233 ClearCurrentSelection();
234 UpdateStatusBar();
235 Invalidate();
238 void CBaseView::UpdateStatusBar()
240 int nRemovedLines = 0;
241 int nAddedLines = 0;
242 int nConflictedLines = 0;
244 if (m_pViewData)
246 for (int i=0; i<m_pViewData->GetCount(); i++)
248 DiffStates state = m_pViewData->GetState(i);
249 switch (state)
251 case DIFFSTATE_ADDED:
252 case DIFFSTATE_IDENTICALADDED:
253 case DIFFSTATE_MOVED_TO:
254 case DIFFSTATE_THEIRSADDED:
255 case DIFFSTATE_YOURSADDED:
256 case DIFFSTATE_CONFLICTADDED:
257 nAddedLines++;
258 break;
259 case DIFFSTATE_IDENTICALREMOVED:
260 case DIFFSTATE_REMOVED:
261 case DIFFSTATE_MOVED_FROM:
262 case DIFFSTATE_THEIRSREMOVED:
263 case DIFFSTATE_YOURSREMOVED:
264 nRemovedLines++;
265 break;
266 case DIFFSTATE_CONFLICTED:
267 case DIFFSTATE_CONFLICTED_IGNORED:
268 nConflictedLines++;
269 break;
274 CString sBarText;
275 CString sTemp;
277 switch (texttype)
279 case CFileTextLines::ASCII:
280 sBarText = _T("ASCII ");
281 break;
282 case CFileTextLines::BINARY:
283 sBarText = _T("BINARY ");
284 break;
285 case CFileTextLines::UTF16_LE:
286 sBarText = _T("UTF-16LE ");
287 break;
288 case CFileTextLines::UTF16_BE:
289 sBarText = _T("UTF-16BE ");
290 break;
291 case CFileTextLines::UTF32_LE:
292 sBarText = _T("UTF-32LE ");
293 break;
294 case CFileTextLines::UTF32_BE:
295 sBarText = _T("UTF-32BE ");
296 break;
297 case CFileTextLines::UTF8:
298 sBarText = _T("UTF8 ");
299 break;
300 case CFileTextLines::UTF8BOM:
301 sBarText = _T("UTF8 BOM ");
302 break;
305 switch(lineendings)
307 case EOL_LF:
308 sBarText += _T("LF ");
309 break;
310 case EOL_CRLF:
311 sBarText += _T("CRLF ");
312 break;
313 case EOL_LFCR:
314 sBarText += _T("LFCR ");
315 break;
316 case EOL_CR:
317 sBarText += _T("CR ");
318 break;
319 case EOL_VT:
320 sBarText += _T("VT ");
321 break;
322 case EOL_FF:
323 sBarText += _T("FF ");
324 break;
325 case EOL_NEL:
326 sBarText += _T("NEL ");
327 break;
328 case EOL_LS:
329 sBarText += _T("LS ");
330 break;
331 case EOL_PS:
332 sBarText += _T("PS ");
333 break;
334 #ifdef _DEBUG
335 case EOL_AUTOLINE:
336 sBarText += _T("AEOL ");
337 break;
338 #endif
341 if (sBarText.IsEmpty())
342 sBarText += _T(" / ");
344 if (nRemovedLines)
346 sTemp.Format(IDS_STATUSBAR_REMOVEDLINES, nRemovedLines);
347 if (!sBarText.IsEmpty())
348 sBarText += _T(" / ");
349 sBarText += sTemp;
351 if (nAddedLines)
353 sTemp.Format(IDS_STATUSBAR_ADDEDLINES, nAddedLines);
354 if (!sBarText.IsEmpty())
355 sBarText += _T(" / ");
356 sBarText += sTemp;
358 if (nConflictedLines)
360 sTemp.Format(IDS_STATUSBAR_CONFLICTEDLINES, nConflictedLines);
361 if (!sBarText.IsEmpty())
362 sBarText += _T(" / ");
363 sBarText += sTemp;
365 if (m_pwndStatusBar)
367 UINT nID;
368 UINT nStyle;
369 int cxWidth;
370 int nIndex = m_pwndStatusBar->CommandToIndex(m_nStatusBarID);
371 if (m_nStatusBarID == ID_INDICATOR_BOTTOMVIEW)
373 sBarText.Format(IDS_STATUSBAR_CONFLICTS, nConflictedLines);
375 if (m_nStatusBarID == ID_INDICATOR_LEFTVIEW)
377 sTemp.LoadString(IDS_STATUSBAR_LEFTVIEW);
378 sBarText = sTemp+sBarText;
380 if (m_nStatusBarID == ID_INDICATOR_RIGHTVIEW)
382 sTemp.LoadString(IDS_STATUSBAR_RIGHTVIEW);
383 sBarText = sTemp+sBarText;
385 m_pwndStatusBar->GetPaneInfo(nIndex, nID, nStyle, cxWidth);
386 //calculate the width of the text
387 CDC * pDC = m_pwndStatusBar->GetDC();
388 if (pDC)
390 CSize size = pDC->GetTextExtent(sBarText);
391 m_pwndStatusBar->SetPaneInfo(nIndex, nID, nStyle, size.cx+2);
392 ReleaseDC(pDC);
394 m_pwndStatusBar->SetPaneText(nIndex, sBarText);
398 BOOL CBaseView::PreCreateWindow(CREATESTRUCT& cs)
400 if (!CView::PreCreateWindow(cs))
401 return FALSE;
403 cs.dwExStyle |= WS_EX_CLIENTEDGE;
404 cs.style &= ~WS_BORDER;
405 cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
406 ::LoadCursor(NULL, IDC_ARROW), reinterpret_cast<HBRUSH>(COLOR_WINDOW+1), NULL);
408 CWnd *pParentWnd = CWnd::FromHandlePermanent(cs.hwndParent);
409 if (pParentWnd == NULL || ! pParentWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd)))
411 // View must always create its own scrollbars,
412 // if only it's not used within splitter
413 cs.style |= (WS_HSCROLL | WS_VSCROLL);
415 cs.lpszClass = AfxRegisterWndClass(CS_DBLCLKS);
416 return TRUE;
419 CFont* CBaseView::GetFont(BOOL bItalic /*= FALSE*/, BOOL bBold /*= FALSE*/)
421 int nIndex = 0;
422 if (bBold)
423 nIndex |= 1;
424 if (bItalic)
425 nIndex |= 2;
426 if (m_apFonts[nIndex] == NULL)
428 m_apFonts[nIndex] = new CFont;
429 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
430 m_lfBaseFont.lfWeight = bBold ? FW_BOLD : FW_NORMAL;
431 m_lfBaseFont.lfItalic = (BYTE) bItalic;
432 CDC * pDC = GetDC();
433 if (pDC)
435 m_lfBaseFont.lfHeight = -MulDiv((DWORD)CRegDWORD(_T("Software\\TortoiseGitMerge\\LogFontSize"), 10), GetDeviceCaps(pDC->m_hDC, LOGPIXELSY), 72);
436 ReleaseDC(pDC);
438 _tcsncpy_s(m_lfBaseFont.lfFaceName, (LPCTSTR)(CString)CRegString(_T("Software\\TortoiseGitMerge\\LogFontName"), _T("Courier New")), 32);
439 if (!m_apFonts[nIndex]->CreateFontIndirect(&m_lfBaseFont))
441 delete m_apFonts[nIndex];
442 m_apFonts[nIndex] = NULL;
443 return CView::GetFont();
446 return m_apFonts[nIndex];
449 void CBaseView::CalcLineCharDim()
451 CDC *pDC = GetDC();
452 if (pDC == nullptr)
453 return;
454 CFont *pOldFont = pDC->SelectObject(GetFont());
455 const CSize szCharExt = pDC->GetTextExtent(_T("X"));
456 pDC->SelectObject(pOldFont);
457 ReleaseDC(pDC);
459 m_nLineHeight = szCharExt.cy;
460 if (m_nLineHeight <= 0)
461 m_nLineHeight = -1;
462 m_nCharWidth = szCharExt.cx;
463 if (m_nCharWidth <= 0)
464 m_nCharWidth = -1;
467 int CBaseView::GetScreenChars()
469 if (m_nScreenChars == -1)
471 CRect rect;
472 GetClientRect(&rect);
473 m_nScreenChars = (rect.Width() - GetMarginWidth() - GetSystemMetrics(SM_CXVSCROLL)) / GetCharWidth();
474 if (m_nScreenChars < 0)
475 m_nScreenChars = 0;
477 return m_nScreenChars;
480 int CBaseView::GetAllMinScreenChars() const
482 int nChars = INT_MAX;
483 if (IsLeftViewGood())
484 nChars = std::min<int>(nChars, m_pwndLeft->GetScreenChars());
485 if (IsRightViewGood())
486 nChars = std::min<int>(nChars, m_pwndRight->GetScreenChars());
487 if (IsBottomViewGood())
488 nChars = std::min<int>(nChars, m_pwndBottom->GetScreenChars());
489 return (nChars==INT_MAX) ? 0 : nChars;
492 int CBaseView::GetAllMaxLineLength() const
494 int nLength = 0;
495 if (IsLeftViewGood())
496 nLength = std::max<int>(nLength, m_pwndLeft->GetMaxLineLength());
497 if (IsRightViewGood())
498 nLength = std::max<int>(nLength, m_pwndRight->GetMaxLineLength());
499 if (IsBottomViewGood())
500 nLength = std::max<int>(nLength, m_pwndBottom->GetMaxLineLength());
501 return nLength;
504 int CBaseView::GetLineHeight()
506 if (m_nLineHeight == -1)
507 CalcLineCharDim();
508 if (m_nLineHeight <= 0)
509 return 1;
510 return m_nLineHeight;
513 int CBaseView::GetCharWidth()
515 if (m_nCharWidth == -1)
516 CalcLineCharDim();
517 if (m_nCharWidth <= 0)
518 return 1;
519 return m_nCharWidth;
522 int CBaseView::GetMaxLineLength()
524 if (m_nMaxLineLength == -1)
526 m_nMaxLineLength = 0;
527 int nLineCount = GetLineCount();
528 for (int i=0; i<nLineCount; i++)
530 int nActualLength = GetLineLength(i);
531 if (m_nMaxLineLength < nActualLength)
532 m_nMaxLineLength = nActualLength;
535 return m_nMaxLineLength;
538 int CBaseView::GetLineLength(int index)
540 if (m_pViewData == NULL)
541 return 0;
542 if (m_pViewData->GetCount() == 0)
543 return 0;
544 if ((int)m_Screen2View.size() <= index)
545 return 0;
546 int viewLine = GetViewLineForScreen(index);
547 if (m_pMainFrame->m_bWrapLines)
549 int nLineLength = GetLineChars(index).GetLength();
550 ASSERT(nLineLength >= 0);
551 return nLineLength;
553 int nLineLength = m_pViewData->GetLine(viewLine).GetLength();
554 ASSERT(nLineLength >= 0);
555 return nLineLength;
558 int CBaseView::GetViewLineLength(int nViewLine) const
560 if (m_pViewData == NULL)
561 return 0;
562 if (m_pViewData->GetCount() <= nViewLine)
563 return 0;
564 int nLineLength = m_pViewData->GetLine(nViewLine).GetLength();
565 ASSERT(nLineLength >= 0);
566 return nLineLength;
569 int CBaseView::GetLineCount() const
571 if (m_pViewData == NULL)
572 return 1;
573 int nLineCount = (int)m_Screen2View.size();
574 ASSERT(nLineCount >= 0);
575 return nLineCount;
578 int CBaseView::GetSubLineOffset(int index)
580 return m_Screen2View.GetSubLineOffset(index);
583 CString CBaseView::GetViewLineChars(int nViewLine) const
585 if (m_pViewData == NULL)
586 return 0;
587 if (m_pViewData->GetCount() <= nViewLine)
588 return 0;
589 return m_pViewData->GetLine(nViewLine);
592 CString CBaseView::GetLineChars(int index)
594 if (m_pViewData == NULL)
595 return 0;
596 if (m_pViewData->GetCount() == 0)
597 return 0;
598 if ((int)m_Screen2View.size() <= index)
599 return 0;
600 int viewLine = GetViewLineForScreen(index);
601 if (m_pMainFrame->m_bWrapLines)
603 int subLine = GetSubLineOffset(index);
604 if (subLine >= 0)
606 if (subLine < CountMultiLines(viewLine))
608 return m_ScreenedViewLine[viewLine].SubLines[subLine];
610 return L"";
613 return m_pViewData->GetLine(viewLine);
616 void CBaseView::CheckOtherView()
618 if (m_bOtherDiffChecked)
619 return;
620 // find out what the 'other' file is
621 m_pOtherViewData = NULL;
622 m_pOtherView = NULL;
623 if (this == m_pwndLeft && IsRightViewGood())
625 m_pOtherViewData = m_pwndRight->m_pViewData;
626 m_pOtherView = m_pwndRight;
629 if (this == m_pwndRight && IsLeftViewGood())
631 m_pOtherViewData = m_pwndLeft->m_pViewData;
632 m_pOtherView = m_pwndLeft;
635 m_bOtherDiffChecked = true;
639 void CBaseView::GetWhitespaceBlock(CViewData *viewData, int nLineIndex, int & nStartBlock, int & nEndBlock)
641 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
642 ASSERT(viewData);
644 DiffStates origstate = viewData->GetState(nLineIndex);
646 // Go back and forward at most MAX_WHITESPACEBLOCK_SIZE lines to see where this block ends
647 nStartBlock = nLineIndex;
648 nEndBlock = nLineIndex;
649 while ((nStartBlock > 0) && (nStartBlock > (nLineIndex - MAX_WHITESPACEBLOCK_SIZE)))
651 DiffStates state = viewData->GetState(nStartBlock - 1);
652 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
653 origstate = state;
654 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
655 nStartBlock--;
656 else
657 break;
659 while ((nEndBlock < (viewData->GetCount() - 1)) && (nEndBlock < (nLineIndex + MAX_WHITESPACEBLOCK_SIZE)))
661 DiffStates state = viewData->GetState(nEndBlock + 1);
662 if ((origstate == DIFFSTATE_EMPTY) && (state != DIFFSTATE_NORMAL))
663 origstate = state;
664 if ((origstate == state) || (state == DIFFSTATE_EMPTY))
665 nEndBlock++;
666 else
667 break;
671 CString CBaseView::GetWhitespaceString(CViewData *viewData, int nStartBlock, int nEndBlock)
673 enum { MAX_WHITESPACEBLOCK_SIZE = 8 };
674 int len = 0;
675 for (int i = nStartBlock; i <= nEndBlock; ++i)
676 len += viewData->GetLine(i).GetLength();
678 CString block;
679 // do not check for whitespace blocks if the line is too long, because
680 // reserving a lot of memory here takes too much time (performance hog)
681 if (len > MAX_WHITESPACEBLOCK_SIZE*256)
682 return block;
683 block.Preallocate(len+1);
684 for (int i = nStartBlock; i <= nEndBlock; ++i)
685 block += viewData->GetLine(i);
686 return block;
689 bool CBaseView::IsBlockWhitespaceOnly(int nLineIndex, bool& bIdentical)
691 if (m_pViewData == NULL)
692 return false;
693 bIdentical = false;
694 CheckOtherView();
695 if (!m_pOtherViewData)
696 return false;
697 int viewLine = GetViewLineForScreen(nLineIndex);
698 if (
699 (m_pViewData->GetState(viewLine) == DIFFSTATE_NORMAL) &&
700 (m_pOtherViewData->GetLine(viewLine) == m_pViewData->GetLine(viewLine))
703 bIdentical = true;
704 return false;
706 // first check whether the line itself only has whitespace changes
707 CString mine = m_pViewData->GetLine(viewLine);
708 CString other = m_pOtherViewData->GetLine(min(viewLine, m_pOtherViewData->GetCount() - 1));
709 if (mine.IsEmpty() && other.IsEmpty())
711 bIdentical = true;
712 return false;
715 if (mine == other)
717 bIdentical = true;
718 return true;
720 FilterWhitespaces(mine, other);
721 if (mine == other)
722 return true;
724 int nStartBlock1, nEndBlock1;
725 int nStartBlock2, nEndBlock2;
726 GetWhitespaceBlock(m_pViewData, viewLine, nStartBlock1, nEndBlock1);
727 GetWhitespaceBlock(m_pOtherViewData, min(viewLine, m_pOtherViewData->GetCount() - 1), nStartBlock2, nEndBlock2);
728 mine = GetWhitespaceString(m_pViewData, nStartBlock1, nEndBlock1);
729 if (mine.IsEmpty())
730 bIdentical = false;
731 else
733 other = GetWhitespaceString(m_pOtherViewData, nStartBlock2, nEndBlock2);
734 bIdentical = mine == other;
735 FilterWhitespaces(mine, other);
738 return (!mine.IsEmpty()) && (mine == other);
741 bool CBaseView::IsViewLineHidden(int nViewLine)
743 return IsViewLineHidden(m_pViewData, nViewLine);
746 bool CBaseView::IsViewLineHidden(CViewData * pViewData, int nViewLine)
748 return m_pMainFrame->m_bCollapsed && (pViewData->GetHideState(nViewLine)!=HIDESTATE_SHOWN);
751 int CBaseView::GetLineNumber(int index) const
753 if (m_pViewData == NULL)
754 return -1;
755 int viewLine = GetViewLineForScreen(index);
756 if (m_pViewData->GetLineNumber(viewLine)==DIFF_EMPTYLINENUMBER)
757 return -1;
758 return m_pViewData->GetLineNumber(viewLine);
761 int CBaseView::GetScreenLines()
763 if (m_nScreenLines == -1)
765 SCROLLBARINFO sbi;
766 sbi.cbSize = sizeof(sbi);
767 int scrollBarHeight = 0;
768 if (GetScrollBarInfo(OBJID_HSCROLL, &sbi))
769 scrollBarHeight = sbi.rcScrollBar.bottom - sbi.rcScrollBar.top;
770 if ((sbi.rgstate[0] & STATE_SYSTEM_INVISIBLE)||(sbi.rgstate[0] & STATE_SYSTEM_UNAVAILABLE))
771 scrollBarHeight = 0;
772 CRect rect;
773 GetClientRect(&rect);
774 m_nScreenLines = (rect.Height() - HEADERHEIGHT - scrollBarHeight) / GetLineHeight();
775 if (m_nScreenLines < 0)
776 m_nScreenLines = 0;
778 return m_nScreenLines;
781 int CBaseView::GetAllMinScreenLines() const
783 int nLines = INT_MAX;
784 if (IsLeftViewGood())
785 nLines = m_pwndLeft->GetScreenLines();
786 if (IsRightViewGood())
787 nLines = std::min<int>(nLines, m_pwndRight->GetScreenLines());
788 if (IsBottomViewGood())
789 nLines = std::min<int>(nLines, m_pwndBottom->GetScreenLines());
790 return (nLines==INT_MAX) ? 0 : nLines;
793 int CBaseView::GetAllLineCount() const
795 int nLines = 0;
796 if (IsLeftViewGood())
797 nLines = m_pwndLeft->GetLineCount();
798 if (IsRightViewGood())
799 nLines = std::max<int>(nLines, m_pwndRight->GetLineCount());
800 if (IsBottomViewGood())
801 nLines = std::max<int>(nLines, m_pwndBottom->GetLineCount());
802 return nLines;
805 void CBaseView::RecalcAllVertScrollBars(BOOL bPositionOnly /*= FALSE*/)
807 if (IsLeftViewGood())
808 m_pwndLeft->RecalcVertScrollBar(bPositionOnly);
809 if (IsRightViewGood())
810 m_pwndRight->RecalcVertScrollBar(bPositionOnly);
811 if (IsBottomViewGood())
812 m_pwndBottom->RecalcVertScrollBar(bPositionOnly);
815 void CBaseView::RecalcVertScrollBar(BOOL bPositionOnly /*= FALSE*/)
817 SCROLLINFO si;
818 si.cbSize = sizeof(si);
819 if (bPositionOnly)
821 si.fMask = SIF_POS;
822 si.nPos = m_nTopLine;
824 else
826 EnableScrollBarCtrl(SB_VERT, TRUE);
827 if (GetAllMinScreenLines() >= GetAllLineCount() && m_nTopLine > 0)
829 m_nTopLine = 0;
830 Invalidate();
832 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
833 si.nMin = 0;
834 si.nMax = GetAllLineCount();
835 si.nPage = GetAllMinScreenLines();
836 si.nPos = m_nTopLine;
838 VERIFY(SetScrollInfo(SB_VERT, &si));
841 void CBaseView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
843 CView::OnVScroll(nSBCode, nPos, pScrollBar);
844 if (m_pwndLeft)
845 m_pwndLeft->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
846 if (m_pwndRight)
847 m_pwndRight->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
848 if (m_pwndBottom)
849 m_pwndBottom->OnDoVScroll(nSBCode, nPos, pScrollBar, this);
850 if (m_pwndLocator)
851 m_pwndLocator->Invalidate();
854 void CBaseView::OnDoVScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
856 // Note we cannot use nPos because of its 16-bit nature
857 SCROLLINFO si;
858 si.cbSize = sizeof(si);
859 si.fMask = SIF_ALL;
860 VERIFY(master->GetScrollInfo(SB_VERT, &si));
862 int nPageLines = GetScreenLines();
863 int nLineCount = GetLineCount();
865 int nNewTopLine;
867 static LONG textwidth = 0;
868 static CString sFormat(MAKEINTRESOURCE(IDS_VIEWSCROLLTIPTEXT));
869 switch (nSBCode)
871 case SB_TOP:
872 nNewTopLine = 0;
873 break;
874 case SB_BOTTOM:
875 nNewTopLine = nLineCount - nPageLines + 1;
876 break;
877 case SB_LINEUP:
878 nNewTopLine = m_nTopLine - 1;
879 break;
880 case SB_LINEDOWN:
881 nNewTopLine = m_nTopLine + 1;
882 break;
883 case SB_PAGEUP:
884 nNewTopLine = m_nTopLine - si.nPage + 1;
885 break;
886 case SB_PAGEDOWN:
887 nNewTopLine = m_nTopLine + si.nPage - 1;
888 break;
889 case SB_THUMBPOSITION:
890 m_ScrollTool.Clear();
891 nNewTopLine = si.nTrackPos;
892 textwidth = 0;
893 break;
894 case SB_THUMBTRACK:
895 nNewTopLine = si.nTrackPos;
896 if (GetFocus() == this)
898 RECT thumbrect;
899 GetClientRect(&thumbrect);
900 ClientToScreen(&thumbrect);
902 POINT thumbpoint;
903 thumbpoint.x = thumbrect.right;
904 thumbpoint.y = thumbrect.top + ((thumbrect.bottom-thumbrect.top)*si.nTrackPos)/(si.nMax-si.nMin);
905 m_ScrollTool.Init(&thumbpoint);
906 if (textwidth == 0)
908 CString sTemp = sFormat;
909 sTemp.Format(sFormat, m_nDigits, 10*m_nDigits-1);
910 textwidth = m_ScrollTool.GetTextWidth(sTemp);
912 thumbpoint.x -= textwidth;
913 int line = GetLineNumber(nNewTopLine);
914 if (line >= 0)
915 m_ScrollTool.SetText(&thumbpoint, sFormat, m_nDigits, GetLineNumber(nNewTopLine)+1);
916 else
917 m_ScrollTool.SetText(&thumbpoint, m_sNoLineNr);
919 break;
920 default:
921 return;
924 if (nNewTopLine < 0)
925 nNewTopLine = 0;
926 if (nNewTopLine >= nLineCount)
927 nNewTopLine = nLineCount - 1;
928 ScrollToLine(nNewTopLine);
931 void CBaseView::RecalcAllHorzScrollBars(BOOL bPositionOnly /*= FALSE*/)
933 if (IsLeftViewGood())
934 m_pwndLeft->RecalcHorzScrollBar(bPositionOnly);
935 if (IsRightViewGood())
936 m_pwndRight->RecalcHorzScrollBar(bPositionOnly);
937 if (IsBottomViewGood())
938 m_pwndBottom->RecalcHorzScrollBar(bPositionOnly);
941 void CBaseView::RecalcHorzScrollBar(BOOL bPositionOnly /*= FALSE*/)
943 SCROLLINFO si;
944 si.cbSize = sizeof(si);
945 if (bPositionOnly)
947 si.fMask = SIF_POS;
948 si.nPos = m_nOffsetChar;
950 else
952 EnableScrollBarCtrl(SB_HORZ, !m_pMainFrame->m_bWrapLines);
953 if (!m_pMainFrame->m_bWrapLines)
955 int minScreenChars = GetAllMinScreenChars();
956 int maxLineLength = GetAllMaxLineLength();
957 if (minScreenChars >= maxLineLength && m_nOffsetChar > 0)
959 m_nOffsetChar = 0;
960 Invalidate();
962 si.fMask = SIF_DISABLENOSCROLL | SIF_PAGE | SIF_POS | SIF_RANGE;
963 si.nMin = 0;
964 si.nMax = m_pMainFrame->m_bWrapLines ? minScreenChars : maxLineLength;
965 si.nMax += GetMarginWidth()/GetCharWidth();
966 si.nPage = minScreenChars;
967 si.nPos = m_nOffsetChar;
970 VERIFY(SetScrollInfo(SB_HORZ, &si));
973 void CBaseView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
975 CView::OnHScroll(nSBCode, nPos, pScrollBar);
976 if (m_pwndLeft)
977 m_pwndLeft->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
978 if (m_pwndRight)
979 m_pwndRight->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
980 if (m_pwndBottom)
981 m_pwndBottom->OnDoHScroll(nSBCode, nPos, pScrollBar, this);
982 if (m_pwndLocator)
983 m_pwndLocator->Invalidate();
986 void CBaseView::OnDoHScroll(UINT nSBCode, UINT /*nPos*/, CScrollBar* /*pScrollBar*/, CBaseView * master)
988 SCROLLINFO si;
989 si.cbSize = sizeof(si);
990 si.fMask = SIF_ALL;
991 VERIFY(master->GetScrollInfo(SB_HORZ, &si));
993 int nPageChars = GetScreenChars();
994 int nMaxLineLength = GetMaxLineLength();
996 int nNewOffset;
997 switch (nSBCode)
999 case SB_LEFT:
1000 nNewOffset = 0;
1001 break;
1002 case SB_BOTTOM:
1003 nNewOffset = nMaxLineLength - nPageChars + 1;
1004 break;
1005 case SB_LINEUP:
1006 nNewOffset = m_nOffsetChar - 1;
1007 break;
1008 case SB_LINEDOWN:
1009 nNewOffset = m_nOffsetChar + 1;
1010 break;
1011 case SB_PAGEUP:
1012 nNewOffset = m_nOffsetChar - si.nPage + 1;
1013 break;
1014 case SB_PAGEDOWN:
1015 nNewOffset = m_nOffsetChar + si.nPage - 1;
1016 break;
1017 case SB_THUMBPOSITION:
1018 case SB_THUMBTRACK:
1019 nNewOffset = si.nTrackPos;
1020 break;
1021 default:
1022 return;
1025 if (nNewOffset >= nMaxLineLength)
1026 nNewOffset = nMaxLineLength - 1;
1027 if (nNewOffset < 0)
1028 nNewOffset = 0;
1029 ScrollToChar(nNewOffset, TRUE);
1032 void CBaseView::ScrollToChar(int nNewOffsetChar, BOOL bTrackScrollBar /*= TRUE*/)
1034 if (m_nOffsetChar != nNewOffsetChar)
1036 int nScrollChars = m_nOffsetChar - nNewOffsetChar;
1037 m_nOffsetChar = nNewOffsetChar;
1038 CRect rcScroll;
1039 GetClientRect(&rcScroll);
1040 rcScroll.left += GetMarginWidth();
1041 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1042 ScrollWindow(nScrollChars * GetCharWidth(), 0, &rcScroll, &rcScroll);
1043 // update the view header
1044 rcScroll.left = 0;
1045 rcScroll.top = 0;
1046 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
1047 InvalidateRect(&rcScroll, FALSE);
1048 UpdateWindow();
1049 if (bTrackScrollBar)
1050 RecalcHorzScrollBar(TRUE);
1051 UpdateCaret();
1055 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1057 if (m_pwndLeft)
1058 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1059 if (m_pwndRight)
1060 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1061 if (m_pwndBottom)
1062 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1065 void CBaseView::ScrollAllSide(int delta)
1067 int nNewOffset = m_nOffsetChar;
1068 nNewOffset += delta;
1069 int nMaxLineLength = GetMaxLineLength();
1070 if (nNewOffset >= nMaxLineLength)
1071 nNewOffset = nMaxLineLength - 1;
1072 if (nNewOffset < 0)
1073 nNewOffset = 0;
1074 ScrollAllToChar(nNewOffset, TRUE);
1075 if (m_pwndLineDiffBar)
1076 m_pwndLineDiffBar->Invalidate();
1077 UpdateCaret();
1080 void CBaseView::ScrollSide(int delta)
1082 int nNewOffset = m_nOffsetChar;
1083 nNewOffset += delta;
1084 int nMaxLineLength = GetMaxLineLength();
1085 if (nNewOffset >= nMaxLineLength)
1086 nNewOffset = nMaxLineLength - 1;
1087 if (nNewOffset < 0)
1088 nNewOffset = 0;
1089 ScrollToChar(nNewOffset, TRUE);
1090 if (m_pwndLineDiffBar)
1091 m_pwndLineDiffBar->Invalidate();
1092 UpdateCaret();
1095 void CBaseView::ScrollVertical(short zDelta)
1097 const int nLineCount = GetLineCount();
1098 int nTopLine = m_nTopLine;
1099 nTopLine -= (zDelta/30);
1100 if (nTopLine < 0)
1101 nTopLine = 0;
1102 if (nTopLine >= nLineCount)
1103 nTopLine = nLineCount - 1;
1104 ScrollToLine(nTopLine, TRUE);
1107 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1109 if (m_nTopLine != nNewTopLine)
1111 if (nNewTopLine < 0)
1112 nNewTopLine = 0;
1114 int nScrollLines = m_nTopLine - nNewTopLine;
1116 m_nTopLine = nNewTopLine;
1117 CRect rcScroll;
1118 GetClientRect(&rcScroll);
1119 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1120 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1121 UpdateWindow();
1122 if (bTrackScrollBar)
1123 RecalcVertScrollBar(TRUE);
1124 UpdateCaret();
1129 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1131 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1133 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1135 int nViewLine = GetViewLineForScreen(nLineIndex);
1136 HICON icon = NULL;
1137 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1138 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1139 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1141 DiffStates state = m_pViewData->GetState(nViewLine);
1142 switch (state)
1144 case DIFFSTATE_ADDED:
1145 case DIFFSTATE_THEIRSADDED:
1146 case DIFFSTATE_YOURSADDED:
1147 case DIFFSTATE_IDENTICALADDED:
1148 case DIFFSTATE_CONFLICTADDED:
1149 eIcon = TScreenedViewLine::ICN_ADD;
1150 break;
1151 case DIFFSTATE_REMOVED:
1152 case DIFFSTATE_THEIRSREMOVED:
1153 case DIFFSTATE_YOURSREMOVED:
1154 case DIFFSTATE_IDENTICALREMOVED:
1155 eIcon = TScreenedViewLine::ICN_REMOVED;
1156 break;
1157 case DIFFSTATE_CONFLICTED:
1158 eIcon = TScreenedViewLine::ICN_CONFLICT;
1159 break;
1160 case DIFFSTATE_CONFLICTED_IGNORED:
1161 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1162 break;
1163 case DIFFSTATE_EDITED:
1164 eIcon = TScreenedViewLine::ICN_EDIT;
1165 break;
1166 case DIFFSTATE_MOVED_TO:
1167 case DIFFSTATE_MOVED_FROM:
1168 eIcon = TScreenedViewLine::ICN_MOVED;
1169 break;
1170 default:
1171 break;
1173 bool bIdentical = false;
1174 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))
1176 if (bIdentical)
1177 eIcon = TScreenedViewLine::ICN_SAME;
1178 else
1179 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1181 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1183 switch (eIcon)
1185 case TScreenedViewLine::ICN_UNKNOWN:
1186 case TScreenedViewLine::ICN_NONE:
1187 break;
1188 case TScreenedViewLine::ICN_SAME:
1189 icon = m_hEqualIcon;
1190 break;
1191 case TScreenedViewLine::ICN_EDIT:
1192 icon = m_hEditedIcon;
1193 break;
1194 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1195 icon = m_hWhitespaceBlockIcon;
1196 break;
1197 case TScreenedViewLine::ICN_ADD:
1198 icon = m_hAddedIcon;
1199 break;
1200 case TScreenedViewLine::ICN_CONFLICT:
1201 icon = m_hConflictedIcon;
1202 break;
1203 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1204 icon = m_hConflictedIgnoredIcon;
1205 break;
1206 case TScreenedViewLine::ICN_REMOVED:
1207 icon = m_hRemovedIcon;
1208 break;
1209 case TScreenedViewLine::ICN_MOVED:
1210 icon = m_hMovedIcon;
1211 break;
1215 if (icon)
1217 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1219 if ((m_bViewLinenumbers)&&(m_nDigits))
1221 int nSubLine = GetSubLineOffset(nLineIndex);
1222 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1223 CString sLinenumber;
1224 if (bIsFirstSubline)
1226 CString sLinenumberFormat;
1227 int nLineNumber = GetLineNumber(nLineIndex);
1228 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1230 // TODO: do not show if there is no number hidden
1231 // TODO: show number if there is only one
1232 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1233 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1235 else if (nLineNumber >= 0)
1237 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1238 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1240 else if (m_pMainFrame->m_bWrapLines)
1242 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1243 sLinenumber.Format(sLinenumberFormat, _T("·"));
1245 if (!sLinenumber.IsEmpty())
1247 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1248 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1250 pdc->SelectObject(GetFont());
1251 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1258 int CBaseView::GetMarginWidth()
1260 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1262 if (m_nDigits <= 0)
1264 int nLength = (int)m_pViewData->GetCount();
1265 // find out how many digits are needed to show the highest line number
1266 CString sMax;
1267 sMax.Format(_T("%d"), nLength);
1268 m_nDigits = sMax.GetLength();
1270 int nWidth = GetCharWidth();
1271 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1273 return MARGINWIDTH;
1276 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1278 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1279 COLORREF crBk, crFg;
1280 if (IsBottomViewGood())
1282 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1283 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1285 else
1287 DiffStates state = DIFFSTATE_REMOVED;
1288 if (this == m_pwndRight)
1290 state = DIFFSTATE_ADDED;
1292 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1294 pdc->SetBkColor(crBk);
1295 pdc->FillSolidRect(textrect, crBk);
1297 pdc->SetTextColor(crFg);
1299 pdc->SelectObject(GetFont(FALSE, TRUE));
1301 CString sViewTitle;
1302 if (IsModified())
1304 sViewTitle = _T("* ") + m_sWindowName;
1306 else
1308 sViewTitle = m_sWindowName;
1310 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1311 if (nStringLength > rect.Width())
1313 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1314 sViewTitle = m_sWindowName.Mid(offset);
1316 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1317 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1318 if (this->GetFocus() == this)
1319 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1320 else
1321 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1324 void CBaseView::OnDraw(CDC * pDC)
1326 CRect rcClient;
1327 GetClientRect(rcClient);
1329 int nLineCount = GetLineCount();
1330 int nLineHeight = GetLineHeight();
1332 CDC cacheDC;
1333 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1334 if (m_pCacheBitmap == NULL)
1336 m_pCacheBitmap = new CBitmap;
1337 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1339 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1341 DrawHeader(pDC, rcClient);
1343 CRect rcLine;
1344 rcLine = rcClient;
1345 rcLine.top += nLineHeight+HEADERHEIGHT;
1346 rcLine.bottom = rcLine.top + nLineHeight;
1347 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1348 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1350 int nCurrentLine = m_nTopLine;
1351 bool bBeyondFileLineCached = false;
1352 while (rcLine.top < rcClient.bottom)
1354 if (nCurrentLine < nLineCount)
1356 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1357 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1358 bBeyondFileLineCached = false;
1360 else if (!bBeyondFileLineCached)
1362 DrawMargin(&cacheDC, rcCacheMargin, -1);
1363 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1364 bBeyondFileLineCached = true;
1367 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1369 nCurrentLine ++;
1370 rcLine.OffsetRect(0, nLineHeight);
1373 cacheDC.SelectObject(pOldBitmap);
1374 cacheDC.DeleteDC();
1377 bool CBaseView::IsStateConflicted(DiffStates state)
1379 switch (state)
1381 case DIFFSTATE_CONFLICTED:
1382 case DIFFSTATE_CONFLICTED_IGNORED:
1383 case DIFFSTATE_CONFLICTEMPTY:
1384 case DIFFSTATE_CONFLICTADDED:
1385 return true;
1387 return false;
1390 bool CBaseView::IsStateEmpty(DiffStates state)
1392 switch (state)
1394 case DIFFSTATE_CONFLICTEMPTY:
1395 case DIFFSTATE_UNKNOWN:
1396 case DIFFSTATE_EMPTY:
1397 return true;
1399 return false;
1402 bool CBaseView::IsStateRemoved(DiffStates state)
1404 switch (state)
1406 case DIFFSTATE_REMOVED:
1407 case DIFFSTATE_MOVED_FROM:
1408 case DIFFSTATE_THEIRSREMOVED:
1409 case DIFFSTATE_YOURSREMOVED:
1410 case DIFFSTATE_IDENTICALREMOVED:
1411 return true;
1413 return false;
1416 DiffStates CBaseView::ResolveState(DiffStates state)
1418 if (IsStateConflicted(state))
1420 if (state == DIFFSTATE_CONFLICTEMPTY)
1421 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1422 else
1423 return DIFFSTATE_CONFLICTRESOLVED;
1425 return state;
1429 bool CBaseView::IsLineEmpty(int nLineIndex)
1431 if (m_pViewData == 0)
1432 return FALSE;
1433 int nViewLine = GetViewLineForScreen(nLineIndex);
1434 return IsViewLineEmpty(nViewLine);
1437 bool CBaseView::IsViewLineEmpty(int nViewLine)
1439 if (m_pViewData == 0)
1440 return FALSE;
1441 const DiffStates state = m_pViewData->GetState(nViewLine);
1442 return IsStateEmpty(state);
1445 bool CBaseView::IsLineRemoved(int nLineIndex)
1447 if (m_pViewData == 0)
1448 return FALSE;
1449 int nViewLine = GetViewLineForScreen(nLineIndex);
1450 return IsViewLineRemoved(nViewLine);
1453 bool CBaseView::IsViewLineRemoved(int nViewLine)
1455 if (m_pViewData == 0)
1456 return FALSE;
1457 const DiffStates state = m_pViewData->GetState(nViewLine);
1458 return IsStateRemoved(state);
1461 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1463 if (m_pViewData == 0)
1464 return false;
1465 const DiffStates state = m_pViewData->GetState(nLineIndex);
1466 return IsStateConflicted(state);
1469 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1471 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1474 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1476 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1479 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1481 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1482 return;
1483 int viewLine = GetViewLineForScreen(nLineIndex);
1484 EOL ending = m_pViewData->GetLineEnding(viewLine);
1485 if (m_bIconLFs)
1487 HICON hEndingIcon = NULL;
1488 switch (ending)
1490 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1491 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1492 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1493 default: return;
1495 if (origin.x < (rc.left-GetCharWidth()))
1496 return;
1497 // If EOL style has changed, color end-of-line markers as inline differences.
1499 m_bShowInlineDiff && m_pOtherViewData &&
1500 (viewLine < m_pOtherViewData->GetCount()) &&
1501 (ending != EOL_NOENDING) &&
1502 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1503 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1506 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1509 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1511 else
1513 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1514 CPen * oldpen = pDC->SelectObject(&pen);
1515 int yMiddle = origin.y + rc.Height()/2;
1516 int xMiddle = origin.x+GetCharWidth()/2;
1517 bool bMultiline = false;
1518 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1520 if (GetLineLength(nLineIndex+1))
1522 // multiline
1523 bMultiline = true;
1524 pDC->MoveTo(origin.x, yMiddle-2);
1525 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1526 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1527 pDC->LineTo(origin.x, yMiddle+2);
1529 else if (GetLineLength(nLineIndex) == 0)
1530 bMultiline = true;
1532 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1533 bMultiline = true;
1535 if (!bMultiline)
1537 switch (ending)
1539 case EOL_AUTOLINE:
1540 case EOL_CRLF:
1541 // arrow from top to middle+2, then left
1542 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1543 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1544 case EOL_CR:
1545 // arrow from right to left
1546 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1547 pDC->LineTo(origin.x, yMiddle);
1548 pDC->LineTo(origin.x+4, yMiddle+4);
1549 pDC->MoveTo(origin.x, yMiddle);
1550 pDC->LineTo(origin.x+4, yMiddle-4);
1551 break;
1552 case EOL_LFCR:
1553 // from right-upper to left then down
1554 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1555 pDC->LineTo(xMiddle, yMiddle-2);
1556 pDC->LineTo(xMiddle, rc.bottom-1);
1557 pDC->LineTo(xMiddle+4, rc.bottom-5);
1558 pDC->MoveTo(xMiddle, rc.bottom-1);
1559 pDC->LineTo(xMiddle-4, rc.bottom-5);
1560 break;
1561 case EOL_LF:
1562 // arrow from top to bottom
1563 pDC->MoveTo(xMiddle, rc.top);
1564 pDC->LineTo(xMiddle, rc.bottom-1);
1565 pDC->LineTo(xMiddle+4, rc.bottom-5);
1566 pDC->MoveTo(xMiddle, rc.bottom-1);
1567 pDC->LineTo(xMiddle-4, rc.bottom-5);
1568 break;
1569 case EOL_FF: // Form Feed, U+000C
1570 case EOL_NEL: // Next Line, U+0085
1571 case EOL_LS: // Line Separator, U+2028
1572 case EOL_PS: // Paragraph Separator, U+2029
1573 // draw a horizontal line at the bottom of this line
1574 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1575 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1576 pDC->LineTo(origin.x, rc.bottom-2);
1577 pDC->LineTo(origin.x+5, rc.bottom-2);
1578 pDC->MoveTo(origin.x, rc.bottom-2);
1579 pDC->LineTo(origin.x+1, rc.bottom-6);
1580 break;
1581 default: // other EOLs
1582 // arrow from top right to bottom left
1583 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1584 pDC->LineTo(origin.x, rc.bottom-1);
1585 pDC->LineTo(origin.x+5, rc.bottom-2);
1586 pDC->MoveTo(origin.x, rc.bottom-1);
1587 pDC->LineTo(origin.x+1, rc.bottom-6);
1588 break;
1589 case EOL_NOENDING:
1590 break;
1593 pDC->SelectObject(oldpen);
1597 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1599 if (!m_bShowSelection)
1600 return;
1602 int nSelBlockStart;
1603 int nSelBlockEnd;
1604 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1605 return;
1607 const int THICKNESS = 2;
1608 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1610 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1611 int nSubLine = GetSubLineOffset(nLineIndex);
1612 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1613 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1615 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1618 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1619 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1621 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1625 void CBaseView::DrawTextLine(
1626 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1628 ASSERT(nLineIndex < GetLineCount());
1629 int nViewLine = GetViewLineForScreen(nLineIndex);
1630 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1632 LineColors lineCols = GetLineColors(nViewLine);
1634 CString sViewLine = GetViewLineChars(nViewLine);
1635 // mark selection
1636 if (m_bShowSelection && HasTextSelection())
1638 // has this line selection ?
1639 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1641 int nViewLineLength = sViewLine.GetLength();
1643 // first suppose the whole line is selected
1644 int selectedStart = 0;
1645 int selectedEnd = nViewLineLength;
1647 // the view line is partially selected
1648 if (m_ptSelectionViewPosStart.y == nViewLine)
1650 selectedStart = m_ptSelectionViewPosStart.x;
1653 if (m_ptSelectionViewPosEnd.y == nViewLine)
1655 selectedEnd = m_ptSelectionViewPosEnd.x;
1657 // apply selection coloring
1658 // First enforce start and end point
1659 lineCols.SplitBlock(selectedStart);
1660 lineCols.SplitBlock(selectedEnd);
1661 // change color of affected parts
1662 long intenseColorScale = m_bFocused ? 70 : 30;
1663 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1664 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1666 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1667 if (it->second.shot == it->second.background)
1669 it->second.shot = crBk;
1671 it->second.background = crBk;
1672 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1677 // TODO: remove duplicate from selection and mark
1678 if (!m_sMarkedWord.IsEmpty())
1680 int nMarkLength = m_sMarkedWord.GetLength();
1681 //int nViewLineLength = sViewLine.GetLength();
1682 const TCHAR * text = sViewLine;
1683 const TCHAR * findText = text;
1684 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1686 int nMarkStart = static_cast<int>(findText - text);
1687 int nMarkEnd = nMarkStart + nMarkLength;
1688 // First enforce start and end point
1689 lineCols.SplitBlock(nMarkStart);
1690 lineCols.SplitBlock(nMarkEnd);
1691 // change color of affected parts
1692 const long int nIntenseColorScale = 200;
1693 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1694 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1696 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1697 if (it->second.shot == it->second.background)
1699 it->second.shot = crBk;
1701 it->second.background = crBk;
1702 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1704 findText += nMarkLength;
1707 if (!m_sFindText.IsEmpty())
1709 int nMarkStart = 0;
1710 int nMarkEnd = 0;
1711 int nStringPos = nMarkStart;
1712 CString searchLine = sViewLine;
1713 if (!m_bMatchCase)
1714 searchLine.MakeLower();
1715 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1717 // First enforce start and end point
1718 lineCols.SplitBlock(nMarkStart+nStringPos);
1719 lineCols.SplitBlock(nMarkEnd+nStringPos);
1720 // change color of affected parts
1721 const long int nIntenseColorScale = 30;
1722 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1723 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1725 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1726 if (it->second.shot == it->second.background)
1728 it->second.shot = crBk;
1730 it->second.background = crBk;
1731 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1733 searchLine = searchLine.Mid(nMarkEnd);
1734 nStringPos = nMarkEnd;
1738 // @ this point we may cache data for next line which may be same in wrapped mode
1740 int nTextOffset = 0;
1741 int nSubline = GetSubLineOffset(nLineIndex);
1742 for (int n=0; n<nSubline; n++)
1744 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1745 nTextOffset += sLine.GetLength();
1748 CString sLine = GetLineChars(nLineIndex);
1749 int nLineLength = sLine.GetLength();
1750 CString sLineExp = ExpandChars(sLine);
1751 LPCTSTR textExp = sLineExp;
1752 //int nLineLengthExp = sLineExp.GetLength();
1753 int nStartExp = 0;
1754 int nLeft = coords.x;
1755 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1757 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1758 ++itEnd;
1759 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1760 int nEnd = nLineLength;
1761 if (itEnd != lineCols.end())
1763 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1765 int nBlockLength = nEnd - nStart;
1766 if (nBlockLength > 0 && nEnd>=0)
1768 pDC->SetBkColor(itStart->second.background);
1769 pDC->SetTextColor(itStart->second.text);
1770 int nEndExp = CountExpandedChars(sLine, nEnd);
1771 int nTextLength = nEndExp - nStartExp;
1772 LPCTSTR p_zBlockText = textExp + nStartExp;
1773 SIZE Size;
1774 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1775 int nRight = nLeft + Size.cx;
1776 if ((nRight > rc.left) && (nLeft < rc.right))
1778 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1779 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1780 // is 4094 (4095 doesn't work anymore).
1781 // So we limit the length here to that 4094 chars.
1782 // In case we're scrolled to the right, there's no need to draw the string
1783 // from way outside our window, so we also offset the drawing to the start of the window.
1784 // This reduces the string length as well.
1785 int offset = 0;
1786 int leftcoord = nLeft;
1787 if (nLeft < 0)
1789 offset = (-nLeft/GetCharWidth());
1790 nTextLength -= offset;
1791 leftcoord = nLeft % GetCharWidth();
1794 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1795 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1797 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1800 nLeft = nRight;
1801 coords.x = nRight;
1802 nStartExp = nEndExp;
1807 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1809 if (nLineIndex >= GetLineCount())
1810 nLineIndex = -1;
1811 ASSERT(nLineIndex >= -1);
1813 if ((nLineIndex == -1) || !m_pViewData)
1815 // Draw line beyond the text
1816 COLORREF crBkgnd, crText;
1817 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1818 pDC->FillSolidRect(rc, crBkgnd);
1819 return;
1822 int viewLine = GetViewLineForScreen(nLineIndex);
1823 if (m_pMainFrame->m_bCollapsed)
1825 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1827 COLORREF crBkgnd, crText;
1828 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1829 pDC->FillSolidRect(rc, crBkgnd);
1831 const int THICKNESS = 2;
1832 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1833 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1834 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1835 pDC->SetBkColor(crBkgnd);
1836 CRect rect = rc;
1837 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1838 return;
1842 DiffStates diffState = m_pViewData->GetState(viewLine);
1843 COLORREF crBkgnd, crText;
1844 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1846 if (diffState == DIFFSTATE_CONFLICTED)
1848 // conflicted lines are shown without 'text' on them
1849 CRect rect = rc;
1850 pDC->FillSolidRect(rc, crBkgnd);
1851 // now draw some faint text patterns
1852 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1853 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1854 DrawBlockLine(pDC, rc, nLineIndex);
1855 return;
1858 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1859 CString sLine = GetLineChars(nLineIndex);
1860 if (sLine.IsEmpty())
1862 pDC->FillSolidRect(rc, crBkgnd);
1863 DrawBlockLine(pDC, rc, nLineIndex);
1864 DrawLineEnding(pDC, rc, nLineIndex, origin);
1865 return;
1868 CheckOtherView();
1870 // Draw the line
1872 pDC->SelectObject(GetFont(FALSE, FALSE));
1874 DrawTextLine(pDC, rc, nLineIndex, origin);
1876 // draw white space after the end of line
1877 CRect frect = rc;
1878 if (origin.x > frect.left)
1879 frect.left = origin.x;
1880 if (frect.right > frect.left)
1881 pDC->FillSolidRect(frect, crBkgnd);
1883 // draw the whitespace chars
1884 LPCTSTR pszChars = (LPCWSTR)sLine;
1885 if (m_bViewWhitespace)
1887 int xpos = 0;
1888 int y = rc.top + (rc.bottom-rc.top)/2;
1890 int nActualOffset = 0;
1891 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1893 if (*pszChars == _T('\t'))
1894 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1895 else
1896 nActualOffset++;
1897 pszChars++;
1899 if (nActualOffset > m_nOffsetChar)
1900 pszChars--;
1902 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1903 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1904 while (*pszChars)
1906 switch (*pszChars)
1908 case _T('\t'):
1910 // draw an arrow
1911 CPen * oldPen = pDC->SelectObject(&pen);
1912 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1913 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1914 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1915 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1916 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1917 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1918 xpos += nSpaces;
1919 pDC->SelectObject(oldPen);
1921 break;
1922 case _T(' '):
1924 // draw a small dot
1925 CPen * oldPen = pDC->SelectObject(&pen2);
1926 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1927 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1928 xpos++;
1929 pDC->SelectObject(oldPen);
1931 break;
1932 default:
1933 xpos++;
1934 break;
1936 pszChars++;
1939 DrawBlockLine(pDC, rc, nLineIndex);
1940 if (origin.x >= rc.left)
1941 DrawLineEnding(pDC, rc, nLineIndex, origin);
1944 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
1946 if (nCount <= 0)
1948 line = _T("");
1949 return;
1952 int nTabSize = GetTabSize();
1954 int nActualOffset = CountExpandedChars(sLine, nOffset);
1956 LPCTSTR pszChars = (LPCWSTR)sLine;
1957 pszChars += nOffset;
1958 int nLength = nCount;
1960 int nTabCount = 0;
1961 for (int i=0; i<nLength; i++)
1963 if (pszChars[i] == _T('\t'))
1964 nTabCount ++;
1967 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
1968 int nCurPos = 0;
1969 if (nTabCount > 0 || m_bViewWhitespace)
1971 for (int i=0; i<nLength; i++)
1973 if (pszChars[i] == _T('\t'))
1975 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
1976 while (nSpaces > 0)
1978 pszBuf[nCurPos ++] = _T(' ');
1979 nSpaces --;
1982 else
1984 pszBuf[nCurPos] = pszChars[i];
1985 nCurPos ++;
1989 else
1991 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
1992 nCurPos = nLength;
1994 pszBuf[nCurPos] = 0;
1995 line.ReleaseBuffer();
1998 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2000 CString sRet;
2001 int nLength = sLine.GetLength();
2002 ExpandChars(sLine, nOffset, nLength, sRet);
2003 return sRet;
2006 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2008 int nTabSize = GetTabSize();
2010 int nActualOffset = 0;
2011 for (int i=0; i<nLength; i++)
2013 if (sLine[i] == _T('\t'))
2014 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2015 else
2016 nActualOffset ++;
2018 return nActualOffset;
2021 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2023 if (m_pwndLeft)
2024 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2025 if (m_pwndRight)
2026 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2027 if (m_pwndBottom)
2028 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2029 if (m_pwndLocator)
2030 m_pwndLocator->Invalidate();
2033 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2035 //almost the same as ScrollAllToLine, but try to put the line in the
2036 //middle of the view, not on top
2037 int nNewTopLine = nNewLine - GetScreenLines()/2;
2038 if (nNewTopLine < 0)
2039 nNewTopLine = 0;
2040 if (nNewTopLine >= (int)m_Screen2View.size())
2041 nNewTopLine = (int)m_Screen2View.size()-1;
2042 if (bAll)
2043 ScrollAllToLine(nNewTopLine);
2044 else
2045 ScrollToLine(nNewTopLine);
2048 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2050 return TRUE;
2053 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2055 if (CView::OnCreate(lpCreateStruct) == -1)
2056 return -1;
2058 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
2059 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2060 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2061 m_lfBaseFont.lfHeight = 0;
2062 m_lfBaseFont.lfWeight = FW_NORMAL;
2063 m_lfBaseFont.lfItalic = FALSE;
2064 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2065 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2066 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2067 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2068 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2070 return 0;
2073 void CBaseView::OnDestroy()
2075 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2077 m_pFindDialog->SendMessage(WM_CLOSE);
2078 return;
2080 CView::OnDestroy();
2081 DeleteFonts();
2082 ReleaseBitmap();
2085 void CBaseView::OnSize(UINT nType, int cx, int cy)
2087 CView::OnSize(nType, cx, cy);
2088 ReleaseBitmap();
2090 m_nScreenLines = -1;
2091 m_nScreenChars = -1;
2092 if (m_nLastScreenChars != GetScreenChars())
2094 BuildAllScreen2ViewVector();
2095 m_nLastScreenChars = m_nScreenChars;
2096 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2098 // if we're in wrap mode, the line wrapping most likely changed
2099 // and that means we have to redraw the whole window, not just the
2100 // scrolled part.
2101 Invalidate(FALSE);
2103 else
2105 // make sure the view header is redrawn
2106 CRect rcScroll;
2107 GetClientRect(&rcScroll);
2108 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2109 InvalidateRect(&rcScroll, FALSE);
2112 else
2114 // make sure the view header is redrawn
2115 CRect rcScroll;
2116 GetClientRect(&rcScroll);
2117 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2118 InvalidateRect(&rcScroll, FALSE);
2120 UpdateLocator();
2121 RecalcVertScrollBar();
2122 RecalcHorzScrollBar();
2124 UpdateCaret();
2127 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2129 if (m_pwndLeft)
2130 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2131 if (m_pwndRight)
2132 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2133 if (m_pwndBottom)
2134 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2135 if (m_pwndLocator)
2136 m_pwndLocator->Invalidate();
2137 return CView::OnMouseWheel(nFlags, zDelta, pt);
2140 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2142 if (m_pwndLeft)
2143 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2144 if (m_pwndRight)
2145 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2146 if (m_pwndBottom)
2147 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2148 if (m_pwndLocator)
2149 m_pwndLocator->Invalidate();
2152 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2154 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2155 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2157 if (bControl || bShift)
2159 if (m_pMainFrame->m_bWrapLines)
2160 return;
2161 // Ctrl-Wheel scrolls sideways
2162 ScrollSide(-zDelta/30);
2164 else
2166 ScrollVertical(zDelta);
2170 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2172 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2173 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2175 if (bControl || bShift)
2177 ScrollVertical(zDelta);
2179 else
2181 if (m_pMainFrame->m_bWrapLines)
2182 return;
2183 // Ctrl-Wheel scrolls sideways
2184 ScrollSide(-zDelta/30);
2188 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2190 if (nHitTest == HTCLIENT)
2192 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2194 if (m_nMouseLine < (int)m_Screen2View.size())
2196 if (m_nMouseLine >= 0)
2198 int viewLine = GetViewLineForScreen(m_nMouseLine);
2199 if (viewLine < m_pViewData->GetCount())
2201 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2203 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2204 return TRUE;
2210 if (m_mouseInMargin)
2212 ::SetCursor(m_margincursor);
2213 return TRUE;
2215 if (m_nMouseLine >= 0)
2217 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2218 return TRUE;
2221 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2222 return TRUE;
2224 return CView::OnSetCursor(pWnd, nHitTest, message);
2227 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2229 CView::OnKillFocus(pNewWnd);
2230 m_bFocused = FALSE;
2231 UpdateCaret();
2232 Invalidate();
2235 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2237 CView::OnSetFocus(pOldWnd);
2238 m_bFocused = TRUE;
2239 UpdateCaret();
2240 Invalidate();
2243 int CBaseView::GetLineFromPoint(CPoint point)
2245 ScreenToClient(&point);
2246 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2249 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2251 if (!this->IsWindowVisible())
2252 return;
2254 CIconMenu popup;
2255 if (!popup.CreatePopupMenu())
2256 return;
2258 AddContextItems(popup, state);
2260 CompensateForKeyboard(point);
2262 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2263 ResetUndoStep();
2264 switch (cmd)
2266 // 2-pane view commands; target is right view
2267 case POPUPCOMMAND_USELEFTBLOCK:
2268 m_pwndRight->UseLeftBlock();
2269 break;
2270 case POPUPCOMMAND_USELEFTFILE:
2271 m_pwndRight->UseLeftFile();
2272 break;
2273 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2274 m_pwndRight->UseBothLeftFirst();
2275 break;
2276 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2277 m_pwndRight->UseBothRightFirst();
2278 break;
2279 // 3-pane view commands; target is bottom view
2280 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2281 m_pwndBottom->UseBothRightFirst();
2282 break;
2283 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2284 m_pwndBottom->UseBothLeftFirst();
2285 break;
2286 case POPUPCOMMAND_USEYOURBLOCK:
2287 m_pwndBottom->UseRightBlock();
2288 break;
2289 case POPUPCOMMAND_USEYOURFILE:
2290 m_pwndBottom->UseRightFile();
2291 break;
2292 case POPUPCOMMAND_USETHEIRBLOCK:
2293 m_pwndBottom->UseLeftBlock();
2294 break;
2295 case POPUPCOMMAND_USETHEIRFILE:
2296 m_pwndBottom->UseLeftFile();
2297 break;
2298 // copy, cut and paste commands
2299 case ID_EDIT_COPY:
2300 OnEditCopy();
2301 break;
2302 case ID_EDIT_CUT:
2303 OnEditCut();
2304 break;
2305 case ID_EDIT_PASTE:
2306 OnEditPaste();
2307 break;
2308 default:
2309 return;
2310 } // switch (cmd)
2311 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2312 return;
2315 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2317 if (!m_pViewData)
2318 return;
2320 int nViewBlockStart = -1;
2321 int nViewBlockEnd = -1;
2322 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2323 if ((point.x >= 0) && (point.y >= 0))
2325 int nLine = GetLineFromPoint(point)-1;
2326 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2328 int nViewLine = GetViewLineForScreen(nLine);
2329 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2331 ClearSelection(); // Clear text-copy selection
2333 nViewBlockStart = nViewLine;
2334 nViewBlockEnd = nViewLine;
2335 DiffStates state = m_pViewData->GetState(nViewLine);
2336 while (nViewBlockStart > 0)
2338 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2339 if (!LinesInOneChange(-1, state, lineState))
2340 break;
2341 nViewBlockStart--;
2344 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2346 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2347 if (!LinesInOneChange(1, state, lineState))
2348 break;
2349 nViewBlockEnd++;
2352 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2353 UpdateCaretPosition(point);
2358 // FixSelection(); fix selection range
2359 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2360 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2362 DiffStates state = DIFFSTATE_UNKNOWN;
2363 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2365 // find a more 'relevant' state in the selection
2366 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2368 state = m_pViewData->GetState(i);
2369 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2370 break;
2373 OnContextMenu(point, state);
2376 void CBaseView::RefreshViews()
2378 if (m_pwndLeft)
2380 m_pwndLeft->UpdateStatusBar();
2381 m_pwndLeft->Invalidate();
2383 if (m_pwndRight)
2385 m_pwndRight->UpdateStatusBar();
2386 m_pwndRight->Invalidate();
2388 if (m_pwndBottom)
2390 m_pwndBottom->UpdateStatusBar();
2391 m_pwndBottom->Invalidate();
2393 if (m_pwndLocator)
2394 m_pwndLocator->Invalidate();
2397 void CBaseView::GoToFirstDifference()
2399 SetCaretToFirstViewLine();
2400 SelectNextBlock(1, false, false);
2403 void CBaseView::GoToFirstConflict()
2405 SetCaretToFirstViewLine();
2406 SelectNextBlock(1, true, false);
2409 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2411 ClearSelection();
2412 SetupAllSelection(nStart, max(nStart, nEnd));
2414 UpdateCaretPosition(SetupPoint(0, nStart));
2415 Invalidate();
2418 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2420 ClearSelection();
2421 SetupAllViewSelection(nStart, max(nStart, nEnd));
2423 UpdateCaretViewPosition(SetupPoint(0, nStart));
2424 Invalidate();
2427 void CBaseView::SetupAllViewSelection(int start, int end)
2429 SetupViewSelection(m_pwndBottom, start, end);
2430 SetupViewSelection(m_pwndLeft, start, end);
2431 SetupViewSelection(m_pwndRight, start, end);
2434 void CBaseView::SetupAllSelection(int start, int end)
2436 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2439 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2441 void CBaseView::SetupSelection(int start, int end)
2443 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2446 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2448 if (!IsViewGood(view))
2449 return;
2450 view->SetupViewSelection(start, end);
2453 void CBaseView::SetupViewSelection(int start, int end)
2455 // clear text selection before setting line selection ?
2456 m_nSelViewBlockStart = start;
2457 m_nSelViewBlockEnd = end;
2458 Invalidate();
2462 void CBaseView::OnMergePreviousconflict()
2464 SelectNextBlock(-1, true);
2467 void CBaseView::OnMergeNextconflict()
2469 SelectNextBlock(1, true);
2472 void CBaseView::OnMergeNextdifference()
2474 SelectNextBlock(1, false);
2477 void CBaseView::OnMergePreviousdifference()
2479 SelectNextBlock(-1, false);
2482 bool CBaseView::HasNextConflict()
2484 return SelectNextBlock(1, true, true, true);
2487 bool CBaseView::HasPrevConflict()
2489 return SelectNextBlock(-1, true, true, true);
2492 bool CBaseView::HasNextDiff()
2494 return SelectNextBlock(1, false, true, true);
2497 bool CBaseView::HasPrevDiff()
2499 return SelectNextBlock(-1, false, true, true);
2502 bool CBaseView::LinesInOneChange(int direction,
2503 DiffStates initialLineState, DiffStates currentLineState)
2505 // Checks whether all the adjacent lines starting from the initial line
2506 // and up to the current line form the single change
2508 // Do not distinguish between moved and added/removed lines
2509 if (initialLineState == DIFFSTATE_MOVED_TO)
2510 initialLineState = DIFFSTATE_ADDED;
2511 if (initialLineState == DIFFSTATE_MOVED_FROM)
2512 initialLineState = DIFFSTATE_REMOVED;
2513 if (currentLineState == DIFFSTATE_MOVED_TO)
2514 currentLineState = DIFFSTATE_ADDED;
2515 if (currentLineState == DIFFSTATE_MOVED_FROM)
2516 currentLineState = DIFFSTATE_REMOVED;
2518 // First of all, if the two lines have identical states, they surely
2519 // belong to one change.
2520 if (initialLineState == currentLineState)
2521 return true;
2523 // Either we move down and initial line state is "added" or "removed" and
2524 // current line state is "empty"...
2525 if (direction > 0)
2527 if (currentLineState == DIFFSTATE_EMPTY)
2529 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2530 return true;
2532 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2533 return true;
2535 // ...or we move up and initial line state is "empty" and current line
2536 // state is "added" or "removed".
2537 if (direction < 0)
2539 if (initialLineState == DIFFSTATE_EMPTY)
2541 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2542 return true;
2544 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2545 return true;
2547 return false;
2550 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2552 if (! m_pViewData)
2553 return false;
2555 const int linesCount = (int)m_Screen2View.size();
2556 if(linesCount == 0)
2557 return false;
2559 int nCenterPos = GetCaretPosition().y;
2560 int nLimit = -1;
2561 if (nDirection > 0)
2562 nLimit = linesCount;
2564 if (nCenterPos >= linesCount)
2565 nCenterPos = linesCount-1;
2567 if (bSkipEndOfCurrentBlock)
2569 // Find end of current block
2570 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2571 while (nCenterPos != nLimit)
2573 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2574 if (!LinesInOneChange(nDirection, state, lineState))
2575 break;
2576 nCenterPos += nDirection;
2580 // Find next diff/conflict block
2581 while (nCenterPos != nLimit)
2583 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2584 if (!bConflict &&
2585 (linestate != DIFFSTATE_NORMAL) &&
2586 (linestate != DIFFSTATE_UNKNOWN))
2588 break;
2590 if (bConflict &&
2591 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2592 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2593 (linestate == DIFFSTATE_CONFLICTED) ||
2594 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2596 break;
2599 nCenterPos += nDirection;
2601 if (nCenterPos == nLimit)
2602 return false;
2603 if (dryrun)
2604 return (nCenterPos != nLimit);
2606 // Find end of new block
2607 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2608 int nBlockEnd = nCenterPos;
2609 const int maxAllowedLine = nLimit-nDirection;
2610 while (nBlockEnd != maxAllowedLine)
2612 const int lineIndex = nBlockEnd + nDirection;
2613 if (lineIndex >= linesCount)
2614 break;
2615 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2616 if (!LinesInOneChange(nDirection, state, lineState))
2617 break;
2618 nBlockEnd += nDirection;
2621 int nTopPos = nCenterPos - (GetScreenLines()/2);
2622 if (nTopPos < 0)
2623 nTopPos = 0;
2625 POINT ptCaretPos = {0, nCenterPos};
2626 SetCaretPosition(ptCaretPos);
2627 ClearSelection();
2628 if (nDirection > 0)
2629 SetupAllSelection(nCenterPos, nBlockEnd);
2630 else
2631 SetupAllSelection(nBlockEnd, nCenterPos);
2633 ScrollAllToLine(nTopPos, FALSE);
2634 RecalcAllVertScrollBars(TRUE);
2635 SetCaretToLineStart();
2636 EnsureCaretVisible();
2637 OnNavigateNextinlinediff();
2639 UpdateViewsCaretPosition();
2640 UpdateCaret();
2641 ShowDiffLines(nCenterPos);
2642 return true;
2645 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2647 if (pNMHDR->idFrom != (UINT)m_hWnd)
2648 return FALSE;
2650 CString strTipText;
2651 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2653 DWORD pos = GetMessagePos();
2654 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2655 ScreenToClient(&point);
2656 const int nLine = GetButtonEventLineIndex(point);
2658 if (nLine >= 0)
2660 int nViewLine = GetViewLineForScreen(nLine);
2661 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2663 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)
2665 strTipText.Format(IDS_MOVED_TO_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2667 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO)
2669 strTipText.Format(IDS_MOVED_FROM_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2675 *pResult = 0;
2676 if (strTipText.IsEmpty())
2677 return TRUE;
2679 // need to handle both ANSI and UNICODE versions of the message
2680 if (pNMHDR->code == TTN_NEEDTEXTA)
2682 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2683 pTTTA->lpszText = m_szTip;
2684 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2686 else
2688 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2689 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2690 pTTTW->lpszText = m_wszTip;
2693 return TRUE; // message was handled
2696 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2698 CRect rcClient;
2699 GetClientRect(rcClient);
2700 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2701 int marginwidth = MARGINWIDTH;
2702 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2704 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2706 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2708 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2710 // inside the header part of the view (showing the filename)
2711 pTI->hwnd = this->m_hWnd;
2712 this->GetClientRect(&pTI->rect);
2713 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2714 pTI->uId = (UINT)m_hWnd;
2715 pTI->lpszText = LPSTR_TEXTCALLBACK;
2717 // we want multi line tooltips
2718 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2719 if (pToolTip->GetSafeHwnd() != NULL)
2721 pToolTip->SetMaxTipWidth(INT_MAX);
2724 return (textrect.PtInRect(point) ? 1 : 2);
2727 return -1;
2730 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2732 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2733 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2735 switch (nChar)
2737 case VK_TAB:
2738 if ((nChar == '\t') && bControl)
2740 if (this==m_pwndLeft)
2742 if (IsViewGood(m_pwndRight))
2744 m_pwndRight->SetFocus();
2746 else if (IsViewGood(m_pwndBottom))
2748 m_pwndBottom->SetFocus();
2751 else if (this==m_pwndRight)
2753 if (IsViewGood(m_pwndBottom))
2755 m_pwndBottom->SetFocus();
2757 else if (IsViewGood(m_pwndLeft))
2759 m_pwndLeft->SetFocus();
2762 else if (this==m_pwndBottom)
2764 if (IsViewGood(m_pwndLeft))
2766 m_pwndLeft->SetFocus();
2768 else if (IsViewGood(m_pwndRight))
2770 m_pwndRight->SetFocus();
2774 break;
2775 case VK_PRIOR:
2777 POINT ptCaretPos = GetCaretPosition();
2778 ptCaretPos.y -= GetScreenLines();
2779 ptCaretPos.y = max(ptCaretPos.y, 0);
2780 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2781 SetCaretPosition(ptCaretPos);
2782 OnCaretMove(MOVELEFT, bShift);
2783 ShowDiffLines(ptCaretPos.y);
2785 break;
2786 case VK_NEXT:
2788 POINT ptCaretPos = GetCaretPosition();
2789 ptCaretPos.y += GetScreenLines();
2790 if (ptCaretPos.y >= GetLineCount())
2791 ptCaretPos.y = GetLineCount()-1;
2792 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2793 SetCaretPosition(ptCaretPos);
2794 OnCaretMove(MOVERIGHT, bShift);
2795 ShowDiffLines(ptCaretPos.y);
2797 break;
2798 case VK_HOME:
2800 if (bControl)
2802 ScrollAllToLine(0);
2803 SetCaretToViewStart();
2804 m_nCaretGoalPos = 0;
2805 if (bShift)
2806 AdjustSelection(MOVELEFT);
2807 else
2808 ClearSelection();
2809 UpdateCaret();
2811 else
2813 SetCaretToLineStart();
2814 m_nCaretGoalPos = 0;
2815 OnCaretMove(MOVERIGHT, bShift);
2816 ScrollAllToChar(0);
2819 break;
2820 case VK_END:
2822 if (bControl)
2824 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2825 POINT ptCaretPos;
2826 ptCaretPos.y = GetLineCount()-1;
2827 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2828 SetCaretAndGoalPosition(ptCaretPos);
2829 if (bShift)
2830 AdjustSelection(MOVERIGHT);
2831 else
2832 ClearSelection();
2834 else
2836 POINT ptCaretPos = GetCaretPosition();
2837 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2838 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
2840 ptCaretPos.x--;
2842 SetCaretAndGoalPosition(ptCaretPos);
2843 OnCaretMove(bShift);
2846 break;
2847 case VK_BACK:
2848 if (IsWritable())
2850 if (! HasTextSelection())
2852 POINT ptCaretPos = GetCaretPosition();
2853 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
2854 break;
2855 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2856 if (bControl)
2857 MoveCaretWordLeft();
2858 else
2860 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
2864 m_ptSelectionViewPosStart = GetCaretViewPosition();
2866 RemoveSelectedText();
2868 break;
2869 case VK_DELETE:
2870 if (IsWritable())
2872 if (! HasTextSelection())
2874 if (bControl)
2876 m_ptSelectionViewPosStart = GetCaretViewPosition();
2877 MoveCaretWordRight();
2878 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2880 else
2882 if (! MoveCaretRight())
2883 break;
2884 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2885 MoveCaretLeft();
2886 m_ptSelectionViewPosStart = GetCaretViewPosition();
2889 RemoveSelectedText();
2891 break;
2893 CView::OnKeyDown(nChar, nRepCnt, nFlags);
2896 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
2898 const int nClickedLine = GetButtonEventLineIndex(point);
2899 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
2901 POINT ptCaretPos;
2902 ptCaretPos.y = nClickedLine;
2903 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
2904 SetCaretAndGoalPosition(ptCaretPos);
2906 if (nFlags & MK_SHIFT)
2907 AdjustSelection(MOVERIGHT);
2908 else
2910 ClearSelection();
2911 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
2912 if (point.x < GetMarginWidth())
2914 // select the whole line
2915 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
2916 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
2920 UpdateViewsCaretPosition();
2921 Invalidate();
2924 CView::OnLButtonDown(nFlags, point);
2927 enum ECharGroup { // ordered by priority low-to-hi
2928 CHG_UNKNOWN,
2929 CHG_CONTROL, // x00-x08, x0a-x1f
2930 CHG_WHITESPACE, // space tab
2931 CHG_PUNCTUATION, // 0x21-2f, x3a-x40, x5b-x60, x7b-x7f .,:;!?(){}[]/\<> ...
2932 CHG_WORDLETTER, // alpha num _ (others)
2935 ECharGroup GetCharGroup(wchar_t zChar)
2937 if (zChar == ' ' || zChar == '\t' )
2939 return CHG_WHITESPACE;
2941 if (zChar < 0x20)
2943 return CHG_CONTROL;
2945 if ((zChar >= 0x21 && zChar <= 0x2f)
2946 || (zChar >= 0x3a && zChar <= 0x40)
2947 || (zChar >= 0x5b && zChar <= 0x5e)
2948 || (zChar == 0x60)
2949 || (zChar >= 0x7b && zChar <= 0x7f))
2951 return CHG_PUNCTUATION;
2953 return CHG_WORDLETTER;
2956 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
2958 if (m_pViewData == 0) {
2959 CView::OnLButtonDblClk(nFlags, point);
2960 return;
2963 const int nClickedLine = GetButtonEventLineIndex(point);
2964 if ( nClickedLine < 0)
2965 return;
2966 int nViewLine = GetViewLineForScreen(nClickedLine);
2967 if (point.x < GetMarginWidth()) // only if double clicked on the margin
2969 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
2971 if((m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)||
2972 (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO))
2974 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
2975 int screenLine = FindViewLineNumber(movedindex);
2976 int nTop = screenLine - GetScreenLines()/2;
2977 if (nTop < 0)
2978 nTop = 0;
2979 ScrollAllToLine(nTop);
2980 // find and select the whole moved block
2981 int startSel = movedindex;
2982 int endSel = movedindex;
2983 while ((startSel > 0) && ((m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_TO)))
2984 startSel--;
2985 startSel++;
2986 while ((endSel < GetLineCount()) && ((m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_TO)))
2987 endSel++;
2988 endSel--;
2989 m_pOtherView->SetupSelection(startSel, endSel);
2990 return CView::OnLButtonDblClk(nFlags, point);
2994 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
2996 // a double click on a marker expands the hidden text
2997 int i = nViewLine;
2998 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3000 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3001 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3002 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3003 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3004 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3005 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3006 i++;
3008 BuildAllScreen2ViewVector();
3009 if (m_pwndLeft)
3010 m_pwndLeft->Invalidate();
3011 if (m_pwndRight)
3012 m_pwndRight->Invalidate();
3013 if (m_pwndBottom)
3014 m_pwndBottom->Invalidate();
3016 else
3018 POINT ptCaretPos;
3019 ptCaretPos.y = nClickedLine;
3020 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3021 SetCaretPosition(ptCaretPos);
3022 ClearSelection();
3024 POINT ptViewCarret = GetCaretViewPosition();
3025 nViewLine = ptViewCarret.y;
3026 if (nViewLine >= GetViewCount())
3027 return;
3028 CString sLine = GetViewLine(nViewLine);
3029 int nLineLength = sLine.GetLength();
3030 int nBasePos = ptViewCarret.x;
3031 // get target char group
3032 ECharGroup eLeft = CHG_UNKNOWN;
3033 if (nBasePos > 0)
3035 eLeft = GetCharGroup(sLine[nBasePos-1]);
3037 ECharGroup eRight = CHG_UNKNOWN;
3038 if (nBasePos < nLineLength)
3040 eRight = GetCharGroup(sLine[nBasePos]);
3042 ECharGroup eTarget = max(eRight, eLeft);
3043 // find left margin
3044 int nLeft = nBasePos;
3045 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3047 nLeft--;
3049 // get right margin
3050 int nRight = nBasePos;
3051 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3053 nRight++;
3055 // set selection
3056 m_ptSelectionViewPosStart.x = nLeft;
3057 m_ptSelectionViewPosStart.y = nViewLine;
3058 m_ptSelectionViewPosEnd.x = nRight;
3059 m_ptSelectionViewPosEnd.y = nViewLine;
3060 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3061 SetupAllViewSelection(nViewLine, nViewLine);
3062 // set caret
3063 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3064 UpdateViewsCaretPosition();
3065 UpdateGoalPos();
3067 // set mark word
3068 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3069 int nMarkWidth = max(nRight - nLeft, 0);
3070 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3071 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3073 m_sMarkedWord.Empty();
3076 if (m_pwndLeft)
3077 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3078 if (m_pwndRight)
3079 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3080 if (m_pwndBottom)
3081 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3083 Invalidate();
3084 if (m_pwndLocator)
3085 m_pwndLocator->Invalidate();
3088 CView::OnLButtonDblClk(nFlags, point);
3091 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3093 const int nClickedLine = GetButtonEventLineIndex(point);
3094 if (nClickedLine < 0)
3095 return;
3096 POINT ptCaretPos;
3097 ptCaretPos.y = nClickedLine;
3098 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3099 SetCaretAndGoalPosition(ptCaretPos);
3100 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3101 if (m_pwndLeft)
3102 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3103 if (m_pwndRight)
3104 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3105 if (m_pwndBottom)
3106 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3107 ClearSelection();
3108 m_ptSelectionViewPosStart.x = 0;
3109 m_ptSelectionViewPosStart.y = nClickedLine;
3110 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3111 m_ptSelectionViewPosEnd.y = nClickedLine;
3112 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3113 UpdateViewsCaretPosition();
3114 Invalidate();
3115 if (m_pwndLocator)
3116 m_pwndLocator->Invalidate();
3119 void CBaseView::OnEditCopy()
3121 CString sCopyData = GetSelectedText();
3123 if (!sCopyData.IsEmpty())
3125 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3129 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3131 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3133 --m_pMainFrame->m_nMoveMovesToIgnore;
3134 CView::OnMouseMove(nFlags, point);
3135 return;
3137 int nMouseLine = GetButtonEventLineIndex(point);
3138 if (nMouseLine < -1)
3139 nMouseLine = -1;
3140 m_mouseInMargin = point.x < GetMarginWidth();
3142 ShowDiffLines(nMouseLine);
3144 KillTimer(IDT_SCROLLTIMER);
3145 if (nFlags & MK_LBUTTON)
3147 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3148 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3149 if (saveMouseLine < 0)
3150 return;
3151 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3152 if (HasSelection() &&
3153 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3155 POINT ptCaretPos = {charIndex, nMouseLine};
3156 SetCaretAndGoalPosition(ptCaretPos);
3157 AdjustSelection(MOVERIGHT);
3158 Invalidate();
3159 UpdateWindow();
3161 if (nMouseLine < m_nTopLine)
3163 ScrollAllToLine(m_nTopLine-1, TRUE);
3164 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3166 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3168 ScrollAllToLine(m_nTopLine+1, TRUE);
3169 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3171 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3173 ScrollAllSide(-1);
3174 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3176 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3178 ScrollAllSide(1);
3179 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3181 SetCapture();
3185 CView::OnMouseMove(nFlags, point);
3188 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3190 ShowDiffLines(-1);
3191 ReleaseCapture();
3192 KillTimer(IDT_SCROLLTIMER);
3194 __super::OnLButtonUp(nFlags, point);
3197 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3199 if (nIDEvent == IDT_SCROLLTIMER)
3201 POINT point;
3202 GetCursorPos(&point);
3203 ScreenToClient(&point);
3204 int nMouseLine = GetButtonEventLineIndex(point);
3205 if (nMouseLine < -1)
3207 nMouseLine = -1;
3209 if (GetKeyState(VK_LBUTTON)&0x8000)
3211 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3212 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3213 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3214 if (nMouseLine < m_nTopLine)
3216 ScrollAllToLine(m_nTopLine-1, TRUE);
3217 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3219 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3221 ScrollAllToLine(m_nTopLine+1, TRUE);
3222 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3224 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3226 ScrollAllSide(-1);
3227 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3229 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3231 ScrollAllSide(1);
3232 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3238 CView::OnTimer(nIDEvent);
3241 void CBaseView::ShowDiffLines(int nLine)
3243 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3245 m_pwndLineDiffBar->ShowLines(nLine);
3246 nLine = -1;
3247 m_nMouseLine = nLine;
3248 return;
3251 if ((!m_pwndRight)||(!m_pwndLeft))
3252 return;
3253 if(m_pMainFrame->m_bOneWay)
3254 return;
3256 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3257 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3259 if (nLine < 0)
3260 return;
3262 if (nLine != m_nMouseLine)
3264 if (nLine >= GetLineCount())
3265 nLine = -1;
3266 m_nMouseLine = nLine;
3267 m_pwndLineDiffBar->ShowLines(nLine);
3269 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3272 const viewdata& CBaseView::GetEmptyLineData()
3274 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN, -1);
3275 return emptyLine;
3278 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3280 for (int i = 0; i < nCount; i++)
3282 InsertViewData(nFirstView, GetEmptyLineData());
3287 void CBaseView::UpdateCaret()
3289 POINT ptCaretPos = GetCaretPosition();
3290 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3291 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3292 SetCaretPosition(ptCaretPos);
3294 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3296 if (m_bFocused &&
3297 ptCaretPos.y >= m_nTopLine &&
3298 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3299 nCaretOffset >= m_nOffsetChar &&
3300 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3302 CreateSolidCaret(2, GetLineHeight());
3303 SetCaretPos(TextToClient(ptCaretPos));
3304 ShowCaret();
3306 else
3308 HideCaret();
3312 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3314 POINT ptViewPos;
3315 ptViewPos.x = pt.x;
3317 int nSubLine = GetSubLineOffset(pt.y);
3318 if (nSubLine > 0)
3320 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3322 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3326 ptViewPos.y = GetViewLineForScreen(pt.y);
3327 return ptViewPos;
3330 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3332 POINT ptPos;
3333 int nViewLineLenLeft = GetViewLineLength(pt.y);
3334 ptPos.x = min(nViewLineLenLeft, pt.x);
3335 ptPos.y = FindScreenLineForViewLine(pt.y);
3336 if (GetViewLineForScreen(ptPos.y) != pt.y )
3338 ptPos.x = 0;
3340 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3342 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3343 while (nSubLineLength < ptPos.x)
3345 ptPos.x -= nSubLineLength;
3346 nViewLineLenLeft -= nSubLineLength;
3347 ptPos.y++;
3348 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3350 // last pos of non last sub-line go to start of next screen line
3351 // Note: while this works correctly, it's not what a user might expect:
3352 // cursor-right when the caret is before the last char of a wrapped line
3353 // now moves the caret to the next line. But users expect the caret to
3354 // move to the right of the last char instead, and with another cursor-right
3355 // keystroke to move the caret to the next line.
3356 // Basically, this would require to handle two caret positions for the same
3357 // logical position in the line string (one on the last position of the first line,
3358 // one on the first position of the new line. For non-wrapped lines this works
3359 // because there's an 'invisible' newline char at the end of the first line.
3360 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3362 ptPos.x = 0;
3363 ptPos.y++;
3367 return ptPos;
3371 void CBaseView::EnsureCaretVisible()
3373 POINT ptCaretPos = GetCaretPosition();
3374 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3376 if (ptCaretPos.y < m_nTopLine)
3377 ScrollAllToLine(ptCaretPos.y);
3378 int screnLines = GetScreenLines();
3379 if (screnLines)
3381 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3382 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3383 if (nCaretOffset < m_nOffsetChar)
3384 ScrollAllToChar(nCaretOffset);
3385 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3386 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3390 int CBaseView::CalculateActualOffset(const POINT& point)
3392 int nLineIndex = point.y;
3393 int nCharIndex = point.x;
3394 ASSERT(nCharIndex >= 0);
3395 CString sLine = GetLineChars(nLineIndex);
3396 int nLineLength = sLine.GetLength();
3397 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3400 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3402 int nLength = GetLineLength(nLineIndex);
3403 int nSubLine = GetSubLineOffset(nLineIndex);
3404 if (nSubLine>=0)
3406 int nViewLine = GetViewLineForScreen(nLineIndex);
3407 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3409 int nMultilineCount = CountMultiLines(nViewLine);
3410 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3412 nLength--;
3416 CString Line = GetLineChars(nLineIndex);
3417 int nIndex = 0;
3418 int nOffset = 0;
3419 int nTabSize = GetTabSize();
3420 while (nOffset < nActualOffset && nIndex < nLength)
3422 if (Line.GetAt(nIndex) == _T('\t'))
3423 nOffset += (nTabSize - nOffset % nTabSize);
3424 else
3425 ++nOffset;
3426 ++nIndex;
3428 return nIndex;
3431 POINT CBaseView::TextToClient(const POINT& point)
3433 POINT pt;
3434 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3435 pt.y = nOffsetScreenLine * GetLineHeight();
3436 pt.x = CalculateActualOffset(point);
3438 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3439 CDC * pDC = GetDC();
3440 if (pDC)
3442 pDC->SelectObject(GetFont()); // is this right font ?
3443 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3444 CString sLine = GetLineChars(nScreenLine);
3445 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3446 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3447 ReleaseDC(pDC);
3448 } else {
3449 nLeft += pt.x * GetCharWidth();
3452 pt.x = nLeft;
3453 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3454 return pt;
3457 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3459 CView::OnChar(nChar, nRepCnt, nFlags);
3461 if (IsReadonly())
3462 return;
3464 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3465 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3467 return;
3470 if (!m_pViewData) // no data - nothing to do
3471 return;
3473 if (nChar == VK_F16)
3475 // generated by a ctrl+backspace - ignore.
3477 else if ((nChar > 31)||(nChar == VK_TAB))
3479 ResetUndoStep();
3480 RemoveSelectedText();
3481 POINT ptCaretViewPos = GetCaretViewPosition();
3482 int nViewLine = ptCaretViewPos.y;
3483 if ((nViewLine==0)&&(GetViewCount()==0))
3484 OnChar(VK_RETURN, 0, 0);
3485 viewdata lineData = GetViewData(nViewLine);
3486 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3487 if (IsStateEmpty(lineData.state))
3489 // if not last line set EOL
3490 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3492 if (!IsViewLineEmpty(nCheckViewLine))
3494 lineData.ending = lineendings;
3495 break;
3498 // make sure previous (non empty) line have EOL set
3499 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3501 if (!IsViewLineEmpty(nCheckViewLine))
3503 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3505 SetViewLineEnding(nCheckViewLine, lineendings);
3507 break;
3511 lineData.state = DIFFSTATE_EDITED;
3512 bool bNeedRenumber = false;
3513 if (lineData.linenumber == -1)
3515 lineData.linenumber = 0;
3516 bNeedRenumber = true;
3518 SetViewData(nViewLine, lineData);
3519 SaveUndoStep();
3520 BuildAllScreen2ViewVector(nViewLine);
3521 if (bNeedRenumber)
3523 UpdateViewLineNumbers();
3525 MoveCaretRight();
3526 UpdateGoalPos();
3528 else if (nChar == 10)
3530 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3531 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3532 EOL newEOL = EOL_CRLF;
3533 switch (eol)
3535 case EOL_CRLF:
3536 newEOL = EOL_CR;
3537 break;
3538 case EOL_CR:
3539 newEOL = EOL_LF;
3540 break;
3541 case EOL_LF:
3542 newEOL = EOL_CRLF;
3543 break;
3545 if (eol==EOL_NOENDING || eol==newEOL)
3546 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3547 // to add EOL on newly edited empty line hit enter
3548 // don't store into UNDO if no change happened
3549 // and don't mark file as modified
3550 return;
3551 AddUndoViewLine(nViewLine);
3552 m_pViewData->SetLineEnding(nViewLine, newEOL);
3553 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3554 UpdateGoalPos();
3556 else if (nChar == VK_RETURN)
3558 // insert a new, fresh and empty line below the cursor
3559 RemoveSelectedText();
3561 CUndo::GetInstance().BeginGrouping();
3563 POINT ptCaretViewPos = GetCaretViewPosition();
3564 int nViewLine = ptCaretViewPos.y;
3565 int nLeft = ptCaretViewPos.x;
3566 CString sLine = GetViewLineChars(nViewLine);
3567 CString sLineLeft = sLine.Left(nLeft);
3568 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3569 EOL eOriginalEnding = EOL_AUTOLINE;
3570 if (m_pViewData->GetCount() > nViewLine)
3571 eOriginalEnding = GetViewLineEnding(nViewLine);
3573 if (!sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3575 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3576 SetViewData(nViewLine, newFirstLine);
3579 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3580 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN, -1);
3581 InsertViewData(nInsertLine, newLastLine);
3582 SaveUndoStep();
3584 // adds new line everywhere except me
3585 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3587 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3589 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3591 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3593 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3595 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3597 SaveUndoStep();
3599 UpdateViewLineNumbers();
3600 SaveUndoStep();
3601 CUndo::GetInstance().EndGrouping();
3603 BuildAllScreen2ViewVector();
3604 // move the cursor to the new line
3605 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3606 SetCaretAndGoalViewPosition(ptCaretViewPos);
3608 else
3609 return; // Unknown control character -- ignore it.
3610 ClearSelection();
3611 EnsureCaretVisible();
3612 UpdateCaret();
3613 SetModified(true);
3614 Invalidate(FALSE);
3617 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3619 ResetUndoStep();
3620 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3621 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3622 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3623 SaveUndoStep();
3624 RecalcAllVertScrollBars();
3625 Invalidate(FALSE);
3628 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3630 if (m_pViewData == NULL)
3631 return;
3632 int viewLine = nViewLineIndex;
3633 EOL ending = m_pViewData->GetLineEnding(viewLine);
3634 if (ending == EOL_NOENDING)
3636 ending = lineendings;
3638 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN, -1);
3639 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3641 CString sPartLine = GetViewLineChars(nViewLineIndex);
3642 int nPosx = GetCaretPosition().x; // should be view pos ?
3643 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3644 sPartLine = sPartLine.Mid(nPosx);
3645 newLine.sLine = sPartLine;
3647 m_pViewData->InsertData(viewLine+1, newLine);
3648 BuildAllScreen2ViewVector();
3651 void CBaseView::RemoveSelectedText()
3653 if (m_pViewData == NULL)
3654 return;
3655 if (!HasTextSelection())
3656 return;
3658 // fix selection if starts or ends on empty line
3659 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3660 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3663 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3664 SetCaretViewPosition(m_ptSelectionViewPosStart);
3665 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3668 m_ptSelectionViewPosStart = GetCaretViewPosition();
3669 if (!HasTextSelection())
3671 ClearSelection();
3672 return;
3675 // We want to undo the insertion in a single step.
3676 ResetUndoStep();
3677 CUndo::GetInstance().BeginGrouping();
3679 // combine first and last line
3680 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3681 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3682 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3683 oFirstLine.ending = oLastLine.ending;
3684 oFirstLine.state = DIFFSTATE_EDITED;
3685 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3687 // clean up middle lines if any
3688 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3690 viewdata oEmptyLine = GetEmptyLineData();
3691 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3693 SetViewData(nViewLine, oEmptyLine);
3695 SaveUndoStep();
3697 if (CleanEmptyLines())
3699 BuildAllScreen2ViewVector(); // schedule full rebuild
3701 SaveUndoStep();
3702 UpdateViewLineNumbers();
3705 SaveUndoStep();
3706 CUndo::GetInstance().EndGrouping();
3708 SetModified();
3709 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3710 SetCaretViewPosition(m_ptSelectionViewPosStart);
3711 UpdateGoalPos();
3712 ClearSelection();
3713 UpdateCaret();
3714 EnsureCaretVisible();
3715 Invalidate(FALSE);
3718 void CBaseView::PasteText()
3720 if (!OpenClipboard())
3721 return;
3723 CString sClipboardText;
3724 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3725 if (hglb)
3727 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3728 sClipboardText = CString(lpstr);
3729 GlobalUnlock(hglb);
3731 hglb = GetClipboardData(CF_UNICODETEXT);
3732 if (hglb)
3734 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3735 sClipboardText = lpstr;
3736 GlobalUnlock(hglb);
3738 CloseClipboard();
3740 if (sClipboardText.IsEmpty())
3741 return;
3743 sClipboardText.Replace(_T("\r\n"), _T("\r"));
3744 sClipboardText.Replace('\n', '\r');
3746 ResetUndoStep();
3748 POINT ptCaretViewPos = GetCaretViewPosition();
3749 int nLeft = ptCaretViewPos.x;
3750 int nViewLine = ptCaretViewPos.y;
3752 if ((nViewLine==0)&&(GetViewCount()==0))
3753 OnChar(VK_RETURN, 0, 0);
3755 std::vector<CString> lines;
3756 int nStart = 0;
3757 int nEolPos = 0;
3758 while ((nEolPos = sClipboardText.Find('\r', nEolPos))>=0)
3760 CString sLine = sClipboardText.Mid(nStart, nEolPos-nStart);
3761 lines.push_back(sLine);
3762 nEolPos++;
3763 nStart = nEolPos;
3765 CString sLine = sClipboardText.Mid(nStart);
3766 lines.push_back(sLine);
3768 int nLinesToPaste = (int)lines.size();
3769 if (nLinesToPaste > 1)
3771 // multiline text
3773 // We want to undo the multiline insertion in a single step.
3774 CUndo::GetInstance().BeginGrouping();
3776 sLine = GetViewLineChars(nViewLine);
3777 CString sLineLeft = sLine.Left(nLeft);
3778 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3779 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
3780 viewdata newLine(_T(""), DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3781 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3783 newLine.sLine = sLineLeft + lines[0];
3784 SetViewData(nViewLine, newLine);
3787 int nInsertLine = nViewLine;
3788 for (int i = 1; i < nLinesToPaste-1; i++)
3790 newLine.sLine = lines[i];
3791 InsertViewData(++nInsertLine, newLine);
3793 newLine.sLine = lines[nLinesToPaste-1] + sLineRight;
3794 newLine.ending = eOriginalEnding;
3795 InsertViewData(++nInsertLine, newLine);
3797 SaveUndoStep();
3799 // adds new lines everywhere except me
3800 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3802 m_pwndLeft->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3804 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3806 m_pwndRight->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3808 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3810 m_pwndBottom->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3812 SaveUndoStep();
3814 UpdateViewLineNumbers();
3815 CUndo::GetInstance().EndGrouping();
3817 ptCaretViewPos = SetupPoint(lines[nLinesToPaste-1].GetLength(), nInsertLine);
3819 else
3821 // single line text - just insert it
3822 sLine = GetViewLineChars(nViewLine);
3823 sLine.Insert(nLeft, sClipboardText);
3824 ptCaretViewPos = SetupPoint(nLeft + sClipboardText.GetLength(), nViewLine);
3825 SetViewLine(nViewLine, sLine);
3826 SetViewState(nViewLine, DIFFSTATE_EDITED);
3827 SaveUndoStep();
3830 SetModified();
3831 RefreshViews();
3832 BuildAllScreen2ViewVector();
3833 UpdateCaretViewPosition(ptCaretViewPos);
3836 void CBaseView::OnCaretDown()
3838 POINT ptCaretPos = GetCaretPosition();
3839 int nLine = ptCaretPos.y;
3840 int nNextLine = nLine + 1;
3841 if (nNextLine >= GetLineCount()) // already at last line
3843 return;
3846 POINT ptCaretViewPos = GetCaretViewPosition();
3847 int nViewLine = ptCaretViewPos.y;
3848 int nNextViewLine = GetViewLineForScreen(nNextLine);
3849 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
3851 // find next suitable screen line
3852 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
3854 nNextLine++;
3855 if (nNextLine >= GetLineCount())
3857 return;
3859 nNextViewLine = GetViewLineForScreen(nNextLine);
3862 ptCaretPos.y = nNextLine;
3863 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3864 SetCaretPosition(ptCaretPos);
3865 OnCaretMove(MOVELEFT);
3866 ShowDiffLines(ptCaretPos.y);
3869 bool CBaseView::MoveCaretLeft()
3871 POINT ptCaretViewPos = GetCaretViewPosition();
3873 //int nViewLine = ptCaretViewPos.y;
3874 if (ptCaretViewPos.x == 0)
3876 int nPrevLine = GetCaretPosition().y;
3877 int nPrevViewLine;
3878 do {
3879 nPrevLine--;
3880 if (nPrevLine < 0)
3882 return false;
3884 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3885 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
3886 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
3887 ShowDiffLines(nPrevLine);
3889 else
3890 --ptCaretViewPos.x;
3892 SetCaretAndGoalViewPosition(ptCaretViewPos);
3893 return true;
3896 bool CBaseView::MoveCaretRight()
3898 POINT ptCaretViewPos = GetCaretViewPosition();
3900 int nViewLine = ptCaretViewPos.y;
3901 int nViewLineLen = GetViewLineLength(nViewLine);
3902 if (ptCaretViewPos.x >= nViewLineLen)
3904 int nNextLine = GetCaretPosition().y;
3905 int nNextViewLine;
3906 do {
3907 nNextLine++;
3908 if (nNextLine >= GetLineCount())
3910 return false;
3912 nNextViewLine = GetViewLineForScreen(nNextLine);
3913 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
3914 ptCaretViewPos.y = nNextViewLine;
3915 ptCaretViewPos.x = 0;
3916 ShowDiffLines(nNextLine);
3918 else
3919 ++ptCaretViewPos.x;
3921 SetCaretAndGoalViewPosition(ptCaretViewPos);
3922 return true;
3925 void CBaseView::UpdateGoalPos()
3927 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
3930 void CBaseView::OnCaretLeft()
3932 MoveCaretLeft();
3933 OnCaretMove(MOVELEFT);
3936 void CBaseView::OnCaretRight()
3938 MoveCaretRight();
3939 OnCaretMove(MOVERIGHT);
3942 void CBaseView::OnCaretUp()
3944 POINT ptCaretPos = GetCaretPosition();
3945 int nLine = ptCaretPos.y;
3946 if (nLine <= 0) // already at first line
3948 return;
3950 int nPrevLine = nLine - 1;
3952 POINT ptCaretViewPos = GetCaretViewPosition();
3953 int nViewLine = ptCaretViewPos.y;
3954 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
3955 if (nPrevViewLine != nViewLine) // not on same view line
3957 // find previous suitable screen line
3958 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
3960 if (nPrevLine <= 0)
3962 return;
3964 nPrevLine--;
3965 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3968 ptCaretPos.y = nPrevLine;
3969 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3970 SetCaretPosition(ptCaretPos);
3971 OnCaretMove(MOVELEFT);
3972 ShowDiffLines(ptCaretPos.y);
3975 bool CBaseView::IsWordSeparator(const wchar_t ch) const
3977 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
3980 bool CBaseView::IsCaretAtWordBoundary()
3982 POINT ptViewCaret = GetCaretViewPosition();
3983 CString line = GetViewLineChars(ptViewCaret.y);
3984 if (line.IsEmpty())
3985 return false; // no boundary at the empty lines
3986 if (ptViewCaret.x == 0)
3987 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
3988 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
3989 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
3990 return
3991 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
3992 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
3995 void CBaseView::UpdateViewsCaretPosition()
3997 POINT ptCaretPos = GetCaretPosition();
3998 if (m_pwndBottom && m_pwndBottom!=this)
3999 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4000 if (m_pwndLeft && m_pwndLeft!=this)
4001 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4002 if (m_pwndRight && m_pwndRight!=this)
4003 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4006 void CBaseView::OnCaretWordleft()
4008 MoveCaretWordLeft();
4009 OnCaretMove(MOVELEFT);
4012 void CBaseView::OnCaretWordright()
4014 MoveCaretWordRight();
4015 OnCaretMove(MOVERIGHT);
4018 void CBaseView::MoveCaretWordLeft()
4020 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4025 void CBaseView::MoveCaretWordRight()
4027 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4032 void CBaseView::ClearCurrentSelection()
4034 m_ptSelectionViewPosStart = GetCaretViewPosition();
4035 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4036 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4037 m_nSelViewBlockStart = -1;
4038 m_nSelViewBlockEnd = -1;
4039 Invalidate(FALSE);
4042 void CBaseView::ClearSelection()
4044 if (m_pwndLeft)
4045 m_pwndLeft->ClearCurrentSelection();
4046 if (m_pwndRight)
4047 m_pwndRight->ClearCurrentSelection();
4048 if (m_pwndBottom)
4049 m_pwndBottom->ClearCurrentSelection();
4052 void CBaseView::AdjustSelection(bool bMoveLeft)
4054 POINT ptCaretViewPos = GetCaretViewPosition();
4055 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4057 // select all have been used recently update origin
4058 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4060 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4061 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4063 m_ptSelectionViewPosStart = ptCaretViewPos;
4064 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4066 else
4068 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4069 m_ptSelectionViewPosEnd = ptCaretViewPos;
4072 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4074 Invalidate(FALSE);
4077 void CBaseView::OnEditCut()
4079 if (IsWritable())
4081 OnEditCopy();
4082 RemoveSelectedText();
4086 void CBaseView::OnEditPaste()
4088 if (IsWritable())
4090 CUndo::GetInstance().BeginGrouping();
4091 RemoveSelectedText();
4092 PasteText();
4093 CUndo::GetInstance().EndGrouping();
4097 void CBaseView::DeleteFonts()
4099 for (int i=0; i<fontsCount; i++)
4101 if (m_apFonts[i] != NULL)
4103 m_apFonts[i]->DeleteObject();
4104 delete m_apFonts[i];
4105 m_apFonts[i] = NULL;
4110 void CBaseView::OnCaretMove(bool bMoveLeft)
4112 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4113 OnCaretMove(bMoveLeft, bShift);
4116 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4118 if(isShiftPressed)
4119 AdjustSelection(bMoveLeft);
4120 else
4121 ClearSelection();
4122 EnsureCaretVisible();
4123 UpdateCaret();
4126 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4128 AddCutCopyAndPaste(popup);
4131 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4133 popup.AppendMenu(MF_SEPARATOR, NULL);
4134 CString temp;
4135 temp.LoadString(IDS_EDIT_COPY);
4136 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4137 if (IsWritable())
4139 temp.LoadString(IDS_EDIT_CUT);
4140 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4141 temp.LoadString(IDS_EDIT_PASTE);
4142 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4146 void CBaseView::CompensateForKeyboard(CPoint& point)
4148 // if the context menu is invoked through the keyboard, we have to use
4149 // a calculated position on where to anchor the menu on
4150 if (ArePointsSame(point, SetupPoint(-1, -1)))
4152 CRect rect;
4153 GetWindowRect(&rect);
4154 point = rect.CenterPoint();
4158 HICON CBaseView::LoadIcon(WORD iconId)
4160 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4161 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4162 return (HICON)icon;
4165 void CBaseView::ReleaseBitmap()
4167 if (m_pCacheBitmap != NULL)
4169 m_pCacheBitmap->DeleteObject();
4170 delete m_pCacheBitmap;
4171 m_pCacheBitmap = NULL;
4175 void CBaseView::BuildMarkedWordArray()
4177 int lineCount = GetLineCount();
4178 m_arMarkedWordLines.clear();
4179 m_arMarkedWordLines.reserve(lineCount);
4180 bool bDoit = !m_sMarkedWord.IsEmpty();
4181 for (int i = 0; i < lineCount; ++i)
4183 if (bDoit)
4185 CString line = GetLineChars(i);
4187 if (!line.IsEmpty())
4189 m_arMarkedWordLines.push_back(line.Find(m_sMarkedWord) != -1);
4191 else
4192 m_arMarkedWordLines.push_back(0);
4194 else
4195 m_arMarkedWordLines.push_back(0);
4199 void CBaseView::BuildFindStringArray()
4201 int lineCount = GetLineCount();
4202 m_arFindStringLines.clear();
4203 m_arFindStringLines.reserve(lineCount);
4204 bool bDoit = !m_sFindText.IsEmpty();
4205 int s = 0;
4206 int e = 0;
4207 for (int i = 0; i < lineCount; ++i)
4209 if (bDoit)
4211 CString line = GetLineChars(i);
4213 if (!line.IsEmpty())
4215 line = line.MakeLower();
4216 m_arFindStringLines.push_back(StringFound(line, SearchNext, s, e));
4218 else
4219 m_arFindStringLines.push_back(0);
4221 else
4222 m_arFindStringLines.push_back(0);
4224 UpdateLocator();
4227 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4229 if (!m_bShowInlineDiff)
4230 return false;
4231 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4232 return false;
4234 CString sLine = GetViewLineChars(nViewLine);
4235 if (sLine.IsEmpty())
4236 return false;
4238 CheckOtherView();
4239 if (!m_pOtherViewData)
4240 return false;
4242 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4243 if (sDiffLine.IsEmpty())
4244 return false;
4246 CString sLineExp = ExpandChars(sLine);
4247 CString sDiffLineExp = ExpandChars(sDiffLine);
4248 svn_diff_t * diff = NULL;
4249 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4250 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4251 return false;
4253 size_t lineoffset = 0;
4254 size_t position = 0;
4255 while (diff)
4257 apr_off_t len = diff->original_length;
4258 size_t oldpos = position;
4260 for (apr_off_t i = 0; i < len; ++i)
4262 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4263 lineoffset++;
4266 if (diff->type == svn_diff__type_diff_modified)
4268 inlineDiffPos p;
4269 p.start = oldpos;
4270 p.end = position;
4271 positions.push_back(p);
4274 diff = diff->next;
4277 return !positions.empty();
4280 void CBaseView::OnNavigateNextinlinediff()
4282 int nX;
4283 if (GetNextInlineDiff(nX))
4285 POINT ptCaretViewPos = GetCaretViewPosition();
4286 ptCaretViewPos.x = nX;
4287 SetCaretAndGoalViewPosition(ptCaretViewPos);
4288 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4289 EnsureCaretVisible();
4293 void CBaseView::OnNavigatePrevinlinediff()
4295 int nX;
4296 if (GetPrevInlineDiff(nX))
4298 POINT ptCaretViewPos = GetCaretViewPosition();
4299 ptCaretViewPos.x = nX;
4300 SetCaretAndGoalViewPosition(ptCaretViewPos);
4301 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4302 EnsureCaretVisible();
4306 bool CBaseView::HasNextInlineDiff()
4308 int nPos;
4309 return GetNextInlineDiff(nPos);
4312 bool CBaseView::GetNextInlineDiff(int & nPos)
4314 POINT ptCaretViewPos = GetCaretViewPosition();
4315 std::vector<inlineDiffPos> positions;
4316 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4318 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4320 if (it->start > ptCaretViewPos.x)
4322 nPos = (LONG)it->start;
4323 return true;
4325 if (it->end > ptCaretViewPos.x)
4327 nPos = (LONG)it->end;
4328 return true;
4332 return false;
4335 bool CBaseView::HasPrevInlineDiff()
4337 int nPos;
4338 return GetPrevInlineDiff(nPos);
4341 bool CBaseView::GetPrevInlineDiff(int & nPos)
4343 POINT ptCaretViewPos = GetCaretViewPosition();
4344 std::vector<inlineDiffPos> positions;
4345 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4347 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4349 if ( it->end < ptCaretViewPos.x)
4351 nPos = (LONG)it->end;
4352 return true;
4354 if ( it->start < ptCaretViewPos.x)
4356 nPos = (LONG)it->start;
4357 return true;
4361 return false;
4364 CBaseView * CBaseView::GetFirstGoodView()
4366 if (IsViewGood(m_pwndLeft))
4367 return m_pwndLeft;
4368 if (IsViewGood(m_pwndRight))
4369 return m_pwndRight;
4370 if (IsViewGood(m_pwndBottom))
4371 return m_pwndBottom;
4372 return NULL;
4375 void CBaseView::BuildAllScreen2ViewVector()
4377 CBaseView * p_pwndView = GetFirstGoodView();
4378 if (p_pwndView)
4380 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4384 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4386 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4389 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4391 CBaseView * p_pwndView = GetFirstGoodView();
4392 if (p_pwndView)
4394 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4398 void CBaseView::UpdateViewLineNumbers()
4400 int nLineNumber = 0;
4401 int nViewLineCount = GetViewCount();
4402 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4404 int oldLine = (int)GetViewLineNumber(nViewLine);
4405 if (oldLine >= 0)
4406 SetViewLineNumber(nViewLine, nLineNumber++);
4408 m_nDigits = 0;
4411 int CBaseView::CleanEmptyLines()
4413 int nRemovedCount = 0;
4414 int nViewLineCount = GetViewCount();
4415 bool bCheckLeft = IsViewGood(m_pwndLeft);
4416 bool bCheckRight = IsViewGood(m_pwndRight);
4417 bool bCheckBottom = IsViewGood(m_pwndBottom);
4418 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4420 bool bAllEmpty = true;
4421 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4422 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4423 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4424 if (bAllEmpty)
4426 if (bCheckLeft)
4428 m_pwndLeft->RemoveViewData(nViewLine);
4430 if (bCheckRight)
4432 m_pwndRight->RemoveViewData(nViewLine);
4434 if (bCheckBottom)
4436 m_pwndBottom->RemoveViewData(nViewLine);
4438 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4440 SaveUndoStep();
4442 nViewLineCount--;
4443 nRemovedCount++;
4444 continue;
4446 nViewLine++;
4448 if (nRemovedCount != 0)
4450 if (bCheckLeft)
4452 m_pwndLeft->SetModified();
4454 if (bCheckRight)
4456 m_pwndRight->SetModified();
4458 if (bCheckBottom)
4460 m_pwndBottom->SetModified();
4463 return nRemovedCount;
4466 int CBaseView::FindScreenLineForViewLine( int viewLine )
4468 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4471 int CBaseView::CountMultiLines( int nViewLine )
4473 if (m_ScreenedViewLine.empty())
4474 return 0; // in case the view is completely empty
4476 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4478 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4480 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4483 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4485 TScreenedViewLine oScreenedLine;
4486 // tokenize string
4487 int prevpos = 0;
4488 int pos = 0;
4489 while ((pos = multiline.Find('\n', pos)) >= 0)
4491 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4492 pos++;
4493 prevpos = pos;
4495 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4496 oScreenedLine.bSublinesSet = true;
4497 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4499 return CountMultiLines(nViewLine);
4502 /// prepare inline diff cache
4503 LineColors & CBaseView::GetLineColors(int nViewLine)
4505 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4507 if (m_bWhitespaceInlineDiffs)
4509 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4510 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4512 else
4514 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4515 return m_ScreenedViewLine[nViewLine].lineColors;
4518 LineColors oLineColors;
4519 // set main line color
4520 COLORREF crBkgnd, crText;
4521 DiffStates diffState = m_pViewData->GetState(nViewLine);
4522 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4523 oLineColors.SetColor(0, crText, crBkgnd);
4525 do {
4526 if (!m_bShowInlineDiff)
4527 break;
4529 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4530 break;
4532 CString sLine = GetViewLineChars(nViewLine);
4533 if (sLine.IsEmpty())
4534 break;
4535 if (!m_pOtherView)
4536 break;
4538 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4539 if (sDiffLine.IsEmpty())
4540 break;
4542 svn_diff_t * diff = NULL;
4543 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4544 break;
4545 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4546 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4547 break;
4549 int lineoffset = 0;
4550 int nTextStartOffset = 0;
4551 std::map<int, COLORREF> removedPositions;
4552 while (diff)
4554 apr_off_t len = diff->original_length;
4556 CString s;
4557 for (int i = 0; i < len; ++i)
4559 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4560 lineoffset++;
4562 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4563 int nTextLength = s.GetLength();
4565 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4566 if ((m_bShowInlineDiff)&&(bInlineDiff))
4568 crBkgnd = InlineViewLineDiffColor(nViewLine);
4570 else
4572 crBkgnd = m_ModifiedBk;
4575 if (len < diff->modified_length)
4577 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4579 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4581 nTextStartOffset += nTextLength;
4582 diff = diff->next;
4584 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4586 oLineColors.AddShotColor(it->first, it->second);
4588 } while (false); // error catch
4590 if (!m_bWhitespaceInlineDiffs)
4592 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4593 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4595 else
4597 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4598 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4601 return GetLineColors(nViewLine);
4604 void CBaseView::OnEditSelectall()
4606 if (m_pViewData == nullptr)
4607 return;
4608 int nLastViewLine = m_pViewData->GetCount()-1;
4609 if (nLastViewLine < 0)
4610 return;
4611 SetupAllViewSelection(0, nLastViewLine);
4613 CString sLine = GetViewLineChars(nLastViewLine);
4614 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4615 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4616 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4618 UpdateWindow();
4621 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4623 FilterWhitespaces(first);
4624 FilterWhitespaces(second);
4627 void CBaseView::FilterWhitespaces(CString& line)
4629 line.Remove(' ');
4630 line.Remove('\t');
4631 line.Remove('\r');
4632 line.Remove('\n');
4635 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4637 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4638 int nEventLine = nLineFromTop + m_nTopLine;
4639 nEventLine--; //we need the index
4640 return nEventLine;
4644 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4646 if (RelayTrippleClick(pMsg))
4647 return TRUE;
4648 return CView::PreTranslateMessage(pMsg);
4652 void CBaseView::ResetUndoStep()
4654 m_AllState.Clear();
4657 void CBaseView::SaveUndoStep()
4659 if (!m_AllState.IsEmpty())
4661 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4663 ResetUndoStep();
4666 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4668 m_pState->addedlines.push_back(index);
4669 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4672 void CBaseView::InsertViewData( int index, const viewdata& data )
4674 m_pState->addedlines.push_back(index);
4675 m_pViewData->InsertData(index, data);
4678 void CBaseView::RemoveViewData( int index )
4680 m_pState->removedlines[index] = m_pViewData->GetData(index);
4681 m_pViewData->RemoveData(index);
4684 void CBaseView::SetViewData( int index, const viewdata& data )
4686 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4687 m_pViewData->SetData(index, data);
4690 void CBaseView::SetViewState( int index, DiffStates state )
4692 m_pState->linestates[index] = m_pViewData->GetState(index);
4693 m_pViewData->SetState(index, state);
4696 void CBaseView::SetViewLine( int index, const CString& sLine )
4698 m_pState->difflines[index] = m_pViewData->GetLine(index);
4699 m_pViewData->SetLine(index, sLine);
4702 void CBaseView::SetViewLineNumber( int index, int linenumber )
4704 int oldLineNumber = m_pViewData->GetLineNumber(index);
4705 if (oldLineNumber != linenumber) {
4706 m_pState->linelines[index] = oldLineNumber;
4707 m_pViewData->SetLineNumber(index, linenumber);
4711 void CBaseView::SetViewLineEnding( int index, EOL ending )
4713 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4714 m_pViewData->SetLineEnding(index, ending);
4718 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4720 if (HasSelection())
4722 start = m_nSelViewBlockStart;
4723 end = m_nSelViewBlockEnd;
4724 return true;
4726 return false;
4729 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4731 RebuildIfNecessary();
4732 if (size() <= screenLine)
4733 return 0;
4734 return m_Screen2View[screenLine].nViewLine;
4737 int CBaseView::Screen2View::size()
4739 RebuildIfNecessary();
4740 return (int)m_Screen2View.size();
4743 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4745 RebuildIfNecessary();
4746 if (size() <= screenLine)
4747 return 0;
4748 return m_Screen2View[screenLine].nViewSubLine;
4751 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4753 RebuildIfNecessary();
4754 return m_Screen2View[screenLine];
4758 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
4760 void CBaseView::Screen2View::RebuildIfNecessary()
4762 if (!m_pViewData)
4763 return; // rebuild not necessary
4765 FixScreenedCacheSize(m_pwndLeft);
4766 FixScreenedCacheSize(m_pwndRight);
4767 FixScreenedCacheSize(m_pwndBottom);
4768 if (!m_bFull)
4770 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
4772 ResetScreenedViewLineCache(m_pwndLeft, *it);
4773 ResetScreenedViewLineCache(m_pwndRight, *it);
4774 ResetScreenedViewLineCache(m_pwndBottom, *it);
4777 else
4779 ResetScreenedViewLineCache(m_pwndLeft);
4780 ResetScreenedViewLineCache(m_pwndRight);
4781 ResetScreenedViewLineCache(m_pwndBottom);
4783 m_RebuildRanges.clear();
4784 m_bFull = false;
4786 size_t OldSize = m_Screen2View.size();
4787 m_Screen2View.clear();
4788 m_Screen2View.reserve(OldSize); // guess same size
4789 for (int i = 0; i < m_pViewData->GetCount(); ++i)
4791 if (m_pMainFrame->m_bCollapsed)
4793 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
4794 ++i;
4795 if (!(i < m_pViewData->GetCount()))
4796 break;
4798 TScreenLineInfo oLineInfo;
4799 oLineInfo.nViewLine = i;
4800 oLineInfo.nViewSubLine = -1; // no wrap
4801 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
4803 int nMaxLines = 0;
4804 if (IsLeftViewGood())
4805 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
4806 if (IsRightViewGood())
4807 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
4808 if (IsBottomViewGood())
4809 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
4810 for (int l = 0; l < (nMaxLines-1); ++l)
4812 oLineInfo.nViewSubLine++;
4813 m_Screen2View.push_back(oLineInfo);
4815 oLineInfo.nViewSubLine++;
4817 m_Screen2View.push_back(oLineInfo);
4819 m_pViewData = NULL;
4821 if (IsLeftViewGood())
4822 m_pwndLeft->BuildMarkedWordArray();
4823 if (IsRightViewGood())
4824 m_pwndRight->BuildMarkedWordArray();
4825 if (IsBottomViewGood())
4826 m_pwndBottom->BuildMarkedWordArray();
4827 UpdateLocator();
4828 RecalcAllVertScrollBars();
4829 RecalcAllHorzScrollBars();
4832 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
4834 RebuildIfNecessary();
4836 int nScreenLineCount = (int)m_Screen2View.size();
4838 int nPos = 0;
4839 if (nScreenLineCount>16)
4841 // for enough long data search for last screen
4842 // with viewline less than one we are looking for
4843 // use approximate method (based on) binary search using asymmetric start point
4844 // in form 2**n (determined as MSB of length) to go around division and rounding;
4845 // this effectively looks for bit values from MSB to LSB
4847 int nTestBit;
4848 //GetMostSignificantBitValue
4849 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
4850 nTestBit = nScreenLineCount;
4851 nTestBit |= nTestBit>>1;
4852 nTestBit |= nTestBit>>2;
4853 nTestBit |= nTestBit>>4;
4854 nTestBit |= nTestBit>>8;
4855 nTestBit |= nTestBit>>16;
4856 nTestBit ^= (nTestBit>>1);
4858 while (nTestBit)
4860 int nTestPos = nPos | nTestBit;
4861 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
4863 nPos = nTestPos;
4865 nTestBit >>= 1;
4868 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
4870 nPos++;
4873 return nPos;
4876 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
4877 m_bFull = true;
4879 m_pViewData = pViewData;
4882 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
4884 if (m_bFull)
4885 return;
4887 m_pViewData = pViewData;
4889 TRebuildRange Range;
4890 Range.FirstViewLine=nFirstViewLine;
4891 Range.LastViewLine=nLastViewLine;
4892 m_RebuildRanges.push_back(Range);
4895 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
4897 if (!IsViewGood(pwndView))
4899 return false;
4901 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
4902 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
4903 if (nOldSize == nViewCount)
4905 return false;
4907 pwndView->m_ScreenedViewLine.resize(nViewCount);
4908 return true;
4911 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView)
4913 if (!IsViewGood(pwndView))
4915 return false;
4917 TRebuildRange Range={0, pwndView->GetViewCount()-1};
4918 ResetScreenedViewLineCache(pwndView, Range);
4919 return true;
4922 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range)
4924 if (!IsViewGood(pwndView))
4926 return false;
4928 if (Range.LastViewLine == -1)
4930 return false;
4932 ASSERT(Range.FirstViewLine >= 0);
4933 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
4934 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
4936 pwndView->m_ScreenedViewLine[i].Clear();
4938 return false;
4941 void CBaseView::WrapChanged()
4943 m_nMaxLineLength = -1;
4944 m_nOffsetChar = 0;
4947 void CBaseView::OnEditFind()
4949 if (m_pFindDialog)
4950 return;
4952 m_pFindDialog = new CFindDlg(this);
4953 m_pFindDialog->Create(this);
4955 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
4958 LRESULT CBaseView::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
4960 ASSERT(m_pFindDialog != NULL);
4962 if (m_pFindDialog->IsTerminating())
4964 // invalidate the handle identifying the dialog box.
4965 m_pFindDialog = NULL;
4966 return 0;
4969 if(m_pFindDialog->FindNext())
4971 //read data from dialog
4972 m_sFindText = m_pFindDialog->GetFindString();
4973 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
4974 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
4975 m_bWholeWord = m_pFindDialog->WholeWord();
4977 if (!m_bMatchCase)
4978 m_sFindText = m_sFindText.MakeLower();
4980 BuildFindStringArray();
4981 OnEditFindnext();
4984 return 0;
4987 void CBaseView::OnEditFindnextStart()
4989 if (m_pViewData == nullptr)
4990 return;
4991 if (HasTextSelection())
4993 m_sFindText = GetSelectedText();
4994 m_bMatchCase = false;
4995 m_bLimitToDiff = false;
4996 m_bWholeWord = false;
4997 m_sFindText = m_sFindText.MakeLower();
4999 BuildFindStringArray();
5000 OnEditFindnext();
5002 else
5004 m_sFindText.Empty();
5005 BuildFindStringArray();
5009 void CBaseView::OnEditFindprevStart()
5011 if (m_pViewData == nullptr)
5012 return;
5013 if (HasTextSelection())
5015 m_sFindText = GetSelectedText();
5016 m_bMatchCase = false;
5017 m_bLimitToDiff = false;
5018 m_bWholeWord = false;
5019 m_sFindText = m_sFindText.MakeLower();
5021 BuildFindStringArray();
5022 OnEditFindprev();
5024 else
5026 m_sFindText.Empty();
5027 BuildFindStringArray();
5031 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5033 start = str.Find(m_sFindText);
5034 if ((srchDir==SearchPrevious)&&(start>=0))
5036 int laststart = start;
5039 start = laststart;
5040 laststart = str.Find(m_sFindText, laststart+1);
5041 } while (laststart >= 0);
5043 end = start + m_sFindText.GetLength();
5044 bool bStringFound = (start >= 0);
5045 if (bStringFound && m_bWholeWord)
5047 if (start)
5048 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5050 if (bStringFound)
5052 if (str.GetLength() > end)
5053 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5056 return bStringFound;
5059 void CBaseView::OnEditFindprev()
5061 Search(SearchPrevious);
5064 void CBaseView::OnEditFindnext()
5066 Search(SearchNext);
5069 void CBaseView::Search(SearchDirection srchDir)
5071 if (m_sFindText.IsEmpty())
5072 return;
5073 if(!m_pViewData)
5074 return;
5076 POINT start = m_ptSelectionViewPosEnd;
5077 POINT end;
5078 end.y = m_pViewData->GetCount()-1;
5079 if (end.y < 0)
5080 return;
5082 if (srchDir==SearchNext)
5083 end.x = GetViewLineLength(end.y);
5084 else
5086 end.x = m_ptSelectionViewPosStart.x;
5087 start.x = 0;
5090 if (!HasTextSelection())
5092 start.y = m_ptCaretViewPos.y;
5093 if (srchDir==SearchNext)
5094 start.x = m_ptCaretViewPos.x;
5095 else
5097 start.x = 0;
5098 end.x = m_ptCaretViewPos.x;
5101 CString sSelectedText;
5102 int startline = -1;
5103 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5105 if (nViewLine < 0)
5107 nViewLine = m_pViewData->GetCount()-1;
5108 startline = start.y;
5110 if (nViewLine > end.y)
5112 nViewLine = 0;
5113 startline = start.y;
5115 if (startline >= 0)
5117 if (nViewLine == startline)
5118 break;
5120 switch (m_pViewData->GetState(nViewLine))
5122 case DIFFSTATE_EMPTY:
5123 break;
5124 case DIFFSTATE_UNKNOWN:
5125 case DIFFSTATE_NORMAL:
5126 if (m_bLimitToDiff)
5127 break;
5128 case DIFFSTATE_REMOVED:
5129 case DIFFSTATE_REMOVEDWHITESPACE:
5130 case DIFFSTATE_ADDED:
5131 case DIFFSTATE_ADDEDWHITESPACE:
5132 case DIFFSTATE_WHITESPACE:
5133 case DIFFSTATE_WHITESPACE_DIFF:
5134 case DIFFSTATE_CONFLICTED:
5135 case DIFFSTATE_CONFLICTED_IGNORED:
5136 case DIFFSTATE_CONFLICTADDED:
5137 case DIFFSTATE_CONFLICTEMPTY:
5138 case DIFFSTATE_CONFLICTRESOLVED:
5139 case DIFFSTATE_IDENTICALREMOVED:
5140 case DIFFSTATE_IDENTICALADDED:
5141 case DIFFSTATE_THEIRSREMOVED:
5142 case DIFFSTATE_THEIRSADDED:
5143 case DIFFSTATE_MOVED_FROM:
5144 case DIFFSTATE_MOVED_TO:
5145 case DIFFSTATE_YOURSREMOVED:
5146 case DIFFSTATE_YOURSADDED:
5147 case DIFFSTATE_EDITED:
5149 sSelectedText = GetViewLineChars(nViewLine);
5150 if (nViewLine==start.y)
5151 sSelectedText = srchDir==SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(start.x);
5152 if (!m_bMatchCase)
5153 sSelectedText = sSelectedText.MakeLower();
5154 int startfound = -1;
5155 int endfound = -1;
5156 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5158 HighlightViewLines(nViewLine, nViewLine);
5159 m_ptSelectionViewPosStart.x = startfound;
5160 m_ptSelectionViewPosEnd.x = endfound;
5161 if (nViewLine==start.y)
5163 m_ptSelectionViewPosStart.x += start.x;
5164 m_ptSelectionViewPosEnd.x += start.x;
5166 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5167 m_ptSelectionViewPosStart.y = nViewLine;
5168 m_ptSelectionViewPosEnd.y = nViewLine;
5169 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5170 UpdateViewsCaretPosition();
5171 EnsureCaretVisible();
5172 Invalidate();
5173 return;
5176 break;
5179 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5182 CString CBaseView::GetSelectedText() const
5184 CString sSelectedText;
5185 POINT start = m_ptSelectionViewPosStart;
5186 POINT end = m_ptSelectionViewPosEnd;
5187 if (!HasTextSelection())
5189 if (!HasSelection())
5190 return sSelectedText;
5191 start.y = m_nSelViewBlockStart;
5192 start.x = 0;
5193 end.y = m_nSelViewBlockEnd;
5194 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5196 // first store the selected lines in one CString
5197 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5199 switch (m_pViewData->GetState(nViewLine))
5201 case DIFFSTATE_EMPTY:
5202 break;
5203 case DIFFSTATE_UNKNOWN:
5204 case DIFFSTATE_NORMAL:
5205 case DIFFSTATE_REMOVED:
5206 case DIFFSTATE_REMOVEDWHITESPACE:
5207 case DIFFSTATE_ADDED:
5208 case DIFFSTATE_ADDEDWHITESPACE:
5209 case DIFFSTATE_WHITESPACE:
5210 case DIFFSTATE_WHITESPACE_DIFF:
5211 case DIFFSTATE_CONFLICTED:
5212 case DIFFSTATE_CONFLICTED_IGNORED:
5213 case DIFFSTATE_CONFLICTADDED:
5214 case DIFFSTATE_CONFLICTEMPTY:
5215 case DIFFSTATE_CONFLICTRESOLVED:
5216 case DIFFSTATE_IDENTICALREMOVED:
5217 case DIFFSTATE_IDENTICALADDED:
5218 case DIFFSTATE_THEIRSREMOVED:
5219 case DIFFSTATE_THEIRSADDED:
5220 case DIFFSTATE_MOVED_FROM:
5221 case DIFFSTATE_MOVED_TO:
5222 case DIFFSTATE_YOURSREMOVED:
5223 case DIFFSTATE_YOURSADDED:
5224 case DIFFSTATE_EDITED:
5225 sSelectedText += GetViewLineChars(nViewLine);
5226 sSelectedText += _T("\r\n");
5227 break;
5230 // remove the non-selected chars from the first line, last line and last \r\n
5231 int nLeftCut = start.x;
5232 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5233 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5234 return sSelectedText;
5237 void CBaseView::OnEditGotoline()
5239 if (m_pViewData == NULL)
5240 return;
5241 // find the last and first line number
5242 int nViewLineCount = m_pViewData->GetCount();
5244 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5245 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5247 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5248 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5250 break;
5253 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5255 return;
5257 nLastLineNumber++;
5258 int nFirstLineNumber=1; // first is always 1
5260 CString sText;
5261 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5263 CGotoLineDlg dlg(this);
5264 dlg.SetLabel(sText);
5265 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5266 if (dlg.DoModal() == IDOK)
5268 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5270 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5272 HighlightViewLines(nViewLine, nViewLine);
5273 return;