Fix compilation warnings
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blobe5417e170fa0da38fbc91d419d8a5e67cea69c6f
1 // TortoiseGitMerge - a Diff/Patch program
3 // Copyright (C) 2003-2013 - 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();
1052 if (m_pwndLineDiffBar)
1053 m_pwndLineDiffBar->Invalidate();
1057 void CBaseView::ScrollAllToChar(int nNewOffsetChar, BOOL bTrackScrollBar /* = TRUE */)
1059 if (m_pwndLeft)
1060 m_pwndLeft->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1061 if (m_pwndRight)
1062 m_pwndRight->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1063 if (m_pwndBottom)
1064 m_pwndBottom->ScrollToChar(nNewOffsetChar, bTrackScrollBar);
1067 void CBaseView::ScrollAllSide(int delta)
1069 int nNewOffset = m_nOffsetChar;
1070 nNewOffset += delta;
1071 int nMaxLineLength = GetMaxLineLength();
1072 if (nNewOffset >= nMaxLineLength)
1073 nNewOffset = nMaxLineLength - 1;
1074 if (nNewOffset < 0)
1075 nNewOffset = 0;
1076 ScrollAllToChar(nNewOffset, TRUE);
1077 if (m_pwndLineDiffBar)
1078 m_pwndLineDiffBar->Invalidate();
1079 UpdateCaret();
1082 void CBaseView::ScrollSide(int delta)
1084 int nNewOffset = m_nOffsetChar;
1085 nNewOffset += delta;
1086 int nMaxLineLength = GetMaxLineLength();
1087 if (nNewOffset >= nMaxLineLength)
1088 nNewOffset = nMaxLineLength - 1;
1089 if (nNewOffset < 0)
1090 nNewOffset = 0;
1091 ScrollToChar(nNewOffset, TRUE);
1092 if (m_pwndLineDiffBar)
1093 m_pwndLineDiffBar->Invalidate();
1094 UpdateCaret();
1097 void CBaseView::ScrollVertical(short zDelta)
1099 const int nLineCount = GetLineCount();
1100 int nTopLine = m_nTopLine;
1101 nTopLine -= (zDelta/30);
1102 if (nTopLine < 0)
1103 nTopLine = 0;
1104 if (nTopLine >= nLineCount)
1105 nTopLine = nLineCount - 1;
1106 ScrollToLine(nTopLine, TRUE);
1109 void CBaseView::ScrollToLine(int nNewTopLine, BOOL bTrackScrollBar /*= TRUE*/)
1111 if (m_nTopLine != nNewTopLine)
1113 if (nNewTopLine < 0)
1114 nNewTopLine = 0;
1116 int nScrollLines = m_nTopLine - nNewTopLine;
1118 m_nTopLine = nNewTopLine;
1119 CRect rcScroll;
1120 GetClientRect(&rcScroll);
1121 rcScroll.top += GetLineHeight()+HEADERHEIGHT;
1122 ScrollWindow(0, nScrollLines * GetLineHeight(), &rcScroll, &rcScroll);
1123 UpdateWindow();
1124 if (bTrackScrollBar)
1125 RecalcVertScrollBar(TRUE);
1126 UpdateCaret();
1131 void CBaseView::DrawMargin(CDC *pdc, const CRect &rect, int nLineIndex)
1133 pdc->FillSolidRect(rect, ::GetSysColor(COLOR_SCROLLBAR));
1135 if ((nLineIndex >= 0)&&(m_pViewData)&&(m_pViewData->GetCount()))
1137 int nViewLine = GetViewLineForScreen(nLineIndex);
1138 HICON icon = NULL;
1139 ASSERT(nViewLine<(int)m_ScreenedViewLine.size());
1140 TScreenedViewLine::EIcon eIcon = m_ScreenedViewLine[nViewLine].eIcon;
1141 if (eIcon==TScreenedViewLine::ICN_UNKNOWN)
1143 DiffStates state = m_pViewData->GetState(nViewLine);
1144 switch (state)
1146 case DIFFSTATE_ADDED:
1147 case DIFFSTATE_THEIRSADDED:
1148 case DIFFSTATE_YOURSADDED:
1149 case DIFFSTATE_IDENTICALADDED:
1150 case DIFFSTATE_CONFLICTADDED:
1151 eIcon = TScreenedViewLine::ICN_ADD;
1152 break;
1153 case DIFFSTATE_REMOVED:
1154 case DIFFSTATE_THEIRSREMOVED:
1155 case DIFFSTATE_YOURSREMOVED:
1156 case DIFFSTATE_IDENTICALREMOVED:
1157 eIcon = TScreenedViewLine::ICN_REMOVED;
1158 break;
1159 case DIFFSTATE_CONFLICTED:
1160 eIcon = TScreenedViewLine::ICN_CONFLICT;
1161 break;
1162 case DIFFSTATE_CONFLICTED_IGNORED:
1163 eIcon = TScreenedViewLine::ICN_CONFLICTIGNORED;
1164 break;
1165 case DIFFSTATE_EDITED:
1166 eIcon = TScreenedViewLine::ICN_EDIT;
1167 break;
1168 case DIFFSTATE_MOVED_TO:
1169 case DIFFSTATE_MOVED_FROM:
1170 eIcon = TScreenedViewLine::ICN_MOVED;
1171 break;
1172 default:
1173 break;
1175 bool bIdentical = false;
1176 if ((state != DIFFSTATE_EDITED)&&(IsBlockWhitespaceOnly(nLineIndex, bIdentical)))
1178 if (bIdentical)
1179 eIcon = TScreenedViewLine::ICN_SAME;
1180 else
1181 eIcon = TScreenedViewLine::ICN_WHITESPACEDIFF;
1183 m_ScreenedViewLine[nViewLine].eIcon = eIcon;
1185 switch (eIcon)
1187 case TScreenedViewLine::ICN_UNKNOWN:
1188 case TScreenedViewLine::ICN_NONE:
1189 break;
1190 case TScreenedViewLine::ICN_SAME:
1191 icon = m_hEqualIcon;
1192 break;
1193 case TScreenedViewLine::ICN_EDIT:
1194 icon = m_hEditedIcon;
1195 break;
1196 case TScreenedViewLine::ICN_WHITESPACEDIFF:
1197 icon = m_hWhitespaceBlockIcon;
1198 break;
1199 case TScreenedViewLine::ICN_ADD:
1200 icon = m_hAddedIcon;
1201 break;
1202 case TScreenedViewLine::ICN_CONFLICT:
1203 icon = m_hConflictedIcon;
1204 break;
1205 case TScreenedViewLine::ICN_CONFLICTIGNORED:
1206 icon = m_hConflictedIgnoredIcon;
1207 break;
1208 case TScreenedViewLine::ICN_REMOVED:
1209 icon = m_hRemovedIcon;
1210 break;
1211 case TScreenedViewLine::ICN_MOVED:
1212 icon = m_hMovedIcon;
1213 break;
1217 if (icon)
1219 ::DrawIconEx(pdc->m_hDC, rect.left + 2, rect.top + (rect.Height()-16)/2, icon, 16, 16, NULL, NULL, DI_NORMAL);
1221 if ((m_bViewLinenumbers)&&(m_nDigits))
1223 int nSubLine = GetSubLineOffset(nLineIndex);
1224 bool bIsFirstSubline = (nSubLine == 0) || (nSubLine == -1);
1225 CString sLinenumber;
1226 if (bIsFirstSubline)
1228 CString sLinenumberFormat;
1229 int nLineNumber = GetLineNumber(nLineIndex);
1230 if (IsViewLineHidden(GetViewLineForScreen(nLineIndex)))
1232 // TODO: do not show if there is no number hidden
1233 // TODO: show number if there is only one
1234 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1235 sLinenumber.Format(sLinenumberFormat, (m_nDigits>1) ? _T("↕⁞") : _T("⁞")); // alternative …
1237 else if (nLineNumber >= 0)
1239 sLinenumberFormat.Format(_T("%%%dd"), m_nDigits);
1240 sLinenumber.Format(sLinenumberFormat, nLineNumber+1);
1242 else if (m_pMainFrame->m_bWrapLines)
1244 sLinenumberFormat.Format(_T("%%%ds"), m_nDigits);
1245 sLinenumber.Format(sLinenumberFormat, _T("·"));
1247 if (!sLinenumber.IsEmpty())
1249 pdc->SetBkColor(::GetSysColor(COLOR_SCROLLBAR));
1250 pdc->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
1252 pdc->SelectObject(GetFont());
1253 pdc->ExtTextOut(rect.left + 18, rect.top, ETO_CLIPPED, &rect, sLinenumber, NULL);
1260 int CBaseView::GetMarginWidth()
1262 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount()))
1264 if (m_nDigits <= 0)
1266 int nLength = (int)m_pViewData->GetCount();
1267 // find out how many digits are needed to show the highest line number
1268 CString sMax;
1269 sMax.Format(_T("%d"), nLength);
1270 m_nDigits = sMax.GetLength();
1272 int nWidth = GetCharWidth();
1273 return (MARGINWIDTH + (m_nDigits * nWidth) + 2);
1275 return MARGINWIDTH;
1278 void CBaseView::DrawHeader(CDC *pdc, const CRect &rect)
1280 CRect textrect(rect.left, rect.top, rect.Width(), GetLineHeight()+HEADERHEIGHT);
1281 COLORREF crBk, crFg;
1282 if (IsBottomViewGood())
1284 CDiffColors::GetInstance().GetColors(DIFFSTATE_NORMAL, crBk, crFg);
1285 crBk = ::GetSysColor(COLOR_SCROLLBAR);
1287 else
1289 DiffStates state = DIFFSTATE_REMOVED;
1290 if (this == m_pwndRight)
1292 state = DIFFSTATE_ADDED;
1294 CDiffColors::GetInstance().GetColors(state, crBk, crFg);
1296 pdc->SetBkColor(crBk);
1297 pdc->FillSolidRect(textrect, crBk);
1299 pdc->SetTextColor(crFg);
1301 pdc->SelectObject(GetFont(FALSE, TRUE));
1303 CString sViewTitle;
1304 if (IsModified())
1306 sViewTitle = _T("* ") + m_sWindowName;
1308 else
1310 sViewTitle = m_sWindowName;
1312 int nStringLength = (GetCharWidth()*m_sWindowName.GetLength());
1313 if (nStringLength > rect.Width())
1315 int offset = std::min<int>(m_nOffsetChar, (nStringLength-rect.Width())/GetCharWidth()+1);
1316 sViewTitle = m_sWindowName.Mid(offset);
1318 pdc->ExtTextOut(std::max<int>(rect.left + (rect.Width()-nStringLength)/2, 1),
1319 rect.top+(HEADERHEIGHT/2), ETO_CLIPPED, textrect, sViewTitle, NULL);
1320 if (this->GetFocus() == this)
1321 pdc->DrawEdge(textrect, EDGE_BUMP, BF_RECT);
1322 else
1323 pdc->DrawEdge(textrect, EDGE_ETCHED, BF_RECT);
1326 void CBaseView::OnDraw(CDC * pDC)
1328 CRect rcClient;
1329 GetClientRect(rcClient);
1331 int nLineCount = GetLineCount();
1332 int nLineHeight = GetLineHeight();
1334 CDC cacheDC;
1335 VERIFY(cacheDC.CreateCompatibleDC(pDC));
1336 if (m_pCacheBitmap == NULL)
1338 m_pCacheBitmap = new CBitmap;
1339 VERIFY(m_pCacheBitmap->CreateCompatibleBitmap(pDC, rcClient.Width(), nLineHeight));
1341 CBitmap *pOldBitmap = cacheDC.SelectObject(m_pCacheBitmap);
1343 DrawHeader(pDC, rcClient);
1345 CRect rcLine;
1346 rcLine = rcClient;
1347 rcLine.top += nLineHeight+HEADERHEIGHT;
1348 rcLine.bottom = rcLine.top + nLineHeight;
1349 CRect rcCacheMargin(0, 0, GetMarginWidth(), nLineHeight);
1350 CRect rcCacheLine(GetMarginWidth(), 0, rcLine.Width(), nLineHeight);
1352 int nCurrentLine = m_nTopLine;
1353 bool bBeyondFileLineCached = false;
1354 while (rcLine.top < rcClient.bottom)
1356 if (nCurrentLine < nLineCount)
1358 DrawMargin(&cacheDC, rcCacheMargin, nCurrentLine);
1359 DrawSingleLine(&cacheDC, rcCacheLine, nCurrentLine);
1360 bBeyondFileLineCached = false;
1362 else if (!bBeyondFileLineCached)
1364 DrawMargin(&cacheDC, rcCacheMargin, -1);
1365 DrawSingleLine(&cacheDC, rcCacheLine, -1);
1366 bBeyondFileLineCached = true;
1369 VERIFY(pDC->BitBlt(rcLine.left, rcLine.top, rcLine.Width(), rcLine.Height(), &cacheDC, 0, 0, SRCCOPY));
1371 nCurrentLine ++;
1372 rcLine.OffsetRect(0, nLineHeight);
1375 cacheDC.SelectObject(pOldBitmap);
1376 cacheDC.DeleteDC();
1379 bool CBaseView::IsStateConflicted(DiffStates state)
1381 switch (state)
1383 case DIFFSTATE_CONFLICTED:
1384 case DIFFSTATE_CONFLICTED_IGNORED:
1385 case DIFFSTATE_CONFLICTEMPTY:
1386 case DIFFSTATE_CONFLICTADDED:
1387 return true;
1389 return false;
1392 bool CBaseView::IsStateEmpty(DiffStates state)
1394 switch (state)
1396 case DIFFSTATE_CONFLICTEMPTY:
1397 case DIFFSTATE_UNKNOWN:
1398 case DIFFSTATE_EMPTY:
1399 return true;
1401 return false;
1404 bool CBaseView::IsStateRemoved(DiffStates state)
1406 switch (state)
1408 case DIFFSTATE_REMOVED:
1409 case DIFFSTATE_MOVED_FROM:
1410 case DIFFSTATE_THEIRSREMOVED:
1411 case DIFFSTATE_YOURSREMOVED:
1412 case DIFFSTATE_IDENTICALREMOVED:
1413 return true;
1415 return false;
1418 DiffStates CBaseView::ResolveState(DiffStates state)
1420 if (IsStateConflicted(state))
1422 if (state == DIFFSTATE_CONFLICTEMPTY)
1423 return DIFFSTATE_CONFLICTRESOLVEDEMPTY;
1424 else
1425 return DIFFSTATE_CONFLICTRESOLVED;
1427 return state;
1431 bool CBaseView::IsLineEmpty(int nLineIndex)
1433 if (m_pViewData == 0)
1434 return FALSE;
1435 int nViewLine = GetViewLineForScreen(nLineIndex);
1436 return IsViewLineEmpty(nViewLine);
1439 bool CBaseView::IsViewLineEmpty(int nViewLine)
1441 if (m_pViewData == 0)
1442 return FALSE;
1443 const DiffStates state = m_pViewData->GetState(nViewLine);
1444 return IsStateEmpty(state);
1447 bool CBaseView::IsLineRemoved(int nLineIndex)
1449 if (m_pViewData == 0)
1450 return FALSE;
1451 int nViewLine = GetViewLineForScreen(nLineIndex);
1452 return IsViewLineRemoved(nViewLine);
1455 bool CBaseView::IsViewLineRemoved(int nViewLine)
1457 if (m_pViewData == 0)
1458 return FALSE;
1459 const DiffStates state = m_pViewData->GetState(nViewLine);
1460 return IsStateRemoved(state);
1463 bool CBaseView::IsViewLineConflicted(int nLineIndex)
1465 if (m_pViewData == 0)
1466 return false;
1467 const DiffStates state = m_pViewData->GetState(nLineIndex);
1468 return IsStateConflicted(state);
1471 COLORREF CBaseView::InlineDiffColor(int nLineIndex)
1473 return IsLineRemoved(nLineIndex) ? m_InlineRemovedBk : m_InlineAddedBk;
1476 COLORREF CBaseView::InlineViewLineDiffColor(int nViewLine)
1478 return IsViewLineRemoved(nViewLine) ? m_InlineRemovedBk : m_InlineAddedBk;
1481 void CBaseView::DrawLineEnding(CDC *pDC, const CRect &rc, int nLineIndex, const CPoint& origin)
1483 if (!(m_bViewWhitespace && m_pViewData && (nLineIndex >= 0) && (nLineIndex < GetLineCount())))
1484 return;
1485 int viewLine = GetViewLineForScreen(nLineIndex);
1486 EOL ending = m_pViewData->GetLineEnding(viewLine);
1487 if (m_bIconLFs)
1489 HICON hEndingIcon = NULL;
1490 switch (ending)
1492 case EOL_CR: hEndingIcon = m_hLineEndingCR; break;
1493 case EOL_CRLF: hEndingIcon = m_hLineEndingCRLF; break;
1494 case EOL_LF: hEndingIcon = m_hLineEndingLF; break;
1495 default: return;
1497 if (origin.x < (rc.left-GetCharWidth()))
1498 return;
1499 // If EOL style has changed, color end-of-line markers as inline differences.
1501 m_bShowInlineDiff && m_pOtherViewData &&
1502 (viewLine < m_pOtherViewData->GetCount()) &&
1503 (ending != EOL_NOENDING) &&
1504 (ending != m_pOtherViewData->GetLineEnding(viewLine) &&
1505 (m_pOtherViewData->GetLineEnding(viewLine) != EOL_NOENDING))
1508 pDC->FillSolidRect(origin.x, origin.y, rc.Height(), rc.Height(), InlineDiffColor(nLineIndex));
1511 DrawIconEx(pDC->GetSafeHdc(), origin.x, origin.y, hEndingIcon, rc.Height(), rc.Height(), NULL, NULL, DI_NORMAL);
1513 else
1515 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1516 CPen * oldpen = pDC->SelectObject(&pen);
1517 int yMiddle = origin.y + rc.Height()/2;
1518 int xMiddle = origin.x+GetCharWidth()/2;
1519 bool bMultiline = false;
1520 if (((int)m_Screen2View.size() > nLineIndex+1) && (GetViewLineForScreen(nLineIndex+1) == viewLine))
1522 if (GetLineLength(nLineIndex+1))
1524 // multiline
1525 bMultiline = true;
1526 pDC->MoveTo(origin.x, yMiddle-2);
1527 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle-2);
1528 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle+2);
1529 pDC->LineTo(origin.x, yMiddle+2);
1531 else if (GetLineLength(nLineIndex) == 0)
1532 bMultiline = true;
1534 else if ((nLineIndex > 0) && (GetViewLineForScreen(nLineIndex-1) == viewLine) && (GetLineLength(nLineIndex) == 0))
1535 bMultiline = true;
1537 if (!bMultiline)
1539 switch (ending)
1541 case EOL_AUTOLINE:
1542 case EOL_CRLF:
1543 // arrow from top to middle+2, then left
1544 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.top+1);
1545 pDC->LineTo(origin.x+GetCharWidth()-1, yMiddle);
1546 case EOL_CR:
1547 // arrow from right to left
1548 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle);
1549 pDC->LineTo(origin.x, yMiddle);
1550 pDC->LineTo(origin.x+4, yMiddle+4);
1551 pDC->MoveTo(origin.x, yMiddle);
1552 pDC->LineTo(origin.x+4, yMiddle-4);
1553 break;
1554 case EOL_LFCR:
1555 // from right-upper to left then down
1556 pDC->MoveTo(origin.x+GetCharWidth()-1, yMiddle-2);
1557 pDC->LineTo(xMiddle, yMiddle-2);
1558 pDC->LineTo(xMiddle, rc.bottom-1);
1559 pDC->LineTo(xMiddle+4, rc.bottom-5);
1560 pDC->MoveTo(xMiddle, rc.bottom-1);
1561 pDC->LineTo(xMiddle-4, rc.bottom-5);
1562 break;
1563 case EOL_LF:
1564 // arrow from top to bottom
1565 pDC->MoveTo(xMiddle, rc.top);
1566 pDC->LineTo(xMiddle, rc.bottom-1);
1567 pDC->LineTo(xMiddle+4, rc.bottom-5);
1568 pDC->MoveTo(xMiddle, rc.bottom-1);
1569 pDC->LineTo(xMiddle-4, rc.bottom-5);
1570 break;
1571 case EOL_FF: // Form Feed, U+000C
1572 case EOL_NEL: // Next Line, U+0085
1573 case EOL_LS: // Line Separator, U+2028
1574 case EOL_PS: // Paragraph Separator, U+2029
1575 // draw a horizontal line at the bottom of this line
1576 pDC->FillSolidRect(rc.left, rc.bottom-1, rc.right, rc.bottom, GetSysColor(COLOR_WINDOWTEXT));
1577 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth()-2);
1578 pDC->LineTo(origin.x, rc.bottom-2);
1579 pDC->LineTo(origin.x+5, rc.bottom-2);
1580 pDC->MoveTo(origin.x, rc.bottom-2);
1581 pDC->LineTo(origin.x+1, rc.bottom-6);
1582 break;
1583 default: // other EOLs
1584 // arrow from top right to bottom left
1585 pDC->MoveTo(origin.x+GetCharWidth()-1, rc.bottom-GetCharWidth());
1586 pDC->LineTo(origin.x, rc.bottom-1);
1587 pDC->LineTo(origin.x+5, rc.bottom-2);
1588 pDC->MoveTo(origin.x, rc.bottom-1);
1589 pDC->LineTo(origin.x+1, rc.bottom-6);
1590 break;
1591 case EOL_NOENDING:
1592 break;
1595 pDC->SelectObject(oldpen);
1599 void CBaseView::DrawBlockLine(CDC *pDC, const CRect &rc, int nLineIndex)
1601 if (!m_bShowSelection)
1602 return;
1604 int nSelBlockStart;
1605 int nSelBlockEnd;
1606 if (!GetViewSelection(nSelBlockStart, nSelBlockEnd))
1607 return;
1609 const int THICKNESS = 2;
1610 COLORREF rectcol = GetSysColor(m_bFocused ? COLOR_WINDOWTEXT : COLOR_GRAYTEXT);
1612 int nViewLineIndex = GetViewLineForScreen(nLineIndex);
1613 int nSubLine = GetSubLineOffset(nLineIndex);
1614 bool bFirstLineOfViewLine = (nSubLine==0 || nSubLine==-1);
1615 if ((nViewLineIndex == nSelBlockStart) && bFirstLineOfViewLine)
1617 pDC->FillSolidRect(rc.left, rc.top, rc.Width(), THICKNESS, rectcol);
1620 bool bLastLineOfViewLine = (nLineIndex+1 == m_Screen2View.size()) || (GetViewLineForScreen(nLineIndex) != GetViewLineForScreen(nLineIndex+1));
1621 if ((nViewLineIndex == nSelBlockEnd) && bLastLineOfViewLine)
1623 pDC->FillSolidRect(rc.left, rc.bottom - THICKNESS, rc.Width(), THICKNESS, rectcol);
1627 void CBaseView::DrawTextLine(
1628 CDC * pDC, const CRect &rc, int nLineIndex, POINT& coords)
1630 ASSERT(nLineIndex < GetLineCount());
1631 int nViewLine = GetViewLineForScreen(nLineIndex);
1632 ASSERT(m_pViewData && (nViewLine < m_pViewData->GetCount()));
1634 LineColors lineCols = GetLineColors(nViewLine);
1636 CString sViewLine = GetViewLineChars(nViewLine);
1637 // mark selection
1638 if (m_bShowSelection && HasTextSelection())
1640 // has this line selection ?
1641 if ((m_ptSelectionViewPosStart.y <= nViewLine) && (nViewLine <= m_ptSelectionViewPosEnd.y))
1643 int nViewLineLength = sViewLine.GetLength();
1645 // first suppose the whole line is selected
1646 int selectedStart = 0;
1647 int selectedEnd = nViewLineLength;
1649 // the view line is partially selected
1650 if (m_ptSelectionViewPosStart.y == nViewLine)
1652 selectedStart = m_ptSelectionViewPosStart.x;
1655 if (m_ptSelectionViewPosEnd.y == nViewLine)
1657 selectedEnd = m_ptSelectionViewPosEnd.x;
1659 // apply selection coloring
1660 // First enforce start and end point
1661 lineCols.SplitBlock(selectedStart);
1662 lineCols.SplitBlock(selectedEnd);
1663 // change color of affected parts
1664 long intenseColorScale = m_bFocused ? 70 : 30;
1665 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(selectedStart);
1666 for ( ; it != lineCols.end() && it->first < selectedEnd; ++it)
1668 COLORREF crBk = CAppUtils::IntenseColor(intenseColorScale, it->second.background);
1669 if (it->second.shot == it->second.background)
1671 it->second.shot = crBk;
1673 it->second.background = crBk;
1674 it->second.text = CAppUtils::IntenseColor(intenseColorScale, it->second.text);
1679 // TODO: remove duplicate from selection and mark
1680 if (!m_sMarkedWord.IsEmpty())
1682 int nMarkLength = m_sMarkedWord.GetLength();
1683 //int nViewLineLength = sViewLine.GetLength();
1684 const TCHAR * text = sViewLine;
1685 const TCHAR * findText = text;
1686 while ((findText = _tcsstr(findText, (LPCTSTR)m_sMarkedWord))!=0)
1688 int nMarkStart = static_cast<int>(findText - text);
1689 int nMarkEnd = nMarkStart + nMarkLength;
1690 // First enforce start and end point
1691 lineCols.SplitBlock(nMarkStart);
1692 lineCols.SplitBlock(nMarkEnd);
1693 // change color of affected parts
1694 const long int nIntenseColorScale = 200;
1695 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart);
1696 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1698 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1699 if (it->second.shot == it->second.background)
1701 it->second.shot = crBk;
1703 it->second.background = crBk;
1704 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1706 findText += nMarkLength;
1709 if (!m_sFindText.IsEmpty())
1711 int nMarkStart = 0;
1712 int nMarkEnd = 0;
1713 int nStringPos = nMarkStart;
1714 CString searchLine = sViewLine;
1715 if (!m_bMatchCase)
1716 searchLine.MakeLower();
1717 while (StringFound(searchLine, SearchNext, nMarkStart, nMarkEnd)!=0)
1719 // First enforce start and end point
1720 lineCols.SplitBlock(nMarkStart+nStringPos);
1721 lineCols.SplitBlock(nMarkEnd+nStringPos);
1722 // change color of affected parts
1723 const long int nIntenseColorScale = 30;
1724 std::map<int, linecolors_t>::iterator it = lineCols.lower_bound(nMarkStart+nStringPos);
1725 for ( ; it != lineCols.end() && it->first < nMarkEnd; ++it)
1727 COLORREF crBk = CAppUtils::IntenseColor(nIntenseColorScale, it->second.background);
1728 if (it->second.shot == it->second.background)
1730 it->second.shot = crBk;
1732 it->second.background = crBk;
1733 it->second.text = CAppUtils::IntenseColor(nIntenseColorScale, it->second.text);
1735 searchLine = searchLine.Mid(nMarkEnd);
1736 nStringPos = nMarkEnd;
1740 // @ this point we may cache data for next line which may be same in wrapped mode
1742 int nTextOffset = 0;
1743 int nSubline = GetSubLineOffset(nLineIndex);
1744 for (int n=0; n<nSubline; n++)
1746 CString sLine = m_ScreenedViewLine[nViewLine].SubLines[n];
1747 nTextOffset += sLine.GetLength();
1750 CString sLine = GetLineChars(nLineIndex);
1751 int nLineLength = sLine.GetLength();
1752 CString sLineExp = ExpandChars(sLine);
1753 LPCTSTR textExp = sLineExp;
1754 //int nLineLengthExp = sLineExp.GetLength();
1755 int nStartExp = 0;
1756 int nLeft = coords.x;
1757 for (std::map<int, linecolors_t>::const_iterator itStart = lineCols.begin(); itStart != lineCols.end(); ++itStart)
1759 std::map<int, linecolors_t>::const_iterator itEnd = itStart;
1760 ++itEnd;
1761 int nStart = std::max<int>(0, itStart->first - nTextOffset);
1762 int nEnd = nLineLength;
1763 if (itEnd != lineCols.end())
1765 nEnd = std::min<int>(nEnd, itEnd->first - nTextOffset);
1767 int nBlockLength = nEnd - nStart;
1768 if (nBlockLength > 0 && nEnd>=0)
1770 pDC->SetBkColor(itStart->second.background);
1771 pDC->SetTextColor(itStart->second.text);
1772 int nEndExp = CountExpandedChars(sLine, nEnd);
1773 int nTextLength = nEndExp - nStartExp;
1774 LPCTSTR p_zBlockText = textExp + nStartExp;
1775 SIZE Size;
1776 GetTextExtentPoint32(pDC->GetSafeHdc(), p_zBlockText, nTextLength, &Size); // falls time-2-tme
1777 int nRight = nLeft + Size.cx;
1778 if ((nRight > rc.left) && (nLeft < rc.right))
1780 // note: ExtTextOut has a limit for the length of the string. That limit is supposed
1781 // to be 8192, but that's not really true: I found that the limit (at least on my machine and a few others)
1782 // is 4094 (4095 doesn't work anymore).
1783 // So we limit the length here to that 4094 chars.
1784 // In case we're scrolled to the right, there's no need to draw the string
1785 // from way outside our window, so we also offset the drawing to the start of the window.
1786 // This reduces the string length as well.
1787 int offset = 0;
1788 int leftcoord = nLeft;
1789 if (nLeft < 0)
1791 offset = (-nLeft/GetCharWidth());
1792 nTextLength -= offset;
1793 leftcoord = nLeft % GetCharWidth();
1796 pDC->ExtTextOut(leftcoord, coords.y, ETO_CLIPPED, &rc, p_zBlockText+offset, min(nTextLength, 4094), NULL);
1797 if ((itStart->second.shot != itStart->second.background) && (itStart->first == nStart + nTextOffset))
1799 pDC->FillSolidRect(nLeft-1, rc.top, 1, rc.Height(), itStart->second.shot);
1802 nLeft = nRight;
1803 coords.x = nRight;
1804 nStartExp = nEndExp;
1809 void CBaseView::DrawSingleLine(CDC *pDC, const CRect &rc, int nLineIndex)
1811 if (nLineIndex >= GetLineCount())
1812 nLineIndex = -1;
1813 ASSERT(nLineIndex >= -1);
1815 if ((nLineIndex == -1) || !m_pViewData)
1817 // Draw line beyond the text
1818 COLORREF crBkgnd, crText;
1819 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1820 pDC->FillSolidRect(rc, crBkgnd);
1821 return;
1824 int viewLine = GetViewLineForScreen(nLineIndex);
1825 if (m_pMainFrame->m_bCollapsed)
1827 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
1829 COLORREF crBkgnd, crText;
1830 CDiffColors::GetInstance().GetColors(DIFFSTATE_UNKNOWN, crBkgnd, crText);
1831 pDC->FillSolidRect(rc, crBkgnd);
1833 const int THICKNESS = 2;
1834 COLORREF rectcol = GetSysColor(COLOR_WINDOWTEXT);
1835 pDC->FillSolidRect(rc.left, rc.top + (rc.Height()/2), rc.Width(), THICKNESS, rectcol);
1836 pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
1837 pDC->SetBkColor(crBkgnd);
1838 CRect rect = rc;
1839 pDC->DrawText(_T("{...}"), &rect, DT_NOPREFIX|DT_SINGLELINE|DT_CENTER);
1840 return;
1844 DiffStates diffState = m_pViewData->GetState(viewLine);
1845 COLORREF crBkgnd, crText;
1846 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
1848 if (diffState == DIFFSTATE_CONFLICTED)
1850 // conflicted lines are shown without 'text' on them
1851 CRect rect = rc;
1852 pDC->FillSolidRect(rc, crBkgnd);
1853 // now draw some faint text patterns
1854 pDC->SetTextColor(CAppUtils::IntenseColor(130, crBkgnd));
1855 pDC->DrawText(m_sConflictedText, rect, DT_LEFT|DT_NOPREFIX|DT_SINGLELINE);
1856 DrawBlockLine(pDC, rc, nLineIndex);
1857 return;
1860 CPoint origin(rc.left - m_nOffsetChar * GetCharWidth(), rc.top);
1861 CString sLine = GetLineChars(nLineIndex);
1862 if (sLine.IsEmpty())
1864 pDC->FillSolidRect(rc, crBkgnd);
1865 DrawBlockLine(pDC, rc, nLineIndex);
1866 DrawLineEnding(pDC, rc, nLineIndex, origin);
1867 return;
1870 CheckOtherView();
1872 // Draw the line
1874 pDC->SelectObject(GetFont(FALSE, FALSE));
1876 DrawTextLine(pDC, rc, nLineIndex, origin);
1878 // draw white space after the end of line
1879 CRect frect = rc;
1880 if (origin.x > frect.left)
1881 frect.left = origin.x;
1882 if (frect.right > frect.left)
1883 pDC->FillSolidRect(frect, crBkgnd);
1885 // draw the whitespace chars
1886 LPCTSTR pszChars = (LPCWSTR)sLine;
1887 if (m_bViewWhitespace)
1889 int xpos = 0;
1890 int y = rc.top + (rc.bottom-rc.top)/2;
1892 int nActualOffset = 0;
1893 while ((nActualOffset < m_nOffsetChar) && (*pszChars))
1895 if (*pszChars == _T('\t'))
1896 nActualOffset += (GetTabSize() - nActualOffset % GetTabSize());
1897 else
1898 nActualOffset++;
1899 pszChars++;
1901 if (nActualOffset > m_nOffsetChar)
1902 pszChars--;
1904 CPen pen(PS_SOLID, 0, m_WhiteSpaceFg);
1905 CPen pen2(PS_SOLID, 2, m_WhiteSpaceFg);
1906 while (*pszChars)
1908 switch (*pszChars)
1910 case _T('\t'):
1912 // draw an arrow
1913 CPen * oldPen = pDC->SelectObject(&pen);
1914 int nSpaces = GetTabSize() - (m_nOffsetChar + xpos) % GetTabSize();
1915 pDC->MoveTo(xpos * GetCharWidth() + rc.left, y);
1916 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1917 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y-4);
1918 pDC->MoveTo((xpos + nSpaces) * GetCharWidth() + rc.left-2, y);
1919 pDC->LineTo((xpos + nSpaces) * GetCharWidth() + rc.left-6, y+4);
1920 xpos += nSpaces;
1921 pDC->SelectObject(oldPen);
1923 break;
1924 case _T(' '):
1926 // draw a small dot
1927 CPen * oldPen = pDC->SelectObject(&pen2);
1928 pDC->MoveTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2-1, y);
1929 pDC->LineTo(xpos * GetCharWidth() + rc.left + GetCharWidth()/2+1, y);
1930 xpos++;
1931 pDC->SelectObject(oldPen);
1933 break;
1934 default:
1935 xpos++;
1936 break;
1938 pszChars++;
1941 DrawBlockLine(pDC, rc, nLineIndex);
1942 if (origin.x >= rc.left)
1943 DrawLineEnding(pDC, rc, nLineIndex, origin);
1946 void CBaseView::ExpandChars(const CString &sLine, int nOffset, int nCount, CString &line)
1948 if (nCount <= 0)
1950 line = _T("");
1951 return;
1954 int nTabSize = GetTabSize();
1956 int nActualOffset = CountExpandedChars(sLine, nOffset);
1958 LPCTSTR pszChars = (LPCWSTR)sLine;
1959 pszChars += nOffset;
1960 int nLength = nCount;
1962 int nTabCount = 0;
1963 for (int i=0; i<nLength; i++)
1965 if (pszChars[i] == _T('\t'))
1966 nTabCount ++;
1969 LPTSTR pszBuf = line.GetBuffer(nLength + nTabCount * (nTabSize - 1) + 1);
1970 int nCurPos = 0;
1971 if (nTabCount > 0 || m_bViewWhitespace)
1973 for (int i=0; i<nLength; i++)
1975 if (pszChars[i] == _T('\t'))
1977 int nSpaces = nTabSize - (nActualOffset + nCurPos) % nTabSize;
1978 while (nSpaces > 0)
1980 pszBuf[nCurPos ++] = _T(' ');
1981 nSpaces --;
1984 else
1986 pszBuf[nCurPos] = pszChars[i];
1987 nCurPos ++;
1991 else
1993 memcpy(pszBuf, pszChars, sizeof(TCHAR) * nLength);
1994 nCurPos = nLength;
1996 pszBuf[nCurPos] = 0;
1997 line.ReleaseBuffer();
2000 CString CBaseView::ExpandChars(const CString &sLine, int nOffset)
2002 CString sRet;
2003 int nLength = sLine.GetLength();
2004 ExpandChars(sLine, nOffset, nLength, sRet);
2005 return sRet;
2008 int CBaseView::CountExpandedChars(const CString &sLine, int nLength)
2010 int nTabSize = GetTabSize();
2012 int nActualOffset = 0;
2013 for (int i=0; i<nLength; i++)
2015 if (sLine[i] == _T('\t'))
2016 nActualOffset += (nTabSize - nActualOffset % nTabSize);
2017 else
2018 nActualOffset ++;
2020 return nActualOffset;
2023 void CBaseView::ScrollAllToLine(int nNewTopLine, BOOL bTrackScrollBar)
2025 if (m_pwndLeft)
2026 m_pwndLeft->ScrollToLine(nNewTopLine, bTrackScrollBar);
2027 if (m_pwndRight)
2028 m_pwndRight->ScrollToLine(nNewTopLine, bTrackScrollBar);
2029 if (m_pwndBottom)
2030 m_pwndBottom->ScrollToLine(nNewTopLine, bTrackScrollBar);
2031 if (m_pwndLocator)
2032 m_pwndLocator->Invalidate();
2035 void CBaseView::GoToLine(int nNewLine, BOOL bAll)
2037 //almost the same as ScrollAllToLine, but try to put the line in the
2038 //middle of the view, not on top
2039 int nNewTopLine = nNewLine - GetScreenLines()/2;
2040 if (nNewTopLine < 0)
2041 nNewTopLine = 0;
2042 if (nNewTopLine >= (int)m_Screen2View.size())
2043 nNewTopLine = (int)m_Screen2View.size()-1;
2044 if (bAll)
2045 ScrollAllToLine(nNewTopLine);
2046 else
2047 ScrollToLine(nNewTopLine);
2050 BOOL CBaseView::OnEraseBkgnd(CDC* /*pDC*/)
2052 return TRUE;
2055 int CBaseView::OnCreate(LPCREATESTRUCT lpCreateStruct)
2057 if (CView::OnCreate(lpCreateStruct) == -1)
2058 return -1;
2060 memset(&m_lfBaseFont, 0, sizeof(m_lfBaseFont));
2061 //lstrcpy(m_lfBaseFont.lfFaceName, _T("Courier New"));
2062 //lstrcpy(m_lfBaseFont.lfFaceName, _T("FixedSys"));
2063 m_lfBaseFont.lfHeight = 0;
2064 m_lfBaseFont.lfWeight = FW_NORMAL;
2065 m_lfBaseFont.lfItalic = FALSE;
2066 m_lfBaseFont.lfCharSet = DEFAULT_CHARSET;
2067 m_lfBaseFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
2068 m_lfBaseFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
2069 m_lfBaseFont.lfQuality = DEFAULT_QUALITY;
2070 m_lfBaseFont.lfPitchAndFamily = DEFAULT_PITCH;
2072 return 0;
2075 void CBaseView::OnDestroy()
2077 if ((m_pFindDialog)&&(!m_pFindDialog->IsTerminating()))
2079 m_pFindDialog->SendMessage(WM_CLOSE);
2080 return;
2082 CView::OnDestroy();
2083 DeleteFonts();
2084 ReleaseBitmap();
2087 void CBaseView::OnSize(UINT nType, int cx, int cy)
2089 CView::OnSize(nType, cx, cy);
2090 ReleaseBitmap();
2092 m_nScreenLines = -1;
2093 m_nScreenChars = -1;
2094 if (m_nLastScreenChars != GetScreenChars())
2096 BuildAllScreen2ViewVector();
2097 m_nLastScreenChars = m_nScreenChars;
2098 if (m_pMainFrame && m_pMainFrame->m_bWrapLines)
2100 // if we're in wrap mode, the line wrapping most likely changed
2101 // and that means we have to redraw the whole window, not just the
2102 // scrolled part.
2103 Invalidate(FALSE);
2105 else
2107 // make sure the view header is redrawn
2108 CRect rcScroll;
2109 GetClientRect(&rcScroll);
2110 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2111 InvalidateRect(&rcScroll, FALSE);
2114 else
2116 // make sure the view header is redrawn
2117 CRect rcScroll;
2118 GetClientRect(&rcScroll);
2119 rcScroll.bottom = GetLineHeight()+HEADERHEIGHT;
2120 InvalidateRect(&rcScroll, FALSE);
2122 UpdateLocator();
2123 RecalcVertScrollBar();
2124 RecalcHorzScrollBar();
2126 UpdateCaret();
2129 BOOL CBaseView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
2131 if (m_pwndLeft)
2132 m_pwndLeft->OnDoMouseWheel(nFlags, zDelta, pt);
2133 if (m_pwndRight)
2134 m_pwndRight->OnDoMouseWheel(nFlags, zDelta, pt);
2135 if (m_pwndBottom)
2136 m_pwndBottom->OnDoMouseWheel(nFlags, zDelta, pt);
2137 if (m_pwndLocator)
2138 m_pwndLocator->Invalidate();
2139 return CView::OnMouseWheel(nFlags, zDelta, pt);
2142 void CBaseView::OnMouseHWheel(UINT nFlags, short zDelta, CPoint pt)
2144 if (m_pwndLeft)
2145 m_pwndLeft->OnDoMouseHWheel(nFlags, zDelta, pt);
2146 if (m_pwndRight)
2147 m_pwndRight->OnDoMouseHWheel(nFlags, zDelta, pt);
2148 if (m_pwndBottom)
2149 m_pwndBottom->OnDoMouseHWheel(nFlags, zDelta, pt);
2150 if (m_pwndLocator)
2151 m_pwndLocator->Invalidate();
2154 void CBaseView::OnDoMouseWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2156 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2157 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2159 if (bControl || bShift)
2161 if (m_pMainFrame->m_bWrapLines)
2162 return;
2163 // Ctrl-Wheel scrolls sideways
2164 ScrollSide(-zDelta/30);
2166 else
2168 ScrollVertical(zDelta);
2172 void CBaseView::OnDoMouseHWheel(UINT /*nFlags*/, short zDelta, CPoint /*pt*/)
2174 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2175 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2177 if (bControl || bShift)
2179 ScrollVertical(zDelta);
2181 else
2183 if (m_pMainFrame->m_bWrapLines)
2184 return;
2185 // Ctrl-Wheel scrolls sideways
2186 ScrollSide(-zDelta/30);
2190 BOOL CBaseView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
2192 if (nHitTest == HTCLIENT)
2194 if ((m_pViewData)&&(m_pMainFrame->m_bCollapsed))
2196 if (m_nMouseLine < (int)m_Screen2View.size())
2198 if (m_nMouseLine >= 0)
2200 int viewLine = GetViewLineForScreen(m_nMouseLine);
2201 if (viewLine < m_pViewData->GetCount())
2203 if (m_pViewData->GetHideState(viewLine) == HIDESTATE_MARKER)
2205 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_HAND)));
2206 return TRUE;
2212 if (m_mouseInMargin)
2214 ::SetCursor(m_margincursor);
2215 return TRUE;
2217 if (m_nMouseLine >= 0)
2219 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_IBEAM))); // Set To Edit Cursor
2220 return TRUE;
2223 ::SetCursor(::LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW))); // Set To Arrow Cursor
2224 return TRUE;
2226 return CView::OnSetCursor(pWnd, nHitTest, message);
2229 void CBaseView::OnKillFocus(CWnd* pNewWnd)
2231 CView::OnKillFocus(pNewWnd);
2232 m_bFocused = FALSE;
2233 UpdateCaret();
2234 Invalidate();
2237 void CBaseView::OnSetFocus(CWnd* pOldWnd)
2239 CView::OnSetFocus(pOldWnd);
2240 m_bFocused = TRUE;
2241 UpdateCaret();
2242 Invalidate();
2245 int CBaseView::GetLineFromPoint(CPoint point)
2247 ScreenToClient(&point);
2248 return (((point.y - HEADERHEIGHT) / GetLineHeight()) + m_nTopLine);
2251 void CBaseView::OnContextMenu(CPoint point, DiffStates state)
2253 if (!this->IsWindowVisible())
2254 return;
2256 CIconMenu popup;
2257 if (!popup.CreatePopupMenu())
2258 return;
2260 AddContextItems(popup, state);
2262 CompensateForKeyboard(point);
2264 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY | TPM_RIGHTBUTTON, point.x, point.y, this, 0);
2265 ResetUndoStep();
2266 switch (cmd)
2268 // 2-pane view commands; target is right view
2269 case POPUPCOMMAND_USELEFTBLOCK:
2270 m_pwndRight->UseLeftBlock();
2271 break;
2272 case POPUPCOMMAND_USELEFTFILE:
2273 m_pwndRight->UseLeftFile();
2274 break;
2275 case POPUPCOMMAND_USEBOTHLEFTFIRST:
2276 m_pwndRight->UseBothLeftFirst();
2277 break;
2278 case POPUPCOMMAND_USEBOTHRIGHTFIRST:
2279 m_pwndRight->UseBothRightFirst();
2280 break;
2281 // 3-pane view commands; target is bottom view
2282 case POPUPCOMMAND_USEYOURANDTHEIRBLOCK:
2283 m_pwndBottom->UseBothRightFirst();
2284 break;
2285 case POPUPCOMMAND_USETHEIRANDYOURBLOCK:
2286 m_pwndBottom->UseBothLeftFirst();
2287 break;
2288 case POPUPCOMMAND_USEYOURBLOCK:
2289 m_pwndBottom->UseRightBlock();
2290 break;
2291 case POPUPCOMMAND_USEYOURFILE:
2292 m_pwndBottom->UseRightFile();
2293 break;
2294 case POPUPCOMMAND_USETHEIRBLOCK:
2295 m_pwndBottom->UseLeftBlock();
2296 break;
2297 case POPUPCOMMAND_USETHEIRFILE:
2298 m_pwndBottom->UseLeftFile();
2299 break;
2300 // copy, cut and paste commands
2301 case ID_EDIT_COPY:
2302 OnEditCopy();
2303 break;
2304 case ID_EDIT_CUT:
2305 OnEditCut();
2306 break;
2307 case ID_EDIT_PASTE:
2308 OnEditPaste();
2309 break;
2310 default:
2311 return;
2312 } // switch (cmd)
2313 SaveUndoStep(); // all except copy, cut paste save undo step, but this should not be harmful as step is empty already and thus not saved
2314 return;
2317 void CBaseView::OnContextMenu(CWnd* /*pWnd*/, CPoint point)
2319 if (!m_pViewData)
2320 return;
2322 int nViewBlockStart = -1;
2323 int nViewBlockEnd = -1;
2324 GetViewSelection(nViewBlockStart, nViewBlockEnd);
2325 if ((point.x >= 0) && (point.y >= 0))
2327 int nLine = GetLineFromPoint(point)-1;
2328 if ((nLine >= 0) && (nLine < m_Screen2View.size()))
2330 int nViewLine = GetViewLineForScreen(nLine);
2331 if (((nViewLine < nViewBlockStart) || (nViewBlockEnd < nViewLine)))
2333 ClearSelection(); // Clear text-copy selection
2335 nViewBlockStart = nViewLine;
2336 nViewBlockEnd = nViewLine;
2337 DiffStates state = m_pViewData->GetState(nViewLine);
2338 while (nViewBlockStart > 0)
2340 const DiffStates lineState = m_pViewData->GetState(nViewBlockStart-1);
2341 if (!LinesInOneChange(-1, state, lineState))
2342 break;
2343 nViewBlockStart--;
2346 while (nViewBlockEnd < (m_pViewData->GetCount()-1))
2348 const DiffStates lineState = m_pViewData->GetState(nViewBlockEnd+1);
2349 if (!LinesInOneChange(1, state, lineState))
2350 break;
2351 nViewBlockEnd++;
2354 SetupAllViewSelection(nViewBlockStart, nViewBlockEnd);
2355 UpdateCaretPosition(point);
2360 // FixSelection(); fix selection range
2361 /*if (m_nSelBlockEnd >= m_pViewData->GetCount())
2362 m_nSelBlockEnd = m_pViewData->GetCount()-1;//*/
2364 DiffStates state = DIFFSTATE_UNKNOWN;
2365 if (GetViewSelection(nViewBlockStart, nViewBlockEnd))
2367 // find a more 'relevant' state in the selection
2368 for (int i=nViewBlockStart; i<=nViewBlockEnd; ++i)
2370 state = m_pViewData->GetState(i);
2371 if ((state != DIFFSTATE_NORMAL) && (state != DIFFSTATE_UNKNOWN))
2372 break;
2375 OnContextMenu(point, state);
2378 void CBaseView::RefreshViews()
2380 if (m_pwndLeft)
2382 m_pwndLeft->UpdateStatusBar();
2383 m_pwndLeft->Invalidate();
2385 if (m_pwndRight)
2387 m_pwndRight->UpdateStatusBar();
2388 m_pwndRight->Invalidate();
2390 if (m_pwndBottom)
2392 m_pwndBottom->UpdateStatusBar();
2393 m_pwndBottom->Invalidate();
2395 if (m_pwndLocator)
2396 m_pwndLocator->Invalidate();
2399 void CBaseView::GoToFirstDifference()
2401 SetCaretToFirstViewLine();
2402 SelectNextBlock(1, false, false);
2405 void CBaseView::GoToFirstConflict()
2407 SetCaretToFirstViewLine();
2408 SelectNextBlock(1, true, false);
2411 void CBaseView::HighlightLines(int nStart, int nEnd /* = -1 */)
2413 ClearSelection();
2414 SetupAllSelection(nStart, max(nStart, nEnd));
2416 UpdateCaretPosition(SetupPoint(0, nStart));
2417 Invalidate();
2420 void CBaseView::HighlightViewLines(int nStart, int nEnd /* = -1 */)
2422 ClearSelection();
2423 SetupAllViewSelection(nStart, max(nStart, nEnd));
2425 UpdateCaretViewPosition(SetupPoint(0, nStart));
2426 Invalidate();
2429 void CBaseView::SetupAllViewSelection(int start, int end)
2431 SetupViewSelection(m_pwndBottom, start, end);
2432 SetupViewSelection(m_pwndLeft, start, end);
2433 SetupViewSelection(m_pwndRight, start, end);
2436 void CBaseView::SetupAllSelection(int start, int end)
2438 SetupAllViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2441 //void CBaseView::SetupSelection(CBaseView* view, int start, int end) { }
2443 void CBaseView::SetupSelection(int start, int end)
2445 SetupViewSelection(GetViewLineForScreen(start), GetViewLineForScreen(end));
2448 void CBaseView::SetupViewSelection(CBaseView* view, int start, int end)
2450 if (!IsViewGood(view))
2451 return;
2452 view->SetupViewSelection(start, end);
2455 void CBaseView::SetupViewSelection(int start, int end)
2457 // clear text selection before setting line selection ?
2458 m_nSelViewBlockStart = start;
2459 m_nSelViewBlockEnd = end;
2460 Invalidate();
2464 void CBaseView::OnMergePreviousconflict()
2466 SelectNextBlock(-1, true);
2469 void CBaseView::OnMergeNextconflict()
2471 SelectNextBlock(1, true);
2474 void CBaseView::OnMergeNextdifference()
2476 SelectNextBlock(1, false);
2479 void CBaseView::OnMergePreviousdifference()
2481 SelectNextBlock(-1, false);
2484 bool CBaseView::HasNextConflict()
2486 return SelectNextBlock(1, true, true, true);
2489 bool CBaseView::HasPrevConflict()
2491 return SelectNextBlock(-1, true, true, true);
2494 bool CBaseView::HasNextDiff()
2496 return SelectNextBlock(1, false, true, true);
2499 bool CBaseView::HasPrevDiff()
2501 return SelectNextBlock(-1, false, true, true);
2504 bool CBaseView::LinesInOneChange(int direction,
2505 DiffStates initialLineState, DiffStates currentLineState)
2507 // Checks whether all the adjacent lines starting from the initial line
2508 // and up to the current line form the single change
2510 // Do not distinguish between moved and added/removed lines
2511 if (initialLineState == DIFFSTATE_MOVED_TO)
2512 initialLineState = DIFFSTATE_ADDED;
2513 if (initialLineState == DIFFSTATE_MOVED_FROM)
2514 initialLineState = DIFFSTATE_REMOVED;
2515 if (currentLineState == DIFFSTATE_MOVED_TO)
2516 currentLineState = DIFFSTATE_ADDED;
2517 if (currentLineState == DIFFSTATE_MOVED_FROM)
2518 currentLineState = DIFFSTATE_REMOVED;
2520 // First of all, if the two lines have identical states, they surely
2521 // belong to one change.
2522 if (initialLineState == currentLineState)
2523 return true;
2525 // Either we move down and initial line state is "added" or "removed" and
2526 // current line state is "empty"...
2527 if (direction > 0)
2529 if (currentLineState == DIFFSTATE_EMPTY)
2531 if (initialLineState == DIFFSTATE_ADDED || initialLineState == DIFFSTATE_REMOVED)
2532 return true;
2534 if (initialLineState == DIFFSTATE_CONFLICTADDED && currentLineState == DIFFSTATE_CONFLICTEMPTY)
2535 return true;
2537 // ...or we move up and initial line state is "empty" and current line
2538 // state is "added" or "removed".
2539 if (direction < 0)
2541 if (initialLineState == DIFFSTATE_EMPTY)
2543 if (currentLineState == DIFFSTATE_ADDED || currentLineState == DIFFSTATE_REMOVED)
2544 return true;
2546 if (initialLineState == DIFFSTATE_CONFLICTEMPTY && currentLineState == DIFFSTATE_CONFLICTADDED)
2547 return true;
2549 return false;
2552 bool CBaseView::SelectNextBlock(int nDirection, bool bConflict, bool bSkipEndOfCurrentBlock /* = true */, bool dryrun /* = false */)
2554 if (! m_pViewData)
2555 return false;
2557 const int linesCount = (int)m_Screen2View.size();
2558 if(linesCount == 0)
2559 return false;
2561 int nCenterPos = GetCaretPosition().y;
2562 int nLimit = -1;
2563 if (nDirection > 0)
2564 nLimit = linesCount;
2566 if (nCenterPos >= linesCount)
2567 nCenterPos = linesCount-1;
2569 if (bSkipEndOfCurrentBlock)
2571 // Find end of current block
2572 const DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2573 while (nCenterPos != nLimit)
2575 const DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2576 if (!LinesInOneChange(nDirection, state, lineState))
2577 break;
2578 nCenterPos += nDirection;
2582 // Find next diff/conflict block
2583 while (nCenterPos != nLimit)
2585 DiffStates linestate = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2586 if (!bConflict &&
2587 (linestate != DIFFSTATE_NORMAL) &&
2588 (linestate != DIFFSTATE_UNKNOWN))
2590 break;
2592 if (bConflict &&
2593 ((linestate == DIFFSTATE_CONFLICTADDED) ||
2594 (linestate == DIFFSTATE_CONFLICTED_IGNORED) ||
2595 (linestate == DIFFSTATE_CONFLICTED) ||
2596 (linestate == DIFFSTATE_CONFLICTEMPTY)))
2598 break;
2601 nCenterPos += nDirection;
2603 if (nCenterPos == nLimit)
2604 return false;
2605 if (dryrun)
2606 return (nCenterPos != nLimit);
2608 // Find end of new block
2609 DiffStates state = m_pViewData->GetState(GetViewLineForScreen(nCenterPos));
2610 int nBlockEnd = nCenterPos;
2611 const int maxAllowedLine = nLimit-nDirection;
2612 while (nBlockEnd != maxAllowedLine)
2614 const int lineIndex = nBlockEnd + nDirection;
2615 if (lineIndex >= linesCount)
2616 break;
2617 DiffStates lineState = m_pViewData->GetState(GetViewLineForScreen(lineIndex));
2618 if (!LinesInOneChange(nDirection, state, lineState))
2619 break;
2620 nBlockEnd += nDirection;
2623 int nTopPos = nCenterPos - (GetScreenLines()/2);
2624 if (nTopPos < 0)
2625 nTopPos = 0;
2627 POINT ptCaretPos = {0, nCenterPos};
2628 SetCaretPosition(ptCaretPos);
2629 ClearSelection();
2630 if (nDirection > 0)
2631 SetupAllSelection(nCenterPos, nBlockEnd);
2632 else
2633 SetupAllSelection(nBlockEnd, nCenterPos);
2635 ScrollAllToLine(nTopPos, FALSE);
2636 RecalcAllVertScrollBars(TRUE);
2637 SetCaretToLineStart();
2638 EnsureCaretVisible();
2639 OnNavigateNextinlinediff();
2641 UpdateViewsCaretPosition();
2642 UpdateCaret();
2643 ShowDiffLines(nCenterPos);
2644 return true;
2647 BOOL CBaseView::OnToolTipNotify(UINT /*id*/, NMHDR *pNMHDR, LRESULT *pResult)
2649 if (pNMHDR->idFrom != (UINT)m_hWnd)
2650 return FALSE;
2652 CString strTipText;
2653 strTipText = m_sWindowName + _T("\r\n") + m_sFullFilePath;
2655 DWORD pos = GetMessagePos();
2656 CPoint point(GET_X_LPARAM(pos), GET_Y_LPARAM(pos));
2657 ScreenToClient(&point);
2658 const int nLine = GetButtonEventLineIndex(point);
2660 if (nLine >= 0)
2662 int nViewLine = GetViewLineForScreen(nLine);
2663 if((m_pViewData)&&(nViewLine < m_pViewData->GetCount()))
2665 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)
2667 strTipText.Format(IDS_MOVED_TO_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2669 if (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO)
2671 strTipText.Format(IDS_MOVED_FROM_TT, m_pViewData->GetMovedIndex(nViewLine)+1);
2677 *pResult = 0;
2678 if (strTipText.IsEmpty())
2679 return TRUE;
2681 // need to handle both ANSI and UNICODE versions of the message
2682 if (pNMHDR->code == TTN_NEEDTEXTA)
2684 TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
2685 pTTTA->lpszText = m_szTip;
2686 WideCharToMultiByte(CP_ACP, 0, strTipText, -1, m_szTip, strTipText.GetLength()+1, 0, 0);
2688 else
2690 TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
2691 lstrcpyn(m_wszTip, strTipText, strTipText.GetLength()+1);
2692 pTTTW->lpszText = m_wszTip;
2695 return TRUE; // message was handled
2698 INT_PTR CBaseView::OnToolHitTest(CPoint point, TOOLINFO* pTI) const
2700 CRect rcClient;
2701 GetClientRect(rcClient);
2702 CRect textrect(rcClient.left, rcClient.top, rcClient.Width(), m_nLineHeight+HEADERHEIGHT);
2703 int marginwidth = MARGINWIDTH;
2704 if ((m_bViewLinenumbers)&&(m_pViewData)&&(m_pViewData->GetCount())&&(m_nDigits > 0))
2706 marginwidth = (MARGINWIDTH + (m_nDigits * m_nCharWidth) + 2);
2708 CRect borderrect(rcClient.left, rcClient.top+m_nLineHeight+HEADERHEIGHT, marginwidth, rcClient.bottom);
2710 if (textrect.PtInRect(point) || borderrect.PtInRect(point))
2712 // inside the header part of the view (showing the filename)
2713 pTI->hwnd = this->m_hWnd;
2714 this->GetClientRect(&pTI->rect);
2715 pTI->uFlags |= TTF_ALWAYSTIP | TTF_IDISHWND;
2716 pTI->uId = (UINT)m_hWnd;
2717 pTI->lpszText = LPSTR_TEXTCALLBACK;
2719 // we want multi line tooltips
2720 CToolTipCtrl* pToolTip = AfxGetModuleThreadState()->m_pToolTip;
2721 if (pToolTip->GetSafeHwnd() != NULL)
2723 pToolTip->SetMaxTipWidth(INT_MAX);
2726 return (textrect.PtInRect(point) ? 1 : 2);
2729 return -1;
2732 void CBaseView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
2734 bool bControl = !!(GetKeyState(VK_CONTROL)&0x8000);
2735 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
2737 switch (nChar)
2739 case VK_TAB:
2740 if ((nChar == '\t') && bControl)
2742 if (this==m_pwndLeft)
2744 if (IsViewGood(m_pwndRight))
2746 m_pwndRight->SetFocus();
2748 else if (IsViewGood(m_pwndBottom))
2750 m_pwndBottom->SetFocus();
2753 else if (this==m_pwndRight)
2755 if (IsViewGood(m_pwndBottom))
2757 m_pwndBottom->SetFocus();
2759 else if (IsViewGood(m_pwndLeft))
2761 m_pwndLeft->SetFocus();
2764 else if (this==m_pwndBottom)
2766 if (IsViewGood(m_pwndLeft))
2768 m_pwndLeft->SetFocus();
2770 else if (IsViewGood(m_pwndRight))
2772 m_pwndRight->SetFocus();
2776 break;
2777 case VK_PRIOR:
2779 POINT ptCaretPos = GetCaretPosition();
2780 ptCaretPos.y -= GetScreenLines();
2781 ptCaretPos.y = max(ptCaretPos.y, 0);
2782 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2783 SetCaretPosition(ptCaretPos);
2784 OnCaretMove(MOVELEFT, bShift);
2785 ShowDiffLines(ptCaretPos.y);
2787 break;
2788 case VK_NEXT:
2790 POINT ptCaretPos = GetCaretPosition();
2791 ptCaretPos.y += GetScreenLines();
2792 if (ptCaretPos.y >= GetLineCount())
2793 ptCaretPos.y = GetLineCount()-1;
2794 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
2795 SetCaretPosition(ptCaretPos);
2796 OnCaretMove(MOVERIGHT, bShift);
2797 ShowDiffLines(ptCaretPos.y);
2799 break;
2800 case VK_HOME:
2802 if (bControl)
2804 ScrollAllToLine(0);
2805 SetCaretToViewStart();
2806 m_nCaretGoalPos = 0;
2807 if (bShift)
2808 AdjustSelection(MOVELEFT);
2809 else
2810 ClearSelection();
2811 UpdateCaret();
2813 else
2815 SetCaretToLineStart();
2816 m_nCaretGoalPos = 0;
2817 OnCaretMove(MOVERIGHT, bShift);
2818 ScrollAllToChar(0);
2821 break;
2822 case VK_END:
2824 if (bControl)
2826 ScrollAllToLine(GetLineCount()-GetAllMinScreenLines());
2827 POINT ptCaretPos;
2828 ptCaretPos.y = GetLineCount()-1;
2829 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2830 SetCaretAndGoalPosition(ptCaretPos);
2831 if (bShift)
2832 AdjustSelection(MOVERIGHT);
2833 else
2834 ClearSelection();
2836 else
2838 POINT ptCaretPos = GetCaretPosition();
2839 ptCaretPos.x = GetLineLength(ptCaretPos.y);
2840 if ((GetSubLineOffset(ptCaretPos.y) != -1) && (GetSubLineOffset(ptCaretPos.y) != CountMultiLines(GetViewLineForScreen(ptCaretPos.y))-1)) // not last screen line of view line
2842 ptCaretPos.x--;
2844 SetCaretAndGoalPosition(ptCaretPos);
2845 OnCaretMove(bShift);
2848 break;
2849 case VK_BACK:
2850 if (IsWritable())
2852 if (! HasTextSelection())
2854 POINT ptCaretPos = GetCaretPosition();
2855 if (ptCaretPos.y == 0 && ptCaretPos.x == 0)
2856 break;
2857 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2858 if (bControl)
2859 MoveCaretWordLeft();
2860 else
2862 while (MoveCaretLeft() && IsViewLineEmpty(GetCaretViewPosition().y))
2866 m_ptSelectionViewPosStart = GetCaretViewPosition();
2868 RemoveSelectedText();
2870 break;
2871 case VK_DELETE:
2872 if (IsWritable())
2874 if (! HasTextSelection())
2876 if (bControl)
2878 m_ptSelectionViewPosStart = GetCaretViewPosition();
2879 MoveCaretWordRight();
2880 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2882 else
2884 if (! MoveCaretRight())
2885 break;
2886 m_ptSelectionViewPosEnd = GetCaretViewPosition();
2887 MoveCaretLeft();
2888 m_ptSelectionViewPosStart = GetCaretViewPosition();
2891 RemoveSelectedText();
2893 break;
2895 CView::OnKeyDown(nChar, nRepCnt, nFlags);
2898 void CBaseView::OnLButtonDown(UINT nFlags, CPoint point)
2900 const int nClickedLine = GetButtonEventLineIndex(point);
2901 if ((nClickedLine >= m_nTopLine)&&(nClickedLine < GetLineCount()))
2903 POINT ptCaretPos;
2904 ptCaretPos.y = nClickedLine;
2905 LONG xpos = point.x - GetMarginWidth();
2906 LONG xpos2 = xpos / GetCharWidth();
2907 xpos2 += m_nOffsetChar;
2908 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
2909 xpos2++;
2910 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
2911 SetCaretAndGoalPosition(ptCaretPos);
2913 if (nFlags & MK_SHIFT)
2914 AdjustSelection(MOVERIGHT);
2915 else
2917 ClearSelection();
2918 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
2919 if (point.x < GetMarginWidth())
2921 // select the whole line
2922 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
2923 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
2927 UpdateViewsCaretPosition();
2928 Invalidate();
2931 CView::OnLButtonDown(nFlags, point);
2934 enum ECharGroup { // ordered by priority low-to-hi
2935 CHG_UNKNOWN,
2936 CHG_CONTROL, // x00-x08, x0a-x1f
2937 CHG_WHITESPACE, // space tab
2938 CHG_PUNCTUATION, // 0x21-2f, x3a-x40, x5b-x60, x7b-x7f .,:;!?(){}[]/\<> ...
2939 CHG_WORDLETTER, // alpha num _ (others)
2942 ECharGroup GetCharGroup(wchar_t zChar)
2944 if (zChar == ' ' || zChar == '\t' )
2946 return CHG_WHITESPACE;
2948 if (zChar < 0x20)
2950 return CHG_CONTROL;
2952 if ((zChar >= 0x21 && zChar <= 0x2f)
2953 || (zChar >= 0x3a && zChar <= 0x40)
2954 || (zChar >= 0x5b && zChar <= 0x5e)
2955 || (zChar == 0x60)
2956 || (zChar >= 0x7b && zChar <= 0x7f))
2958 return CHG_PUNCTUATION;
2960 return CHG_WORDLETTER;
2963 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
2965 if (m_pViewData == 0) {
2966 CView::OnLButtonDblClk(nFlags, point);
2967 return;
2970 const int nClickedLine = GetButtonEventLineIndex(point);
2971 if ( nClickedLine < 0)
2972 return;
2973 int nViewLine = GetViewLineForScreen(nClickedLine);
2974 if (point.x < GetMarginWidth()) // only if double clicked on the margin
2976 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
2978 if((m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)||
2979 (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO))
2981 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
2982 int screenLine = FindViewLineNumber(movedindex);
2983 int nTop = screenLine - GetScreenLines()/2;
2984 if (nTop < 0)
2985 nTop = 0;
2986 ScrollAllToLine(nTop);
2987 // find and select the whole moved block
2988 int startSel = movedindex;
2989 int endSel = movedindex;
2990 while ((startSel > 0) && ((m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_TO)))
2991 startSel--;
2992 startSel++;
2993 while ((endSel < GetLineCount()) && ((m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_TO)))
2994 endSel++;
2995 endSel--;
2996 m_pOtherView->SetupSelection(startSel, endSel);
2997 return CView::OnLButtonDblClk(nFlags, point);
3001 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3003 // a double click on a marker expands the hidden text
3004 int i = nViewLine;
3005 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3007 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3008 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3009 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3010 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3011 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3012 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3013 i++;
3015 BuildAllScreen2ViewVector();
3016 if (m_pwndLeft)
3017 m_pwndLeft->Invalidate();
3018 if (m_pwndRight)
3019 m_pwndRight->Invalidate();
3020 if (m_pwndBottom)
3021 m_pwndBottom->Invalidate();
3023 else
3025 POINT ptCaretPos;
3026 ptCaretPos.y = nClickedLine;
3027 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3028 SetCaretPosition(ptCaretPos);
3029 ClearSelection();
3031 POINT ptViewCarret = GetCaretViewPosition();
3032 nViewLine = ptViewCarret.y;
3033 if (nViewLine >= GetViewCount())
3034 return;
3035 CString sLine = GetViewLine(nViewLine);
3036 int nLineLength = sLine.GetLength();
3037 int nBasePos = ptViewCarret.x;
3038 // get target char group
3039 ECharGroup eLeft = CHG_UNKNOWN;
3040 if (nBasePos > 0)
3042 eLeft = GetCharGroup(sLine[nBasePos-1]);
3044 ECharGroup eRight = CHG_UNKNOWN;
3045 if (nBasePos < nLineLength)
3047 eRight = GetCharGroup(sLine[nBasePos]);
3049 ECharGroup eTarget = max(eRight, eLeft);
3050 // find left margin
3051 int nLeft = nBasePos;
3052 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3054 nLeft--;
3056 // get right margin
3057 int nRight = nBasePos;
3058 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3060 nRight++;
3062 // set selection
3063 m_ptSelectionViewPosStart.x = nLeft;
3064 m_ptSelectionViewPosStart.y = nViewLine;
3065 m_ptSelectionViewPosEnd.x = nRight;
3066 m_ptSelectionViewPosEnd.y = nViewLine;
3067 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3068 SetupAllViewSelection(nViewLine, nViewLine);
3069 // set caret
3070 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3071 UpdateViewsCaretPosition();
3072 UpdateGoalPos();
3074 // set mark word
3075 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3076 int nMarkWidth = max(nRight - nLeft, 0);
3077 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3078 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3080 m_sMarkedWord.Empty();
3083 if (m_pwndLeft)
3084 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3085 if (m_pwndRight)
3086 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3087 if (m_pwndBottom)
3088 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3090 Invalidate();
3091 if (m_pwndLocator)
3092 m_pwndLocator->Invalidate();
3095 CView::OnLButtonDblClk(nFlags, point);
3098 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3100 const int nClickedLine = GetButtonEventLineIndex(point);
3101 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3103 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3105 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3106 if (pidl)
3108 SHOpenFolderAndSelectItems(pidl,0,0,0);
3109 CoTaskMemFree((LPVOID)pidl);
3112 return;
3114 POINT ptCaretPos;
3115 ptCaretPos.y = nClickedLine;
3116 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3117 SetCaretAndGoalPosition(ptCaretPos);
3118 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3119 if (m_pwndLeft)
3120 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3121 if (m_pwndRight)
3122 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3123 if (m_pwndBottom)
3124 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3125 ClearSelection();
3126 m_ptSelectionViewPosStart.x = 0;
3127 m_ptSelectionViewPosStart.y = nClickedLine;
3128 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3129 m_ptSelectionViewPosEnd.y = nClickedLine;
3130 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3131 UpdateViewsCaretPosition();
3132 Invalidate();
3133 if (m_pwndLocator)
3134 m_pwndLocator->Invalidate();
3137 void CBaseView::OnEditCopy()
3139 CString sCopyData = GetSelectedText();
3141 if (!sCopyData.IsEmpty())
3143 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3147 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3149 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3151 --m_pMainFrame->m_nMoveMovesToIgnore;
3152 CView::OnMouseMove(nFlags, point);
3153 return;
3155 int nMouseLine = GetButtonEventLineIndex(point);
3156 if (nMouseLine < -1)
3157 nMouseLine = -1;
3158 m_mouseInMargin = point.x < GetMarginWidth();
3160 ShowDiffLines(nMouseLine);
3162 KillTimer(IDT_SCROLLTIMER);
3163 if (nFlags & MK_LBUTTON)
3165 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3166 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3167 if (saveMouseLine < 0)
3168 return;
3169 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3170 if (HasSelection() &&
3171 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3173 POINT ptCaretPos = {charIndex, nMouseLine};
3174 SetCaretAndGoalPosition(ptCaretPos);
3175 AdjustSelection(MOVERIGHT);
3176 Invalidate();
3177 UpdateWindow();
3179 if (nMouseLine < m_nTopLine)
3181 ScrollAllToLine(m_nTopLine-1, TRUE);
3182 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3184 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3186 ScrollAllToLine(m_nTopLine+1, TRUE);
3187 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3189 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3191 ScrollAllSide(-1);
3192 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3194 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3196 ScrollAllSide(1);
3197 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3199 SetCapture();
3203 CView::OnMouseMove(nFlags, point);
3206 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3208 ShowDiffLines(-1);
3209 ReleaseCapture();
3210 KillTimer(IDT_SCROLLTIMER);
3212 __super::OnLButtonUp(nFlags, point);
3215 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3217 if (nIDEvent == IDT_SCROLLTIMER)
3219 POINT point;
3220 GetCursorPos(&point);
3221 ScreenToClient(&point);
3222 int nMouseLine = GetButtonEventLineIndex(point);
3223 if (nMouseLine < -1)
3225 nMouseLine = -1;
3227 if (GetKeyState(VK_LBUTTON)&0x8000)
3229 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3230 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3231 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3232 if (nMouseLine < m_nTopLine)
3234 ScrollAllToLine(m_nTopLine-1, TRUE);
3235 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3237 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3239 ScrollAllToLine(m_nTopLine+1, TRUE);
3240 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3242 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3244 ScrollAllSide(-1);
3245 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3247 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3249 ScrollAllSide(1);
3250 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3256 CView::OnTimer(nIDEvent);
3259 void CBaseView::ShowDiffLines(int nLine)
3261 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3263 m_pwndLineDiffBar->ShowLines(nLine);
3264 nLine = -1;
3265 m_nMouseLine = nLine;
3266 return;
3269 if ((!m_pwndRight)||(!m_pwndLeft))
3270 return;
3271 if(m_pMainFrame->m_bOneWay)
3272 return;
3274 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3275 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3277 if (nLine < 0)
3278 return;
3280 if (nLine != m_nMouseLine)
3282 if (nLine >= GetLineCount())
3283 nLine = -1;
3284 m_nMouseLine = nLine;
3285 m_pwndLineDiffBar->ShowLines(nLine);
3287 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3290 const viewdata& CBaseView::GetEmptyLineData()
3292 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN, -1);
3293 return emptyLine;
3296 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3298 for (int i = 0; i < nCount; i++)
3300 InsertViewData(nFirstView, GetEmptyLineData());
3305 void CBaseView::UpdateCaret()
3307 POINT ptCaretPos = GetCaretPosition();
3308 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3309 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3310 SetCaretPosition(ptCaretPos);
3312 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3314 if (m_bFocused &&
3315 ptCaretPos.y >= m_nTopLine &&
3316 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3317 nCaretOffset >= m_nOffsetChar &&
3318 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3320 CreateSolidCaret(2, GetLineHeight());
3321 SetCaretPos(TextToClient(ptCaretPos));
3322 ShowCaret();
3324 else
3326 HideCaret();
3330 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3332 POINT ptViewPos;
3333 ptViewPos.x = pt.x;
3335 int nSubLine = GetSubLineOffset(pt.y);
3336 if (nSubLine > 0)
3338 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3340 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3344 ptViewPos.y = GetViewLineForScreen(pt.y);
3345 return ptViewPos;
3348 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3350 POINT ptPos;
3351 int nViewLineLenLeft = GetViewLineLength(pt.y);
3352 ptPos.x = min(nViewLineLenLeft, pt.x);
3353 ptPos.y = FindScreenLineForViewLine(pt.y);
3354 if (GetViewLineForScreen(ptPos.y) != pt.y )
3356 ptPos.x = 0;
3358 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3360 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3361 while (nSubLineLength < ptPos.x)
3363 ptPos.x -= nSubLineLength;
3364 nViewLineLenLeft -= nSubLineLength;
3365 ptPos.y++;
3366 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3368 // last pos of non last sub-line go to start of next screen line
3369 // Note: while this works correctly, it's not what a user might expect:
3370 // cursor-right when the caret is before the last char of a wrapped line
3371 // now moves the caret to the next line. But users expect the caret to
3372 // move to the right of the last char instead, and with another cursor-right
3373 // keystroke to move the caret to the next line.
3374 // Basically, this would require to handle two caret positions for the same
3375 // logical position in the line string (one on the last position of the first line,
3376 // one on the first position of the new line. For non-wrapped lines this works
3377 // because there's an 'invisible' newline char at the end of the first line.
3378 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3380 ptPos.x = 0;
3381 ptPos.y++;
3385 return ptPos;
3389 void CBaseView::EnsureCaretVisible()
3391 POINT ptCaretPos = GetCaretPosition();
3392 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3394 if (ptCaretPos.y < m_nTopLine)
3395 ScrollAllToLine(ptCaretPos.y);
3396 int screnLines = GetScreenLines();
3397 if (screnLines)
3399 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3400 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3401 if (nCaretOffset < m_nOffsetChar)
3402 ScrollAllToChar(nCaretOffset);
3403 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3404 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3408 int CBaseView::CalculateActualOffset(const POINT& point)
3410 int nLineIndex = point.y;
3411 int nCharIndex = point.x;
3412 ASSERT(nCharIndex >= 0);
3413 CString sLine = GetLineChars(nLineIndex);
3414 int nLineLength = sLine.GetLength();
3415 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3418 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3420 int nLength = GetLineLength(nLineIndex);
3421 int nSubLine = GetSubLineOffset(nLineIndex);
3422 if (nSubLine>=0)
3424 int nViewLine = GetViewLineForScreen(nLineIndex);
3425 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3427 int nMultilineCount = CountMultiLines(nViewLine);
3428 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3430 nLength--;
3434 CString Line = GetLineChars(nLineIndex);
3435 int nIndex = 0;
3436 int nOffset = 0;
3437 int nTabSize = GetTabSize();
3438 while (nOffset < nActualOffset && nIndex < nLength)
3440 if (Line.GetAt(nIndex) == _T('\t'))
3441 nOffset += (nTabSize - nOffset % nTabSize);
3442 else
3443 ++nOffset;
3444 ++nIndex;
3446 return nIndex;
3449 POINT CBaseView::TextToClient(const POINT& point)
3451 POINT pt;
3452 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3453 pt.y = nOffsetScreenLine * GetLineHeight();
3454 pt.x = CalculateActualOffset(point);
3456 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3457 CDC * pDC = GetDC();
3458 if (pDC)
3460 pDC->SelectObject(GetFont()); // is this right font ?
3461 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3462 CString sLine = GetLineChars(nScreenLine);
3463 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3464 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3465 ReleaseDC(pDC);
3466 } else {
3467 nLeft += pt.x * GetCharWidth();
3470 pt.x = nLeft;
3471 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3472 return pt;
3475 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3477 CView::OnChar(nChar, nRepCnt, nFlags);
3479 if (IsReadonly())
3480 return;
3482 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3483 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3485 return;
3488 if (!m_pViewData) // no data - nothing to do
3489 return;
3491 if (nChar == VK_F16)
3493 // generated by a ctrl+backspace - ignore.
3495 else if ((nChar > 31)||(nChar == VK_TAB))
3497 ResetUndoStep();
3498 RemoveSelectedText();
3499 POINT ptCaretViewPos = GetCaretViewPosition();
3500 int nViewLine = ptCaretViewPos.y;
3501 if ((nViewLine==0)&&(GetViewCount()==0))
3502 OnChar(VK_RETURN, 0, 0);
3503 viewdata lineData = GetViewData(nViewLine);
3504 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3505 if (IsStateEmpty(lineData.state))
3507 // if not last line set EOL
3508 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3510 if (!IsViewLineEmpty(nCheckViewLine))
3512 lineData.ending = lineendings;
3513 break;
3516 // make sure previous (non empty) line have EOL set
3517 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3519 if (!IsViewLineEmpty(nCheckViewLine))
3521 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3523 SetViewLineEnding(nCheckViewLine, lineendings);
3525 break;
3529 lineData.state = DIFFSTATE_EDITED;
3530 bool bNeedRenumber = false;
3531 if (lineData.linenumber == -1)
3533 lineData.linenumber = 0;
3534 bNeedRenumber = true;
3536 SetViewData(nViewLine, lineData);
3537 SaveUndoStep();
3538 BuildAllScreen2ViewVector(nViewLine);
3539 if (bNeedRenumber)
3541 UpdateViewLineNumbers();
3543 MoveCaretRight();
3544 UpdateGoalPos();
3546 else if (nChar == 10)
3548 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3549 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3550 EOL newEOL = EOL_CRLF;
3551 switch (eol)
3553 case EOL_CRLF:
3554 newEOL = EOL_CR;
3555 break;
3556 case EOL_CR:
3557 newEOL = EOL_LF;
3558 break;
3559 case EOL_LF:
3560 newEOL = EOL_CRLF;
3561 break;
3563 if (eol==EOL_NOENDING || eol==newEOL)
3564 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3565 // to add EOL on newly edited empty line hit enter
3566 // don't store into UNDO if no change happened
3567 // and don't mark file as modified
3568 return;
3569 AddUndoViewLine(nViewLine);
3570 m_pViewData->SetLineEnding(nViewLine, newEOL);
3571 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3572 UpdateGoalPos();
3574 else if (nChar == VK_RETURN)
3576 // insert a new, fresh and empty line below the cursor
3577 RemoveSelectedText();
3579 CUndo::GetInstance().BeginGrouping();
3581 POINT ptCaretViewPos = GetCaretViewPosition();
3582 int nViewLine = ptCaretViewPos.y;
3583 int nLeft = ptCaretViewPos.x;
3584 CString sLine = GetViewLineChars(nViewLine);
3585 CString sLineLeft = sLine.Left(nLeft);
3586 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3587 EOL eOriginalEnding = EOL_AUTOLINE;
3588 if (m_pViewData->GetCount() > nViewLine)
3589 eOriginalEnding = GetViewLineEnding(nViewLine);
3591 if (!sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3593 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3594 SetViewData(nViewLine, newFirstLine);
3597 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3598 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN, -1);
3599 InsertViewData(nInsertLine, newLastLine);
3600 SaveUndoStep();
3602 // adds new line everywhere except me
3603 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3605 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3607 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3609 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3611 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3613 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3615 SaveUndoStep();
3617 UpdateViewLineNumbers();
3618 SaveUndoStep();
3619 CUndo::GetInstance().EndGrouping();
3621 BuildAllScreen2ViewVector();
3622 // move the cursor to the new line
3623 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3624 SetCaretAndGoalViewPosition(ptCaretViewPos);
3626 else
3627 return; // Unknown control character -- ignore it.
3628 ClearSelection();
3629 EnsureCaretVisible();
3630 UpdateCaret();
3631 SetModified(true);
3632 Invalidate(FALSE);
3635 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3637 ResetUndoStep();
3638 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3639 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3640 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3641 SaveUndoStep();
3642 RecalcAllVertScrollBars();
3643 Invalidate(FALSE);
3646 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3648 if (m_pViewData == NULL)
3649 return;
3650 int viewLine = nViewLineIndex;
3651 EOL ending = m_pViewData->GetLineEnding(viewLine);
3652 if (ending == EOL_NOENDING)
3654 ending = lineendings;
3656 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN, -1);
3657 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3659 CString sPartLine = GetViewLineChars(nViewLineIndex);
3660 int nPosx = GetCaretPosition().x; // should be view pos ?
3661 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3662 sPartLine = sPartLine.Mid(nPosx);
3663 newLine.sLine = sPartLine;
3665 m_pViewData->InsertData(viewLine+1, newLine);
3666 BuildAllScreen2ViewVector();
3669 void CBaseView::RemoveSelectedText()
3671 if (m_pViewData == NULL)
3672 return;
3673 if (!HasTextSelection())
3674 return;
3676 // fix selection if starts or ends on empty line
3677 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3678 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3681 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3682 SetCaretViewPosition(m_ptSelectionViewPosStart);
3683 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3686 m_ptSelectionViewPosStart = GetCaretViewPosition();
3687 if (!HasTextSelection())
3689 ClearSelection();
3690 return;
3693 // We want to undo the insertion in a single step.
3694 ResetUndoStep();
3695 CUndo::GetInstance().BeginGrouping();
3697 // combine first and last line
3698 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3699 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3700 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3701 oFirstLine.ending = oLastLine.ending;
3702 oFirstLine.state = DIFFSTATE_EDITED;
3703 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3705 // clean up middle lines if any
3706 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3708 viewdata oEmptyLine = GetEmptyLineData();
3709 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3711 SetViewData(nViewLine, oEmptyLine);
3713 SaveUndoStep();
3715 if (CleanEmptyLines())
3717 BuildAllScreen2ViewVector(); // schedule full rebuild
3719 SaveUndoStep();
3720 UpdateViewLineNumbers();
3723 SaveUndoStep();
3724 CUndo::GetInstance().EndGrouping();
3726 SetModified();
3727 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3728 SetCaretViewPosition(m_ptSelectionViewPosStart);
3729 UpdateGoalPos();
3730 ClearSelection();
3731 UpdateCaret();
3732 EnsureCaretVisible();
3733 Invalidate(FALSE);
3736 void CBaseView::PasteText()
3738 if (!OpenClipboard())
3739 return;
3741 CString sClipboardText;
3742 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3743 if (hglb)
3745 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3746 sClipboardText = CString(lpstr);
3747 GlobalUnlock(hglb);
3749 hglb = GetClipboardData(CF_UNICODETEXT);
3750 if (hglb)
3752 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3753 sClipboardText = lpstr;
3754 GlobalUnlock(hglb);
3756 CloseClipboard();
3758 if (sClipboardText.IsEmpty())
3759 return;
3761 sClipboardText.Replace(_T("\r\n"), _T("\r"));
3762 sClipboardText.Replace('\n', '\r');
3764 ResetUndoStep();
3766 POINT ptCaretViewPos = GetCaretViewPosition();
3767 int nLeft = ptCaretViewPos.x;
3768 int nViewLine = ptCaretViewPos.y;
3770 if ((nViewLine==0)&&(GetViewCount()==0))
3771 OnChar(VK_RETURN, 0, 0);
3773 std::vector<CString> lines;
3774 int nStart = 0;
3775 int nEolPos = 0;
3776 while ((nEolPos = sClipboardText.Find('\r', nEolPos))>=0)
3778 CString sLine = sClipboardText.Mid(nStart, nEolPos-nStart);
3779 lines.push_back(sLine);
3780 nEolPos++;
3781 nStart = nEolPos;
3783 CString sLine = sClipboardText.Mid(nStart);
3784 lines.push_back(sLine);
3786 int nLinesToPaste = (int)lines.size();
3787 if (nLinesToPaste > 1)
3789 // multiline text
3791 // We want to undo the multiline insertion in a single step.
3792 CUndo::GetInstance().BeginGrouping();
3794 sLine = GetViewLineChars(nViewLine);
3795 CString sLineLeft = sLine.Left(nLeft);
3796 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3797 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
3798 viewdata newLine(_T(""), DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3799 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3801 newLine.sLine = sLineLeft + lines[0];
3802 SetViewData(nViewLine, newLine);
3805 int nInsertLine = nViewLine;
3806 for (int i = 1; i < nLinesToPaste-1; i++)
3808 newLine.sLine = lines[i];
3809 InsertViewData(++nInsertLine, newLine);
3811 newLine.sLine = lines[nLinesToPaste-1] + sLineRight;
3812 newLine.ending = eOriginalEnding;
3813 InsertViewData(++nInsertLine, newLine);
3815 SaveUndoStep();
3817 // adds new lines everywhere except me
3818 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3820 m_pwndLeft->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3822 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3824 m_pwndRight->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3826 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3828 m_pwndBottom->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3830 SaveUndoStep();
3832 UpdateViewLineNumbers();
3833 CUndo::GetInstance().EndGrouping();
3835 ptCaretViewPos = SetupPoint(lines[nLinesToPaste-1].GetLength(), nInsertLine);
3837 else
3839 // single line text - just insert it
3840 sLine = GetViewLineChars(nViewLine);
3841 sLine.Insert(nLeft, sClipboardText);
3842 ptCaretViewPos = SetupPoint(nLeft + sClipboardText.GetLength(), nViewLine);
3843 SetViewLine(nViewLine, sLine);
3844 SetViewState(nViewLine, DIFFSTATE_EDITED);
3845 SaveUndoStep();
3848 SetModified();
3849 RefreshViews();
3850 BuildAllScreen2ViewVector();
3851 UpdateCaretViewPosition(ptCaretViewPos);
3854 void CBaseView::OnCaretDown()
3856 POINT ptCaretPos = GetCaretPosition();
3857 int nLine = ptCaretPos.y;
3858 int nNextLine = nLine + 1;
3859 if (nNextLine >= GetLineCount()) // already at last line
3861 return;
3864 POINT ptCaretViewPos = GetCaretViewPosition();
3865 int nViewLine = ptCaretViewPos.y;
3866 int nNextViewLine = GetViewLineForScreen(nNextLine);
3867 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
3869 // find next suitable screen line
3870 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
3872 nNextLine++;
3873 if (nNextLine >= GetLineCount())
3875 return;
3877 nNextViewLine = GetViewLineForScreen(nNextLine);
3880 ptCaretPos.y = nNextLine;
3881 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3882 SetCaretPosition(ptCaretPos);
3883 OnCaretMove(MOVELEFT);
3884 ShowDiffLines(ptCaretPos.y);
3887 bool CBaseView::MoveCaretLeft()
3889 POINT ptCaretViewPos = GetCaretViewPosition();
3891 //int nViewLine = ptCaretViewPos.y;
3892 if (ptCaretViewPos.x == 0)
3894 int nPrevLine = GetCaretPosition().y;
3895 int nPrevViewLine;
3896 do {
3897 nPrevLine--;
3898 if (nPrevLine < 0)
3900 return false;
3902 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3903 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
3904 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
3905 ShowDiffLines(nPrevLine);
3907 else
3908 --ptCaretViewPos.x;
3910 SetCaretAndGoalViewPosition(ptCaretViewPos);
3911 return true;
3914 bool CBaseView::MoveCaretRight()
3916 POINT ptCaretViewPos = GetCaretViewPosition();
3918 int nViewLine = ptCaretViewPos.y;
3919 int nViewLineLen = GetViewLineLength(nViewLine);
3920 if (ptCaretViewPos.x >= nViewLineLen)
3922 int nNextLine = GetCaretPosition().y;
3923 int nNextViewLine;
3924 do {
3925 nNextLine++;
3926 if (nNextLine >= GetLineCount())
3928 return false;
3930 nNextViewLine = GetViewLineForScreen(nNextLine);
3931 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
3932 ptCaretViewPos.y = nNextViewLine;
3933 ptCaretViewPos.x = 0;
3934 ShowDiffLines(nNextLine);
3936 else
3937 ++ptCaretViewPos.x;
3939 SetCaretAndGoalViewPosition(ptCaretViewPos);
3940 return true;
3943 void CBaseView::UpdateGoalPos()
3945 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
3948 void CBaseView::OnCaretLeft()
3950 MoveCaretLeft();
3951 OnCaretMove(MOVELEFT);
3954 void CBaseView::OnCaretRight()
3956 MoveCaretRight();
3957 OnCaretMove(MOVERIGHT);
3960 void CBaseView::OnCaretUp()
3962 POINT ptCaretPos = GetCaretPosition();
3963 int nLine = ptCaretPos.y;
3964 if (nLine <= 0) // already at first line
3966 return;
3968 int nPrevLine = nLine - 1;
3970 POINT ptCaretViewPos = GetCaretViewPosition();
3971 int nViewLine = ptCaretViewPos.y;
3972 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
3973 if (nPrevViewLine != nViewLine) // not on same view line
3975 // find previous suitable screen line
3976 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
3978 if (nPrevLine <= 0)
3980 return;
3982 nPrevLine--;
3983 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3986 ptCaretPos.y = nPrevLine;
3987 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3988 SetCaretPosition(ptCaretPos);
3989 OnCaretMove(MOVELEFT);
3990 ShowDiffLines(ptCaretPos.y);
3993 bool CBaseView::IsWordSeparator(const wchar_t ch) const
3995 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
3998 bool CBaseView::IsCaretAtWordBoundary()
4000 POINT ptViewCaret = GetCaretViewPosition();
4001 CString line = GetViewLineChars(ptViewCaret.y);
4002 if (line.IsEmpty())
4003 return false; // no boundary at the empty lines
4004 if (ptViewCaret.x == 0)
4005 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4006 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4007 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4008 return
4009 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4010 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4013 void CBaseView::UpdateViewsCaretPosition()
4015 POINT ptCaretPos = GetCaretPosition();
4016 if (m_pwndBottom && m_pwndBottom!=this)
4017 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4018 if (m_pwndLeft && m_pwndLeft!=this)
4019 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4020 if (m_pwndRight && m_pwndRight!=this)
4021 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4024 void CBaseView::OnCaretWordleft()
4026 MoveCaretWordLeft();
4027 OnCaretMove(MOVELEFT);
4030 void CBaseView::OnCaretWordright()
4032 MoveCaretWordRight();
4033 OnCaretMove(MOVERIGHT);
4036 void CBaseView::MoveCaretWordLeft()
4038 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4043 void CBaseView::MoveCaretWordRight()
4045 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4050 void CBaseView::ClearCurrentSelection()
4052 m_ptSelectionViewPosStart = GetCaretViewPosition();
4053 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4054 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4055 m_nSelViewBlockStart = -1;
4056 m_nSelViewBlockEnd = -1;
4057 Invalidate(FALSE);
4060 void CBaseView::ClearSelection()
4062 if (m_pwndLeft)
4063 m_pwndLeft->ClearCurrentSelection();
4064 if (m_pwndRight)
4065 m_pwndRight->ClearCurrentSelection();
4066 if (m_pwndBottom)
4067 m_pwndBottom->ClearCurrentSelection();
4070 void CBaseView::AdjustSelection(bool bMoveLeft)
4072 POINT ptCaretViewPos = GetCaretViewPosition();
4073 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4075 // select all have been used recently update origin
4076 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4078 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4079 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4081 m_ptSelectionViewPosStart = ptCaretViewPos;
4082 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4084 else
4086 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4087 m_ptSelectionViewPosEnd = ptCaretViewPos;
4090 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4092 Invalidate(FALSE);
4095 void CBaseView::OnEditCut()
4097 if (IsWritable())
4099 OnEditCopy();
4100 RemoveSelectedText();
4104 void CBaseView::OnEditPaste()
4106 if (IsWritable())
4108 CUndo::GetInstance().BeginGrouping();
4109 RemoveSelectedText();
4110 PasteText();
4111 CUndo::GetInstance().EndGrouping();
4115 void CBaseView::DeleteFonts()
4117 for (int i=0; i<fontsCount; i++)
4119 if (m_apFonts[i] != NULL)
4121 m_apFonts[i]->DeleteObject();
4122 delete m_apFonts[i];
4123 m_apFonts[i] = NULL;
4128 void CBaseView::OnCaretMove(bool bMoveLeft)
4130 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4131 OnCaretMove(bMoveLeft, bShift);
4134 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4136 if(isShiftPressed)
4137 AdjustSelection(bMoveLeft);
4138 else
4139 ClearSelection();
4140 EnsureCaretVisible();
4141 UpdateCaret();
4144 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4146 AddCutCopyAndPaste(popup);
4149 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4151 popup.AppendMenu(MF_SEPARATOR, NULL);
4152 CString temp;
4153 temp.LoadString(IDS_EDIT_COPY);
4154 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4155 if (IsWritable())
4157 temp.LoadString(IDS_EDIT_CUT);
4158 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4159 temp.LoadString(IDS_EDIT_PASTE);
4160 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4164 void CBaseView::CompensateForKeyboard(CPoint& point)
4166 // if the context menu is invoked through the keyboard, we have to use
4167 // a calculated position on where to anchor the menu on
4168 if (ArePointsSame(point, SetupPoint(-1, -1)))
4170 CRect rect;
4171 GetWindowRect(&rect);
4172 point = rect.CenterPoint();
4176 HICON CBaseView::LoadIcon(WORD iconId)
4178 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4179 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4180 return (HICON)icon;
4183 void CBaseView::ReleaseBitmap()
4185 if (m_pCacheBitmap != NULL)
4187 m_pCacheBitmap->DeleteObject();
4188 delete m_pCacheBitmap;
4189 m_pCacheBitmap = NULL;
4193 void CBaseView::BuildMarkedWordArray()
4195 int lineCount = GetLineCount();
4196 m_arMarkedWordLines.clear();
4197 m_arMarkedWordLines.reserve(lineCount);
4198 bool bDoit = !m_sMarkedWord.IsEmpty();
4199 for (int i = 0; i < lineCount; ++i)
4201 if (bDoit)
4203 CString line = GetLineChars(i);
4205 if (!line.IsEmpty())
4207 m_arMarkedWordLines.push_back(line.Find(m_sMarkedWord) != -1);
4209 else
4210 m_arMarkedWordLines.push_back(0);
4212 else
4213 m_arMarkedWordLines.push_back(0);
4217 void CBaseView::BuildFindStringArray()
4219 int lineCount = GetLineCount();
4220 m_arFindStringLines.clear();
4221 m_arFindStringLines.reserve(lineCount);
4222 bool bDoit = !m_sFindText.IsEmpty();
4223 int s = 0;
4224 int e = 0;
4225 for (int i = 0; i < lineCount; ++i)
4227 if (bDoit)
4229 CString line = GetLineChars(i);
4231 if (!line.IsEmpty())
4233 line = line.MakeLower();
4234 m_arFindStringLines.push_back(StringFound(line, SearchNext, s, e));
4236 else
4237 m_arFindStringLines.push_back(0);
4239 else
4240 m_arFindStringLines.push_back(0);
4242 UpdateLocator();
4245 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4247 if (!m_bShowInlineDiff)
4248 return false;
4249 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4250 return false;
4252 CString sLine = GetViewLineChars(nViewLine);
4253 if (sLine.IsEmpty())
4254 return false;
4256 CheckOtherView();
4257 if (!m_pOtherViewData)
4258 return false;
4260 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4261 if (sDiffLine.IsEmpty())
4262 return false;
4264 CString sLineExp = ExpandChars(sLine);
4265 CString sDiffLineExp = ExpandChars(sDiffLine);
4266 svn_diff_t * diff = NULL;
4267 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4268 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4269 return false;
4271 size_t lineoffset = 0;
4272 size_t position = 0;
4273 while (diff)
4275 apr_off_t len = diff->original_length;
4276 size_t oldpos = position;
4278 for (apr_off_t i = 0; i < len; ++i)
4280 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4281 lineoffset++;
4284 if (diff->type == svn_diff__type_diff_modified)
4286 inlineDiffPos p;
4287 p.start = oldpos;
4288 p.end = position;
4289 positions.push_back(p);
4292 diff = diff->next;
4295 return !positions.empty();
4298 void CBaseView::OnNavigateNextinlinediff()
4300 int nX;
4301 if (GetNextInlineDiff(nX))
4303 POINT ptCaretViewPos = GetCaretViewPosition();
4304 ptCaretViewPos.x = nX;
4305 SetCaretAndGoalViewPosition(ptCaretViewPos);
4306 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4307 EnsureCaretVisible();
4311 void CBaseView::OnNavigatePrevinlinediff()
4313 int nX;
4314 if (GetPrevInlineDiff(nX))
4316 POINT ptCaretViewPos = GetCaretViewPosition();
4317 ptCaretViewPos.x = nX;
4318 SetCaretAndGoalViewPosition(ptCaretViewPos);
4319 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4320 EnsureCaretVisible();
4324 bool CBaseView::HasNextInlineDiff()
4326 int nPos;
4327 return GetNextInlineDiff(nPos);
4330 bool CBaseView::GetNextInlineDiff(int & nPos)
4332 POINT ptCaretViewPos = GetCaretViewPosition();
4333 std::vector<inlineDiffPos> positions;
4334 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4336 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4338 if (it->start > ptCaretViewPos.x)
4340 nPos = (LONG)it->start;
4341 return true;
4343 if (it->end > ptCaretViewPos.x)
4345 nPos = (LONG)it->end;
4346 return true;
4350 return false;
4353 bool CBaseView::HasPrevInlineDiff()
4355 int nPos;
4356 return GetPrevInlineDiff(nPos);
4359 bool CBaseView::GetPrevInlineDiff(int & nPos)
4361 POINT ptCaretViewPos = GetCaretViewPosition();
4362 std::vector<inlineDiffPos> positions;
4363 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4365 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4367 if ( it->end < ptCaretViewPos.x)
4369 nPos = (LONG)it->end;
4370 return true;
4372 if ( it->start < ptCaretViewPos.x)
4374 nPos = (LONG)it->start;
4375 return true;
4379 return false;
4382 CBaseView * CBaseView::GetFirstGoodView()
4384 if (IsViewGood(m_pwndLeft))
4385 return m_pwndLeft;
4386 if (IsViewGood(m_pwndRight))
4387 return m_pwndRight;
4388 if (IsViewGood(m_pwndBottom))
4389 return m_pwndBottom;
4390 return NULL;
4393 void CBaseView::BuildAllScreen2ViewVector()
4395 CBaseView * p_pwndView = GetFirstGoodView();
4396 if (p_pwndView)
4398 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4402 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4404 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4407 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4409 CBaseView * p_pwndView = GetFirstGoodView();
4410 if (p_pwndView)
4412 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4416 void CBaseView::UpdateViewLineNumbers()
4418 int nLineNumber = 0;
4419 int nViewLineCount = GetViewCount();
4420 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4422 int oldLine = (int)GetViewLineNumber(nViewLine);
4423 if (oldLine >= 0)
4424 SetViewLineNumber(nViewLine, nLineNumber++);
4426 m_nDigits = 0;
4429 int CBaseView::CleanEmptyLines()
4431 int nRemovedCount = 0;
4432 int nViewLineCount = GetViewCount();
4433 bool bCheckLeft = IsViewGood(m_pwndLeft);
4434 bool bCheckRight = IsViewGood(m_pwndRight);
4435 bool bCheckBottom = IsViewGood(m_pwndBottom);
4436 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4438 bool bAllEmpty = true;
4439 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4440 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4441 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4442 if (bAllEmpty)
4444 if (bCheckLeft)
4446 m_pwndLeft->RemoveViewData(nViewLine);
4448 if (bCheckRight)
4450 m_pwndRight->RemoveViewData(nViewLine);
4452 if (bCheckBottom)
4454 m_pwndBottom->RemoveViewData(nViewLine);
4456 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4458 SaveUndoStep();
4460 nViewLineCount--;
4461 nRemovedCount++;
4462 continue;
4464 nViewLine++;
4466 if (nRemovedCount != 0)
4468 if (bCheckLeft)
4470 m_pwndLeft->SetModified();
4472 if (bCheckRight)
4474 m_pwndRight->SetModified();
4476 if (bCheckBottom)
4478 m_pwndBottom->SetModified();
4481 return nRemovedCount;
4484 int CBaseView::FindScreenLineForViewLine( int viewLine )
4486 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4489 int CBaseView::CountMultiLines( int nViewLine )
4491 if (m_ScreenedViewLine.empty())
4492 return 0; // in case the view is completely empty
4494 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4496 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4498 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4501 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4503 TScreenedViewLine oScreenedLine;
4504 // tokenize string
4505 int prevpos = 0;
4506 int pos = 0;
4507 while ((pos = multiline.Find('\n', pos)) >= 0)
4509 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4510 pos++;
4511 prevpos = pos;
4513 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4514 oScreenedLine.bSublinesSet = true;
4515 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4517 return CountMultiLines(nViewLine);
4520 /// prepare inline diff cache
4521 LineColors & CBaseView::GetLineColors(int nViewLine)
4523 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4525 if (m_bWhitespaceInlineDiffs)
4527 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4528 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4530 else
4532 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4533 return m_ScreenedViewLine[nViewLine].lineColors;
4536 LineColors oLineColors;
4537 // set main line color
4538 COLORREF crBkgnd, crText;
4539 DiffStates diffState = m_pViewData->GetState(nViewLine);
4540 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4541 oLineColors.SetColor(0, crText, crBkgnd);
4543 do {
4544 if (!m_bShowInlineDiff)
4545 break;
4547 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4548 break;
4550 CString sLine = GetViewLineChars(nViewLine);
4551 if (sLine.IsEmpty())
4552 break;
4553 if (!m_pOtherView)
4554 break;
4556 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4557 if (sDiffLine.IsEmpty())
4558 break;
4560 svn_diff_t * diff = NULL;
4561 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4562 break;
4563 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4564 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4565 break;
4567 int lineoffset = 0;
4568 int nTextStartOffset = 0;
4569 std::map<int, COLORREF> removedPositions;
4570 while (diff)
4572 apr_off_t len = diff->original_length;
4574 CString s;
4575 for (int i = 0; i < len; ++i)
4577 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4578 lineoffset++;
4580 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4581 int nTextLength = s.GetLength();
4583 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4584 if ((m_bShowInlineDiff)&&(bInlineDiff))
4586 crBkgnd = InlineViewLineDiffColor(nViewLine);
4588 else
4590 crBkgnd = m_ModifiedBk;
4593 if (len < diff->modified_length)
4595 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4597 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4599 nTextStartOffset += nTextLength;
4600 diff = diff->next;
4602 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4604 oLineColors.AddShotColor(it->first, it->second);
4606 } while (false); // error catch
4608 if (!m_bWhitespaceInlineDiffs)
4610 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4611 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4613 else
4615 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4616 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4619 return GetLineColors(nViewLine);
4622 void CBaseView::OnEditSelectall()
4624 if (m_pViewData == nullptr)
4625 return;
4626 int nLastViewLine = m_pViewData->GetCount()-1;
4627 if (nLastViewLine < 0)
4628 return;
4629 SetupAllViewSelection(0, nLastViewLine);
4631 CString sLine = GetViewLineChars(nLastViewLine);
4632 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4633 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4634 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4636 UpdateWindow();
4639 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4641 FilterWhitespaces(first);
4642 FilterWhitespaces(second);
4645 void CBaseView::FilterWhitespaces(CString& line)
4647 line.Remove(' ');
4648 line.Remove('\t');
4649 line.Remove('\r');
4650 line.Remove('\n');
4653 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4655 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4656 int nEventLine = nLineFromTop + m_nTopLine;
4657 nEventLine--; //we need the index
4658 return nEventLine;
4662 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4664 if (RelayTrippleClick(pMsg))
4665 return TRUE;
4666 return CView::PreTranslateMessage(pMsg);
4670 void CBaseView::ResetUndoStep()
4672 m_AllState.Clear();
4675 void CBaseView::SaveUndoStep()
4677 if (!m_AllState.IsEmpty())
4679 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4681 ResetUndoStep();
4684 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4686 m_pState->addedlines.push_back(index);
4687 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4690 void CBaseView::InsertViewData( int index, const viewdata& data )
4692 m_pState->addedlines.push_back(index);
4693 m_pViewData->InsertData(index, data);
4696 void CBaseView::RemoveViewData( int index )
4698 m_pState->removedlines[index] = m_pViewData->GetData(index);
4699 m_pViewData->RemoveData(index);
4702 void CBaseView::SetViewData( int index, const viewdata& data )
4704 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4705 m_pViewData->SetData(index, data);
4708 void CBaseView::SetViewState( int index, DiffStates state )
4710 m_pState->linestates[index] = m_pViewData->GetState(index);
4711 m_pViewData->SetState(index, state);
4714 void CBaseView::SetViewLine( int index, const CString& sLine )
4716 m_pState->difflines[index] = m_pViewData->GetLine(index);
4717 m_pViewData->SetLine(index, sLine);
4720 void CBaseView::SetViewLineNumber( int index, int linenumber )
4722 int oldLineNumber = m_pViewData->GetLineNumber(index);
4723 if (oldLineNumber != linenumber) {
4724 m_pState->linelines[index] = oldLineNumber;
4725 m_pViewData->SetLineNumber(index, linenumber);
4729 void CBaseView::SetViewLineEnding( int index, EOL ending )
4731 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4732 m_pViewData->SetLineEnding(index, ending);
4736 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4738 if (HasSelection())
4740 start = m_nSelViewBlockStart;
4741 end = m_nSelViewBlockEnd;
4742 return true;
4744 return false;
4747 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4749 RebuildIfNecessary();
4750 if (size() <= screenLine)
4751 return 0;
4752 return m_Screen2View[screenLine].nViewLine;
4755 int CBaseView::Screen2View::size()
4757 RebuildIfNecessary();
4758 return (int)m_Screen2View.size();
4761 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4763 RebuildIfNecessary();
4764 if (size() <= screenLine)
4765 return 0;
4766 return m_Screen2View[screenLine].nViewSubLine;
4769 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4771 RebuildIfNecessary();
4772 return m_Screen2View[screenLine];
4776 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
4778 void CBaseView::Screen2View::RebuildIfNecessary()
4780 if (!m_pViewData)
4781 return; // rebuild not necessary
4783 FixScreenedCacheSize(m_pwndLeft);
4784 FixScreenedCacheSize(m_pwndRight);
4785 FixScreenedCacheSize(m_pwndBottom);
4786 if (!m_bFull)
4788 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
4790 ResetScreenedViewLineCache(m_pwndLeft, *it);
4791 ResetScreenedViewLineCache(m_pwndRight, *it);
4792 ResetScreenedViewLineCache(m_pwndBottom, *it);
4795 else
4797 ResetScreenedViewLineCache(m_pwndLeft);
4798 ResetScreenedViewLineCache(m_pwndRight);
4799 ResetScreenedViewLineCache(m_pwndBottom);
4801 m_RebuildRanges.clear();
4802 m_bFull = false;
4804 size_t OldSize = m_Screen2View.size();
4805 m_Screen2View.clear();
4806 m_Screen2View.reserve(OldSize); // guess same size
4807 for (int i = 0; i < m_pViewData->GetCount(); ++i)
4809 if (m_pMainFrame->m_bCollapsed)
4811 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
4812 ++i;
4813 if (!(i < m_pViewData->GetCount()))
4814 break;
4816 TScreenLineInfo oLineInfo;
4817 oLineInfo.nViewLine = i;
4818 oLineInfo.nViewSubLine = -1; // no wrap
4819 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
4821 int nMaxLines = 0;
4822 if (IsLeftViewGood())
4823 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
4824 if (IsRightViewGood())
4825 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
4826 if (IsBottomViewGood())
4827 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
4828 for (int l = 0; l < (nMaxLines-1); ++l)
4830 oLineInfo.nViewSubLine++;
4831 m_Screen2View.push_back(oLineInfo);
4833 oLineInfo.nViewSubLine++;
4835 m_Screen2View.push_back(oLineInfo);
4837 m_pViewData = NULL;
4839 if (IsLeftViewGood())
4840 m_pwndLeft->BuildMarkedWordArray();
4841 if (IsRightViewGood())
4842 m_pwndRight->BuildMarkedWordArray();
4843 if (IsBottomViewGood())
4844 m_pwndBottom->BuildMarkedWordArray();
4845 UpdateLocator();
4846 RecalcAllVertScrollBars();
4847 RecalcAllHorzScrollBars();
4850 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
4852 RebuildIfNecessary();
4854 int nScreenLineCount = (int)m_Screen2View.size();
4856 int nPos = 0;
4857 if (nScreenLineCount>16)
4859 // for enough long data search for last screen
4860 // with viewline less than one we are looking for
4861 // use approximate method (based on) binary search using asymmetric start point
4862 // in form 2**n (determined as MSB of length) to go around division and rounding;
4863 // this effectively looks for bit values from MSB to LSB
4865 int nTestBit;
4866 //GetMostSignificantBitValue
4867 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
4868 nTestBit = nScreenLineCount;
4869 nTestBit |= nTestBit>>1;
4870 nTestBit |= nTestBit>>2;
4871 nTestBit |= nTestBit>>4;
4872 nTestBit |= nTestBit>>8;
4873 nTestBit |= nTestBit>>16;
4874 nTestBit ^= (nTestBit>>1);
4876 while (nTestBit)
4878 int nTestPos = nPos | nTestBit;
4879 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
4881 nPos = nTestPos;
4883 nTestBit >>= 1;
4886 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
4888 nPos++;
4891 return nPos;
4894 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
4895 m_bFull = true;
4897 m_pViewData = pViewData;
4900 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
4902 if (m_bFull)
4903 return;
4905 m_pViewData = pViewData;
4907 TRebuildRange Range;
4908 Range.FirstViewLine=nFirstViewLine;
4909 Range.LastViewLine=nLastViewLine;
4910 m_RebuildRanges.push_back(Range);
4913 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
4915 if (!IsViewGood(pwndView))
4917 return false;
4919 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
4920 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
4921 if (nOldSize == nViewCount)
4923 return false;
4925 pwndView->m_ScreenedViewLine.resize(nViewCount);
4926 return true;
4929 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView)
4931 if (!IsViewGood(pwndView))
4933 return false;
4935 TRebuildRange Range={0, pwndView->GetViewCount()-1};
4936 ResetScreenedViewLineCache(pwndView, Range);
4937 return true;
4940 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range)
4942 if (!IsViewGood(pwndView))
4944 return false;
4946 if (Range.LastViewLine == -1)
4948 return false;
4950 ASSERT(Range.FirstViewLine >= 0);
4951 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
4952 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
4954 pwndView->m_ScreenedViewLine[i].Clear();
4956 return false;
4959 void CBaseView::WrapChanged()
4961 m_nMaxLineLength = -1;
4962 m_nOffsetChar = 0;
4965 void CBaseView::OnEditFind()
4967 if (m_pFindDialog)
4968 return;
4970 m_pFindDialog = new CFindDlg(this);
4971 m_pFindDialog->Create(this);
4973 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
4976 LRESULT CBaseView::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
4978 ASSERT(m_pFindDialog != NULL);
4980 if (m_pFindDialog->IsTerminating())
4982 // invalidate the handle identifying the dialog box.
4983 m_pFindDialog = NULL;
4984 return 0;
4987 if(m_pFindDialog->FindNext())
4989 //read data from dialog
4990 m_sFindText = m_pFindDialog->GetFindString();
4991 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
4992 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
4993 m_bWholeWord = m_pFindDialog->WholeWord();
4995 if (!m_bMatchCase)
4996 m_sFindText = m_sFindText.MakeLower();
4998 BuildFindStringArray();
4999 OnEditFindnext();
5002 return 0;
5005 void CBaseView::OnEditFindnextStart()
5007 if (m_pViewData == nullptr)
5008 return;
5009 if (HasTextSelection())
5011 m_sFindText = GetSelectedText();
5012 m_bMatchCase = false;
5013 m_bLimitToDiff = false;
5014 m_bWholeWord = false;
5015 m_sFindText = m_sFindText.MakeLower();
5017 BuildFindStringArray();
5018 OnEditFindnext();
5020 else
5022 m_sFindText.Empty();
5023 BuildFindStringArray();
5027 void CBaseView::OnEditFindprevStart()
5029 if (m_pViewData == nullptr)
5030 return;
5031 if (HasTextSelection())
5033 m_sFindText = GetSelectedText();
5034 m_bMatchCase = false;
5035 m_bLimitToDiff = false;
5036 m_bWholeWord = false;
5037 m_sFindText = m_sFindText.MakeLower();
5039 BuildFindStringArray();
5040 OnEditFindprev();
5042 else
5044 m_sFindText.Empty();
5045 BuildFindStringArray();
5049 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5051 start = str.Find(m_sFindText);
5052 if ((srchDir==SearchPrevious)&&(start>=0))
5054 int laststart = start;
5057 start = laststart;
5058 laststart = str.Find(m_sFindText, laststart+1);
5059 } while (laststart >= 0);
5061 end = start + m_sFindText.GetLength();
5062 bool bStringFound = (start >= 0);
5063 if (bStringFound && m_bWholeWord)
5065 if (start)
5066 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5068 if (bStringFound)
5070 if (str.GetLength() > end)
5071 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5074 return bStringFound;
5077 void CBaseView::OnEditFindprev()
5079 Search(SearchPrevious);
5082 void CBaseView::OnEditFindnext()
5084 Search(SearchNext);
5087 void CBaseView::Search(SearchDirection srchDir)
5089 if (m_sFindText.IsEmpty())
5090 return;
5091 if(!m_pViewData)
5092 return;
5094 POINT start = m_ptSelectionViewPosEnd;
5095 POINT end;
5096 end.y = m_pViewData->GetCount()-1;
5097 if (end.y < 0)
5098 return;
5100 if (srchDir==SearchNext)
5101 end.x = GetViewLineLength(end.y);
5102 else
5104 end.x = m_ptSelectionViewPosStart.x;
5105 start.x = 0;
5108 if (!HasTextSelection())
5110 start.y = m_ptCaretViewPos.y;
5111 if (srchDir==SearchNext)
5112 start.x = m_ptCaretViewPos.x;
5113 else
5115 start.x = 0;
5116 end.x = m_ptCaretViewPos.x;
5119 CString sSelectedText;
5120 int startline = -1;
5121 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5123 if (nViewLine < 0)
5125 nViewLine = m_pViewData->GetCount()-1;
5126 startline = start.y;
5128 if (nViewLine > end.y)
5130 nViewLine = 0;
5131 startline = start.y;
5133 if (startline >= 0)
5135 if (nViewLine == startline)
5136 break;
5138 switch (m_pViewData->GetState(nViewLine))
5140 case DIFFSTATE_EMPTY:
5141 break;
5142 case DIFFSTATE_UNKNOWN:
5143 case DIFFSTATE_NORMAL:
5144 if (m_bLimitToDiff)
5145 break;
5146 case DIFFSTATE_REMOVED:
5147 case DIFFSTATE_REMOVEDWHITESPACE:
5148 case DIFFSTATE_ADDED:
5149 case DIFFSTATE_ADDEDWHITESPACE:
5150 case DIFFSTATE_WHITESPACE:
5151 case DIFFSTATE_WHITESPACE_DIFF:
5152 case DIFFSTATE_CONFLICTED:
5153 case DIFFSTATE_CONFLICTED_IGNORED:
5154 case DIFFSTATE_CONFLICTADDED:
5155 case DIFFSTATE_CONFLICTEMPTY:
5156 case DIFFSTATE_CONFLICTRESOLVED:
5157 case DIFFSTATE_IDENTICALREMOVED:
5158 case DIFFSTATE_IDENTICALADDED:
5159 case DIFFSTATE_THEIRSREMOVED:
5160 case DIFFSTATE_THEIRSADDED:
5161 case DIFFSTATE_MOVED_FROM:
5162 case DIFFSTATE_MOVED_TO:
5163 case DIFFSTATE_YOURSREMOVED:
5164 case DIFFSTATE_YOURSADDED:
5165 case DIFFSTATE_EDITED:
5167 sSelectedText = GetViewLineChars(nViewLine);
5168 if (nViewLine==start.y)
5169 sSelectedText = srchDir==SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(start.x);
5170 if (!m_bMatchCase)
5171 sSelectedText = sSelectedText.MakeLower();
5172 int startfound = -1;
5173 int endfound = -1;
5174 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5176 HighlightViewLines(nViewLine, nViewLine);
5177 m_ptSelectionViewPosStart.x = startfound;
5178 m_ptSelectionViewPosEnd.x = endfound;
5179 if (nViewLine==start.y)
5181 m_ptSelectionViewPosStart.x += start.x;
5182 m_ptSelectionViewPosEnd.x += start.x;
5184 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5185 m_ptSelectionViewPosStart.y = nViewLine;
5186 m_ptSelectionViewPosEnd.y = nViewLine;
5187 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5188 UpdateViewsCaretPosition();
5189 EnsureCaretVisible();
5190 Invalidate();
5191 return;
5194 break;
5197 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5200 CString CBaseView::GetSelectedText() const
5202 CString sSelectedText;
5203 POINT start = m_ptSelectionViewPosStart;
5204 POINT end = m_ptSelectionViewPosEnd;
5205 if (!HasTextSelection())
5207 if (!HasSelection())
5208 return sSelectedText;
5209 start.y = m_nSelViewBlockStart;
5210 start.x = 0;
5211 end.y = m_nSelViewBlockEnd;
5212 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5214 // first store the selected lines in one CString
5215 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5217 switch (m_pViewData->GetState(nViewLine))
5219 case DIFFSTATE_EMPTY:
5220 break;
5221 case DIFFSTATE_UNKNOWN:
5222 case DIFFSTATE_NORMAL:
5223 case DIFFSTATE_REMOVED:
5224 case DIFFSTATE_REMOVEDWHITESPACE:
5225 case DIFFSTATE_ADDED:
5226 case DIFFSTATE_ADDEDWHITESPACE:
5227 case DIFFSTATE_WHITESPACE:
5228 case DIFFSTATE_WHITESPACE_DIFF:
5229 case DIFFSTATE_CONFLICTED:
5230 case DIFFSTATE_CONFLICTED_IGNORED:
5231 case DIFFSTATE_CONFLICTADDED:
5232 case DIFFSTATE_CONFLICTEMPTY:
5233 case DIFFSTATE_CONFLICTRESOLVED:
5234 case DIFFSTATE_IDENTICALREMOVED:
5235 case DIFFSTATE_IDENTICALADDED:
5236 case DIFFSTATE_THEIRSREMOVED:
5237 case DIFFSTATE_THEIRSADDED:
5238 case DIFFSTATE_MOVED_FROM:
5239 case DIFFSTATE_MOVED_TO:
5240 case DIFFSTATE_YOURSREMOVED:
5241 case DIFFSTATE_YOURSADDED:
5242 case DIFFSTATE_EDITED:
5243 sSelectedText += GetViewLineChars(nViewLine);
5244 sSelectedText += _T("\r\n");
5245 break;
5248 // remove the non-selected chars from the first line, last line and last \r\n
5249 int nLeftCut = start.x;
5250 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5251 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5252 return sSelectedText;
5255 void CBaseView::OnEditGotoline()
5257 if (m_pViewData == NULL)
5258 return;
5259 // find the last and first line number
5260 int nViewLineCount = m_pViewData->GetCount();
5262 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5263 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5265 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5266 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5268 break;
5271 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5273 return;
5275 nLastLineNumber++;
5276 int nFirstLineNumber=1; // first is always 1
5278 CString sText;
5279 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5281 CGotoLineDlg dlg(this);
5282 dlg.SetLabel(sText);
5283 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5284 if (dlg.DoModal() == IDOK)
5286 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5288 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5290 HighlightViewLines(nViewLine, nViewLine);
5291 return;