Open the explorer with the converted temp file selected on a Ctrl+trippleclick on...
[TortoiseGit.git] / src / TortoiseMerge / BaseView.cpp
blob637a5ccc142b0eaa44ac9d62b526b62720e61bb9
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 = m_nOffsetChar + (point.x - GetMarginWidth());
2906 LONG xpos2 = xpos / GetCharWidth();
2907 if ((xpos % GetCharWidth()) >= (GetCharWidth()/2))
2908 xpos2++;
2909 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, xpos2);
2910 SetCaretAndGoalPosition(ptCaretPos);
2912 if (nFlags & MK_SHIFT)
2913 AdjustSelection(MOVERIGHT);
2914 else
2916 ClearSelection();
2917 SetupAllSelection(ptCaretPos.y, ptCaretPos.y);
2918 if (point.x < GetMarginWidth())
2920 // select the whole line
2921 m_ptSelectionViewPosStart = m_ptSelectionViewPosEnd = GetCaretViewPosition();
2922 m_ptSelectionViewPosEnd.x = GetViewLineLength(m_ptSelectionViewPosEnd.y);
2926 UpdateViewsCaretPosition();
2927 Invalidate();
2930 CView::OnLButtonDown(nFlags, point);
2933 enum ECharGroup { // ordered by priority low-to-hi
2934 CHG_UNKNOWN,
2935 CHG_CONTROL, // x00-x08, x0a-x1f
2936 CHG_WHITESPACE, // space tab
2937 CHG_PUNCTUATION, // 0x21-2f, x3a-x40, x5b-x60, x7b-x7f .,:;!?(){}[]/\<> ...
2938 CHG_WORDLETTER, // alpha num _ (others)
2941 ECharGroup GetCharGroup(wchar_t zChar)
2943 if (zChar == ' ' || zChar == '\t' )
2945 return CHG_WHITESPACE;
2947 if (zChar < 0x20)
2949 return CHG_CONTROL;
2951 if ((zChar >= 0x21 && zChar <= 0x2f)
2952 || (zChar >= 0x3a && zChar <= 0x40)
2953 || (zChar >= 0x5b && zChar <= 0x5e)
2954 || (zChar == 0x60)
2955 || (zChar >= 0x7b && zChar <= 0x7f))
2957 return CHG_PUNCTUATION;
2959 return CHG_WORDLETTER;
2962 void CBaseView::OnLButtonDblClk(UINT nFlags, CPoint point)
2964 if (m_pViewData == 0) {
2965 CView::OnLButtonDblClk(nFlags, point);
2966 return;
2969 const int nClickedLine = GetButtonEventLineIndex(point);
2970 if ( nClickedLine < 0)
2971 return;
2972 int nViewLine = GetViewLineForScreen(nClickedLine);
2973 if (point.x < GetMarginWidth()) // only if double clicked on the margin
2975 if((nViewLine < m_pViewData->GetCount())) // a double click on moved line scrolls to corresponding line
2977 if((m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_FROM)||
2978 (m_pViewData->GetState(nViewLine)==DIFFSTATE_MOVED_TO))
2980 int movedindex = m_pViewData->GetMovedIndex(nViewLine);
2981 int screenLine = FindViewLineNumber(movedindex);
2982 int nTop = screenLine - GetScreenLines()/2;
2983 if (nTop < 0)
2984 nTop = 0;
2985 ScrollAllToLine(nTop);
2986 // find and select the whole moved block
2987 int startSel = movedindex;
2988 int endSel = movedindex;
2989 while ((startSel > 0) && ((m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(startSel) == DIFFSTATE_MOVED_TO)))
2990 startSel--;
2991 startSel++;
2992 while ((endSel < GetLineCount()) && ((m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_FROM) || (m_pOtherViewData->GetState(endSel) == DIFFSTATE_MOVED_TO)))
2993 endSel++;
2994 endSel--;
2995 m_pOtherView->SetupSelection(startSel, endSel);
2996 return CView::OnLButtonDblClk(nFlags, point);
3000 if ((m_pMainFrame->m_bCollapsed)&&(m_pViewData->GetHideState(nViewLine) == HIDESTATE_MARKER))
3002 // a double click on a marker expands the hidden text
3003 int i = nViewLine;
3004 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) != HIDESTATE_SHOWN))
3006 if ((m_pwndLeft)&&(m_pwndLeft->m_pViewData))
3007 m_pwndLeft->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3008 if ((m_pwndRight)&&(m_pwndRight->m_pViewData))
3009 m_pwndRight->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3010 if ((m_pwndBottom)&&(m_pwndBottom->m_pViewData))
3011 m_pwndBottom->m_pViewData->SetLineHideState(i, HIDESTATE_SHOWN);
3012 i++;
3014 BuildAllScreen2ViewVector();
3015 if (m_pwndLeft)
3016 m_pwndLeft->Invalidate();
3017 if (m_pwndRight)
3018 m_pwndRight->Invalidate();
3019 if (m_pwndBottom)
3020 m_pwndBottom->Invalidate();
3022 else
3024 POINT ptCaretPos;
3025 ptCaretPos.y = nClickedLine;
3026 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3027 SetCaretPosition(ptCaretPos);
3028 ClearSelection();
3030 POINT ptViewCarret = GetCaretViewPosition();
3031 nViewLine = ptViewCarret.y;
3032 if (nViewLine >= GetViewCount())
3033 return;
3034 CString sLine = GetViewLine(nViewLine);
3035 int nLineLength = sLine.GetLength();
3036 int nBasePos = ptViewCarret.x;
3037 // get target char group
3038 ECharGroup eLeft = CHG_UNKNOWN;
3039 if (nBasePos > 0)
3041 eLeft = GetCharGroup(sLine[nBasePos-1]);
3043 ECharGroup eRight = CHG_UNKNOWN;
3044 if (nBasePos < nLineLength)
3046 eRight = GetCharGroup(sLine[nBasePos]);
3048 ECharGroup eTarget = max(eRight, eLeft);
3049 // find left margin
3050 int nLeft = nBasePos;
3051 while (nLeft > 0 && GetCharGroup(sLine[nLeft-1]) == eTarget)
3053 nLeft--;
3055 // get right margin
3056 int nRight = nBasePos;
3057 while (nRight < nLineLength && GetCharGroup(sLine[nRight]) == eTarget)
3059 nRight++;
3061 // set selection
3062 m_ptSelectionViewPosStart.x = nLeft;
3063 m_ptSelectionViewPosStart.y = nViewLine;
3064 m_ptSelectionViewPosEnd.x = nRight;
3065 m_ptSelectionViewPosEnd.y = nViewLine;
3066 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
3067 SetupAllViewSelection(nViewLine, nViewLine);
3068 // set caret
3069 ptCaretPos = ConvertViewPosToScreen(m_ptSelectionViewPosEnd);
3070 UpdateViewsCaretPosition();
3071 UpdateGoalPos();
3073 // set mark word
3074 m_sPreviousMarkedWord = m_sMarkedWord; // store marked word to recall in case of triple click
3075 int nMarkWidth = max(nRight - nLeft, 0);
3076 m_sMarkedWord = sLine.Mid(m_ptSelectionViewPosStart.x, nMarkWidth).Trim();
3077 if (m_sMarkedWord.Compare(m_sPreviousMarkedWord) == 0)
3079 m_sMarkedWord.Empty();
3082 if (m_pwndLeft)
3083 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3084 if (m_pwndRight)
3085 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3086 if (m_pwndBottom)
3087 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3089 Invalidate();
3090 if (m_pwndLocator)
3091 m_pwndLocator->Invalidate();
3094 CView::OnLButtonDblClk(nFlags, point);
3097 void CBaseView::OnLButtonTrippleClick( UINT /*nFlags*/, CPoint point )
3099 const int nClickedLine = GetButtonEventLineIndex(point);
3100 if (((point.y - HEADERHEIGHT) / GetLineHeight()) <= 0)
3102 if (!m_sConvertedFilePath.IsEmpty() && (GetKeyState(VK_CONTROL)&0x8000))
3104 PCIDLIST_ABSOLUTE __unaligned pidl = ILCreateFromPath((LPCTSTR)m_sConvertedFilePath);
3105 if (pidl)
3107 SHOpenFolderAndSelectItems(pidl,0,0,0);
3108 CoTaskMemFree((LPVOID)pidl);
3111 return;
3113 POINT ptCaretPos;
3114 ptCaretPos.y = nClickedLine;
3115 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3116 SetCaretAndGoalPosition(ptCaretPos);
3117 m_sMarkedWord = m_sPreviousMarkedWord; // recall previous Marked word
3118 if (m_pwndLeft)
3119 m_pwndLeft->SetMarkedWord(m_sMarkedWord);
3120 if (m_pwndRight)
3121 m_pwndRight->SetMarkedWord(m_sMarkedWord);
3122 if (m_pwndBottom)
3123 m_pwndBottom->SetMarkedWord(m_sMarkedWord);
3124 ClearSelection();
3125 m_ptSelectionViewPosStart.x = 0;
3126 m_ptSelectionViewPosStart.y = nClickedLine;
3127 m_ptSelectionViewPosEnd.x = GetLineLength(nClickedLine);
3128 m_ptSelectionViewPosEnd.y = nClickedLine;
3129 SetupSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3130 UpdateViewsCaretPosition();
3131 Invalidate();
3132 if (m_pwndLocator)
3133 m_pwndLocator->Invalidate();
3136 void CBaseView::OnEditCopy()
3138 CString sCopyData = GetSelectedText();
3140 if (!sCopyData.IsEmpty())
3142 CStringUtils::WriteAsciiStringToClipboard(sCopyData, m_hWnd);
3146 void CBaseView::OnMouseMove(UINT nFlags, CPoint point)
3148 if (m_pMainFrame->m_nMoveMovesToIgnore > 0)
3150 --m_pMainFrame->m_nMoveMovesToIgnore;
3151 CView::OnMouseMove(nFlags, point);
3152 return;
3154 int nMouseLine = GetButtonEventLineIndex(point);
3155 if (nMouseLine < -1)
3156 nMouseLine = -1;
3157 m_mouseInMargin = point.x < GetMarginWidth();
3159 ShowDiffLines(nMouseLine);
3161 KillTimer(IDT_SCROLLTIMER);
3162 if (nFlags & MK_LBUTTON)
3164 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3165 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3166 if (saveMouseLine < 0)
3167 return;
3168 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3169 if (HasSelection() &&
3170 ((nMouseLine >= m_nTopLine)&&(nMouseLine < GetLineCount())))
3172 POINT ptCaretPos = {charIndex, nMouseLine};
3173 SetCaretAndGoalPosition(ptCaretPos);
3174 AdjustSelection(MOVERIGHT);
3175 Invalidate();
3176 UpdateWindow();
3178 if (nMouseLine < m_nTopLine)
3180 ScrollAllToLine(m_nTopLine-1, TRUE);
3181 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3183 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3185 ScrollAllToLine(m_nTopLine+1, TRUE);
3186 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3188 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3190 ScrollAllSide(-1);
3191 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3193 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3195 ScrollAllSide(1);
3196 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3198 SetCapture();
3202 CView::OnMouseMove(nFlags, point);
3205 void CBaseView::OnLButtonUp(UINT nFlags, CPoint point)
3207 ShowDiffLines(-1);
3208 ReleaseCapture();
3209 KillTimer(IDT_SCROLLTIMER);
3211 __super::OnLButtonUp(nFlags, point);
3214 void CBaseView::OnTimer(UINT_PTR nIDEvent)
3216 if (nIDEvent == IDT_SCROLLTIMER)
3218 POINT point;
3219 GetCursorPos(&point);
3220 ScreenToClient(&point);
3221 int nMouseLine = GetButtonEventLineIndex(point);
3222 if (nMouseLine < -1)
3224 nMouseLine = -1;
3226 if (GetKeyState(VK_LBUTTON)&0x8000)
3228 int saveMouseLine = nMouseLine >= 0 ? nMouseLine : 0;
3229 saveMouseLine = saveMouseLine < GetLineCount() ? saveMouseLine : GetLineCount() - 1;
3230 int charIndex = CalculateCharIndex(saveMouseLine, m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth());
3231 if (nMouseLine < m_nTopLine)
3233 ScrollAllToLine(m_nTopLine-1, TRUE);
3234 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3236 if (nMouseLine >= m_nTopLine + GetScreenLines() - 2)
3238 ScrollAllToLine(m_nTopLine+1, TRUE);
3239 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3241 if (!m_pMainFrame->m_bWrapLines && ((m_nOffsetChar + (point.x - GetMarginWidth()) / GetCharWidth()) <= m_nOffsetChar))
3243 ScrollAllSide(-1);
3244 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3246 if (!m_pMainFrame->m_bWrapLines && (charIndex >= (GetScreenChars()+m_nOffsetChar-4)))
3248 ScrollAllSide(1);
3249 SetTimer(IDT_SCROLLTIMER, 20, NULL);
3255 CView::OnTimer(nIDEvent);
3258 void CBaseView::ShowDiffLines(int nLine)
3260 if ((nLine < m_nTopLine)||(nLine >= GetLineCount()))
3262 m_pwndLineDiffBar->ShowLines(nLine);
3263 nLine = -1;
3264 m_nMouseLine = nLine;
3265 return;
3268 if ((!m_pwndRight)||(!m_pwndLeft))
3269 return;
3270 if(m_pMainFrame->m_bOneWay)
3271 return;
3273 nLine = (nLine > (int)m_pwndRight->m_Screen2View.size() ? -1 : nLine);
3274 nLine = (nLine > (int)m_pwndLeft->m_Screen2View.size() ? -1 : nLine);
3276 if (nLine < 0)
3277 return;
3279 if (nLine != m_nMouseLine)
3281 if (nLine >= GetLineCount())
3282 nLine = -1;
3283 m_nMouseLine = nLine;
3284 m_pwndLineDiffBar->ShowLines(nLine);
3286 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
3289 const viewdata& CBaseView::GetEmptyLineData()
3291 static const viewdata emptyLine(_T(""), DIFFSTATE_EMPTY, -1, EOL_NOENDING, HIDESTATE_SHOWN, -1);
3292 return emptyLine;
3295 void CBaseView::InsertViewEmptyLines(int nFirstView, int nCount)
3297 for (int i = 0; i < nCount; i++)
3299 InsertViewData(nFirstView, GetEmptyLineData());
3304 void CBaseView::UpdateCaret()
3306 POINT ptCaretPos = GetCaretPosition();
3307 ptCaretPos.y = std::max<int>(std::min<int>(ptCaretPos.y, GetLineCount()-1), 0);
3308 ptCaretPos.x = std::max<int>(std::min<int>(ptCaretPos.x, GetLineLength(ptCaretPos.y)), 0);
3309 SetCaretPosition(ptCaretPos);
3311 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3313 if (m_bFocused &&
3314 ptCaretPos.y >= m_nTopLine &&
3315 ptCaretPos.y < (m_nTopLine+GetScreenLines()) &&
3316 nCaretOffset >= m_nOffsetChar &&
3317 nCaretOffset < (m_nOffsetChar+GetScreenChars()))
3319 CreateSolidCaret(2, GetLineHeight());
3320 SetCaretPos(TextToClient(ptCaretPos));
3321 ShowCaret();
3323 else
3325 HideCaret();
3329 POINT CBaseView::ConvertScreenPosToView(const POINT& pt)
3331 POINT ptViewPos;
3332 ptViewPos.x = pt.x;
3334 int nSubLine = GetSubLineOffset(pt.y);
3335 if (nSubLine > 0)
3337 for (int nScreenLine = pt.y-1; nScreenLine >= pt.y-nSubLine; nScreenLine--)
3339 ptViewPos.x += GetLineChars(nScreenLine).GetLength();
3343 ptViewPos.y = GetViewLineForScreen(pt.y);
3344 return ptViewPos;
3347 POINT CBaseView::ConvertViewPosToScreen(const POINT& pt)
3349 POINT ptPos;
3350 int nViewLineLenLeft = GetViewLineLength(pt.y);
3351 ptPos.x = min(nViewLineLenLeft, pt.x);
3352 ptPos.y = FindScreenLineForViewLine(pt.y);
3353 if (GetViewLineForScreen(ptPos.y) != pt.y )
3355 ptPos.x = 0;
3357 else if (GetSubLineOffset(ptPos.y) >= 0) // sublined
3359 int nSubLineLength = GetLineChars(ptPos.y).GetLength();
3360 while (nSubLineLength < ptPos.x)
3362 ptPos.x -= nSubLineLength;
3363 nViewLineLenLeft -= nSubLineLength;
3364 ptPos.y++;
3365 nSubLineLength = GetLineChars(ptPos.y).GetLength();
3367 // last pos of non last sub-line go to start of next screen line
3368 // Note: while this works correctly, it's not what a user might expect:
3369 // cursor-right when the caret is before the last char of a wrapped line
3370 // now moves the caret to the next line. But users expect the caret to
3371 // move to the right of the last char instead, and with another cursor-right
3372 // keystroke to move the caret to the next line.
3373 // Basically, this would require to handle two caret positions for the same
3374 // logical position in the line string (one on the last position of the first line,
3375 // one on the first position of the new line. For non-wrapped lines this works
3376 // because there's an 'invisible' newline char at the end of the first line.
3377 if (nSubLineLength == ptPos.x && nViewLineLenLeft > nSubLineLength)
3379 ptPos.x = 0;
3380 ptPos.y++;
3384 return ptPos;
3388 void CBaseView::EnsureCaretVisible()
3390 POINT ptCaretPos = GetCaretPosition();
3391 int nCaretOffset = CalculateActualOffset(ptCaretPos);
3393 if (ptCaretPos.y < m_nTopLine)
3394 ScrollAllToLine(ptCaretPos.y);
3395 int screnLines = GetScreenLines();
3396 if (screnLines)
3398 if (ptCaretPos.y >= (m_nTopLine+screnLines)-1)
3399 ScrollAllToLine(ptCaretPos.y-screnLines+2);
3400 if (nCaretOffset < m_nOffsetChar)
3401 ScrollAllToChar(nCaretOffset);
3402 if (nCaretOffset > (m_nOffsetChar+GetScreenChars()-1))
3403 ScrollAllToChar(nCaretOffset-GetScreenChars()+1);
3407 int CBaseView::CalculateActualOffset(const POINT& point)
3409 int nLineIndex = point.y;
3410 int nCharIndex = point.x;
3411 ASSERT(nCharIndex >= 0);
3412 CString sLine = GetLineChars(nLineIndex);
3413 int nLineLength = sLine.GetLength();
3414 return CountExpandedChars(sLine, min(nCharIndex, nLineLength));
3417 int CBaseView::CalculateCharIndex(int nLineIndex, int nActualOffset)
3419 int nLength = GetLineLength(nLineIndex);
3420 int nSubLine = GetSubLineOffset(nLineIndex);
3421 if (nSubLine>=0)
3423 int nViewLine = GetViewLineForScreen(nLineIndex);
3424 if ((nViewLine>=0)&&(nViewLine < (int)m_ScreenedViewLine.size()))
3426 int nMultilineCount = CountMultiLines(nViewLine);
3427 if ((nMultilineCount>0) && (nSubLine<nMultilineCount-1))
3429 nLength--;
3433 CString Line = GetLineChars(nLineIndex);
3434 int nIndex = 0;
3435 int nOffset = 0;
3436 int nTabSize = GetTabSize();
3437 while (nOffset < nActualOffset && nIndex < nLength)
3439 if (Line.GetAt(nIndex) == _T('\t'))
3440 nOffset += (nTabSize - nOffset % nTabSize);
3441 else
3442 ++nOffset;
3443 ++nIndex;
3445 return nIndex;
3448 POINT CBaseView::TextToClient(const POINT& point)
3450 POINT pt;
3451 int nOffsetScreenLine = max(0, (point.y - m_nTopLine));
3452 pt.y = nOffsetScreenLine * GetLineHeight();
3453 pt.x = CalculateActualOffset(point);
3455 int nLeft = GetMarginWidth() - m_nOffsetChar * GetCharWidth();
3456 CDC * pDC = GetDC();
3457 if (pDC)
3459 pDC->SelectObject(GetFont()); // is this right font ?
3460 int nScreenLine = nOffsetScreenLine + m_nTopLine;
3461 CString sLine = GetLineChars(nScreenLine);
3462 ExpandChars(sLine, 0, std::min<int>(pt.x, sLine.GetLength()), sLine);
3463 nLeft += pDC->GetTextExtent(sLine, pt.x).cx;
3464 ReleaseDC(pDC);
3465 } else {
3466 nLeft += pt.x * GetCharWidth();
3469 pt.x = nLeft;
3470 pt.y = (pt.y + GetLineHeight() + HEADERHEIGHT);
3471 return pt;
3474 void CBaseView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
3476 CView::OnChar(nChar, nRepCnt, nFlags);
3478 if (IsReadonly())
3479 return;
3481 if ((::GetKeyState(VK_LBUTTON) & 0x8000) != 0 ||
3482 (::GetKeyState(VK_RBUTTON) & 0x8000) != 0)
3484 return;
3487 if (!m_pViewData) // no data - nothing to do
3488 return;
3490 if (nChar == VK_F16)
3492 // generated by a ctrl+backspace - ignore.
3494 else if ((nChar > 31)||(nChar == VK_TAB))
3496 ResetUndoStep();
3497 RemoveSelectedText();
3498 POINT ptCaretViewPos = GetCaretViewPosition();
3499 int nViewLine = ptCaretViewPos.y;
3500 if ((nViewLine==0)&&(GetViewCount()==0))
3501 OnChar(VK_RETURN, 0, 0);
3502 viewdata lineData = GetViewData(nViewLine);
3503 lineData.sLine.Insert(ptCaretViewPos.x, (wchar_t)nChar);
3504 if (IsStateEmpty(lineData.state))
3506 // if not last line set EOL
3507 for (int nCheckViewLine = nViewLine+1; nCheckViewLine < GetViewCount(); nCheckViewLine++)
3509 if (!IsViewLineEmpty(nCheckViewLine))
3511 lineData.ending = lineendings;
3512 break;
3515 // make sure previous (non empty) line have EOL set
3516 for (int nCheckViewLine = nViewLine-1; nCheckViewLine > 0; nCheckViewLine--)
3518 if (!IsViewLineEmpty(nCheckViewLine))
3520 if (GetViewLineEnding(nCheckViewLine) == EOL_NOENDING)
3522 SetViewLineEnding(nCheckViewLine, lineendings);
3524 break;
3528 lineData.state = DIFFSTATE_EDITED;
3529 bool bNeedRenumber = false;
3530 if (lineData.linenumber == -1)
3532 lineData.linenumber = 0;
3533 bNeedRenumber = true;
3535 SetViewData(nViewLine, lineData);
3536 SaveUndoStep();
3537 BuildAllScreen2ViewVector(nViewLine);
3538 if (bNeedRenumber)
3540 UpdateViewLineNumbers();
3542 MoveCaretRight();
3543 UpdateGoalPos();
3545 else if (nChar == 10)
3547 int nViewLine = GetViewLineForScreen(GetCaretPosition().y);
3548 EOL eol = m_pViewData->GetLineEnding(nViewLine);
3549 EOL newEOL = EOL_CRLF;
3550 switch (eol)
3552 case EOL_CRLF:
3553 newEOL = EOL_CR;
3554 break;
3555 case EOL_CR:
3556 newEOL = EOL_LF;
3557 break;
3558 case EOL_LF:
3559 newEOL = EOL_CRLF;
3560 break;
3562 if (eol==EOL_NOENDING || eol==newEOL)
3563 // don't allow to change enter on empty line, or last text line (lines with EOL_NOENDING)
3564 // to add EOL on newly edited empty line hit enter
3565 // don't store into UNDO if no change happened
3566 // and don't mark file as modified
3567 return;
3568 AddUndoViewLine(nViewLine);
3569 m_pViewData->SetLineEnding(nViewLine, newEOL);
3570 m_pViewData->SetState(nViewLine, DIFFSTATE_EDITED);
3571 UpdateGoalPos();
3573 else if (nChar == VK_RETURN)
3575 // insert a new, fresh and empty line below the cursor
3576 RemoveSelectedText();
3578 CUndo::GetInstance().BeginGrouping();
3580 POINT ptCaretViewPos = GetCaretViewPosition();
3581 int nViewLine = ptCaretViewPos.y;
3582 int nLeft = ptCaretViewPos.x;
3583 CString sLine = GetViewLineChars(nViewLine);
3584 CString sLineLeft = sLine.Left(nLeft);
3585 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3586 EOL eOriginalEnding = EOL_AUTOLINE;
3587 if (m_pViewData->GetCount() > nViewLine)
3588 eOriginalEnding = GetViewLineEnding(nViewLine);
3590 if (!sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3592 viewdata newFirstLine(sLineLeft, DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3593 SetViewData(nViewLine, newFirstLine);
3596 int nInsertLine = (m_pViewData->GetCount()==0) ? 0 : nViewLine + 1;
3597 viewdata newLastLine(sLineRight, DIFFSTATE_EDITED, 1, eOriginalEnding, HIDESTATE_SHOWN, -1);
3598 InsertViewData(nInsertLine, newLastLine);
3599 SaveUndoStep();
3601 // adds new line everywhere except me
3602 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3604 m_pwndLeft->InsertViewEmptyLines(nInsertLine, 1);
3606 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3608 m_pwndRight->InsertViewEmptyLines(nInsertLine, 1);
3610 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3612 m_pwndBottom->InsertViewEmptyLines(nInsertLine, 1);
3614 SaveUndoStep();
3616 UpdateViewLineNumbers();
3617 SaveUndoStep();
3618 CUndo::GetInstance().EndGrouping();
3620 BuildAllScreen2ViewVector();
3621 // move the cursor to the new line
3622 ptCaretViewPos = SetupPoint(0, nViewLine+1);
3623 SetCaretAndGoalViewPosition(ptCaretViewPos);
3625 else
3626 return; // Unknown control character -- ignore it.
3627 ClearSelection();
3628 EnsureCaretVisible();
3629 UpdateCaret();
3630 SetModified(true);
3631 Invalidate(FALSE);
3634 void CBaseView::AddUndoViewLine(int nViewLine, bool bAddEmptyLine)
3636 ResetUndoStep();
3637 m_AllState.left.AddViewLineFromView(m_pwndLeft, nViewLine, bAddEmptyLine);
3638 m_AllState.right.AddViewLineFromView(m_pwndRight, nViewLine, bAddEmptyLine);
3639 m_AllState.bottom.AddViewLineFromView(m_pwndBottom, nViewLine, bAddEmptyLine);
3640 SaveUndoStep();
3641 RecalcAllVertScrollBars();
3642 Invalidate(FALSE);
3645 void CBaseView::AddEmptyViewLine(int nViewLineIndex)
3647 if (m_pViewData == NULL)
3648 return;
3649 int viewLine = nViewLineIndex;
3650 EOL ending = m_pViewData->GetLineEnding(viewLine);
3651 if (ending == EOL_NOENDING)
3653 ending = lineendings;
3655 viewdata newLine(_T(""), DIFFSTATE_EDITED, -1, ending, HIDESTATE_SHOWN, -1);
3656 if (IsTarget()) // TODO: once more wievs will writable this is not correct anymore
3658 CString sPartLine = GetViewLineChars(nViewLineIndex);
3659 int nPosx = GetCaretPosition().x; // should be view pos ?
3660 m_pViewData->SetLine(viewLine, sPartLine.Left(nPosx));
3661 sPartLine = sPartLine.Mid(nPosx);
3662 newLine.sLine = sPartLine;
3664 m_pViewData->InsertData(viewLine+1, newLine);
3665 BuildAllScreen2ViewVector();
3668 void CBaseView::RemoveSelectedText()
3670 if (m_pViewData == NULL)
3671 return;
3672 if (!HasTextSelection())
3673 return;
3675 // fix selection if starts or ends on empty line
3676 SetCaretViewPosition(m_ptSelectionViewPosEnd);
3677 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3680 m_ptSelectionViewPosEnd = GetCaretViewPosition();
3681 SetCaretViewPosition(m_ptSelectionViewPosStart);
3682 while (IsViewLineEmpty(GetCaretViewPosition().y) && MoveCaretRight())
3685 m_ptSelectionViewPosStart = GetCaretViewPosition();
3686 if (!HasTextSelection())
3688 ClearSelection();
3689 return;
3692 // We want to undo the insertion in a single step.
3693 ResetUndoStep();
3694 CUndo::GetInstance().BeginGrouping();
3696 // combine first and last line
3697 viewdata oFirstLine = GetViewData(m_ptSelectionViewPosStart.y);
3698 viewdata oLastLine = GetViewData(m_ptSelectionViewPosEnd.y);
3699 oFirstLine.sLine = oFirstLine.sLine.Left(m_ptSelectionViewPosStart.x) + oLastLine.sLine.Mid(m_ptSelectionViewPosEnd.x);
3700 oFirstLine.ending = oLastLine.ending;
3701 oFirstLine.state = DIFFSTATE_EDITED;
3702 SetViewData(m_ptSelectionViewPosStart.y, oFirstLine);
3704 // clean up middle lines if any
3705 if (m_ptSelectionViewPosStart.y != m_ptSelectionViewPosEnd.y)
3707 viewdata oEmptyLine = GetEmptyLineData();
3708 for (int nViewLine = m_ptSelectionViewPosStart.y+1; nViewLine <= m_ptSelectionViewPosEnd.y; nViewLine++)
3710 SetViewData(nViewLine, oEmptyLine);
3712 SaveUndoStep();
3714 if (CleanEmptyLines())
3716 BuildAllScreen2ViewVector(); // schedule full rebuild
3718 SaveUndoStep();
3719 UpdateViewLineNumbers();
3722 SaveUndoStep();
3723 CUndo::GetInstance().EndGrouping();
3725 SetModified();
3726 BuildAllScreen2ViewVector(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
3727 SetCaretViewPosition(m_ptSelectionViewPosStart);
3728 UpdateGoalPos();
3729 ClearSelection();
3730 UpdateCaret();
3731 EnsureCaretVisible();
3732 Invalidate(FALSE);
3735 void CBaseView::PasteText()
3737 if (!OpenClipboard())
3738 return;
3740 CString sClipboardText;
3741 HGLOBAL hglb = GetClipboardData(CF_TEXT);
3742 if (hglb)
3744 LPCSTR lpstr = (LPCSTR)GlobalLock(hglb);
3745 sClipboardText = CString(lpstr);
3746 GlobalUnlock(hglb);
3748 hglb = GetClipboardData(CF_UNICODETEXT);
3749 if (hglb)
3751 LPCTSTR lpstr = (LPCTSTR)GlobalLock(hglb);
3752 sClipboardText = lpstr;
3753 GlobalUnlock(hglb);
3755 CloseClipboard();
3757 if (sClipboardText.IsEmpty())
3758 return;
3760 sClipboardText.Replace(_T("\r\n"), _T("\r"));
3761 sClipboardText.Replace('\n', '\r');
3763 ResetUndoStep();
3765 POINT ptCaretViewPos = GetCaretViewPosition();
3766 int nLeft = ptCaretViewPos.x;
3767 int nViewLine = ptCaretViewPos.y;
3769 if ((nViewLine==0)&&(GetViewCount()==0))
3770 OnChar(VK_RETURN, 0, 0);
3772 std::vector<CString> lines;
3773 int nStart = 0;
3774 int nEolPos = 0;
3775 while ((nEolPos = sClipboardText.Find('\r', nEolPos))>=0)
3777 CString sLine = sClipboardText.Mid(nStart, nEolPos-nStart);
3778 lines.push_back(sLine);
3779 nEolPos++;
3780 nStart = nEolPos;
3782 CString sLine = sClipboardText.Mid(nStart);
3783 lines.push_back(sLine);
3785 int nLinesToPaste = (int)lines.size();
3786 if (nLinesToPaste > 1)
3788 // multiline text
3790 // We want to undo the multiline insertion in a single step.
3791 CUndo::GetInstance().BeginGrouping();
3793 sLine = GetViewLineChars(nViewLine);
3794 CString sLineLeft = sLine.Left(nLeft);
3795 CString sLineRight = sLine.Right(sLine.GetLength() - nLeft);
3796 EOL eOriginalEnding = GetViewLineEnding(nViewLine);
3797 viewdata newLine(_T(""), DIFFSTATE_EDITED, 1, lineendings, HIDESTATE_SHOWN, -1);
3798 if (!lines[0].IsEmpty() || !sLineRight.IsEmpty() || (eOriginalEnding!=lineendings))
3800 newLine.sLine = sLineLeft + lines[0];
3801 SetViewData(nViewLine, newLine);
3804 int nInsertLine = nViewLine;
3805 for (int i = 1; i < nLinesToPaste-1; i++)
3807 newLine.sLine = lines[i];
3808 InsertViewData(++nInsertLine, newLine);
3810 newLine.sLine = lines[nLinesToPaste-1] + sLineRight;
3811 newLine.ending = eOriginalEnding;
3812 InsertViewData(++nInsertLine, newLine);
3814 SaveUndoStep();
3816 // adds new lines everywhere except me
3817 if (IsViewGood(m_pwndLeft) && m_pwndLeft!=this)
3819 m_pwndLeft->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3821 if (IsViewGood(m_pwndRight) && m_pwndRight!=this)
3823 m_pwndRight->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3825 if (IsViewGood(m_pwndBottom) && m_pwndBottom!=this)
3827 m_pwndBottom->InsertViewEmptyLines(nViewLine+1, nLinesToPaste-1);
3829 SaveUndoStep();
3831 UpdateViewLineNumbers();
3832 CUndo::GetInstance().EndGrouping();
3834 ptCaretViewPos = SetupPoint(lines[nLinesToPaste-1].GetLength(), nInsertLine);
3836 else
3838 // single line text - just insert it
3839 sLine = GetViewLineChars(nViewLine);
3840 sLine.Insert(nLeft, sClipboardText);
3841 ptCaretViewPos = SetupPoint(nLeft + sClipboardText.GetLength(), nViewLine);
3842 SetViewLine(nViewLine, sLine);
3843 SetViewState(nViewLine, DIFFSTATE_EDITED);
3844 SaveUndoStep();
3847 SetModified();
3848 RefreshViews();
3849 BuildAllScreen2ViewVector();
3850 UpdateCaretViewPosition(ptCaretViewPos);
3853 void CBaseView::OnCaretDown()
3855 POINT ptCaretPos = GetCaretPosition();
3856 int nLine = ptCaretPos.y;
3857 int nNextLine = nLine + 1;
3858 if (nNextLine >= GetLineCount()) // already at last line
3860 return;
3863 POINT ptCaretViewPos = GetCaretViewPosition();
3864 int nViewLine = ptCaretViewPos.y;
3865 int nNextViewLine = GetViewLineForScreen(nNextLine);
3866 if (!((nNextViewLine == nViewLine) && (GetSubLineOffset(nNextLine)<CountMultiLines(nNextViewLine)))) // not on same view line
3868 // find next suitable screen line
3869 while ((nNextViewLine == nViewLine) || IsViewLineHidden(nNextViewLine))
3871 nNextLine++;
3872 if (nNextLine >= GetLineCount())
3874 return;
3876 nNextViewLine = GetViewLineForScreen(nNextLine);
3879 ptCaretPos.y = nNextLine;
3880 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3881 SetCaretPosition(ptCaretPos);
3882 OnCaretMove(MOVELEFT);
3883 ShowDiffLines(ptCaretPos.y);
3886 bool CBaseView::MoveCaretLeft()
3888 POINT ptCaretViewPos = GetCaretViewPosition();
3890 //int nViewLine = ptCaretViewPos.y;
3891 if (ptCaretViewPos.x == 0)
3893 int nPrevLine = GetCaretPosition().y;
3894 int nPrevViewLine;
3895 do {
3896 nPrevLine--;
3897 if (nPrevLine < 0)
3899 return false;
3901 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3902 } while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine));
3903 ptCaretViewPos = ConvertScreenPosToView(SetupPoint(GetLineLength(nPrevLine), nPrevLine));
3904 ShowDiffLines(nPrevLine);
3906 else
3907 --ptCaretViewPos.x;
3909 SetCaretAndGoalViewPosition(ptCaretViewPos);
3910 return true;
3913 bool CBaseView::MoveCaretRight()
3915 POINT ptCaretViewPos = GetCaretViewPosition();
3917 int nViewLine = ptCaretViewPos.y;
3918 int nViewLineLen = GetViewLineLength(nViewLine);
3919 if (ptCaretViewPos.x >= nViewLineLen)
3921 int nNextLine = GetCaretPosition().y;
3922 int nNextViewLine;
3923 do {
3924 nNextLine++;
3925 if (nNextLine >= GetLineCount())
3927 return false;
3929 nNextViewLine = GetViewLineForScreen(nNextLine);
3930 } while (nNextViewLine == nViewLine || IsViewLineHidden(nNextViewLine));
3931 ptCaretViewPos.y = nNextViewLine;
3932 ptCaretViewPos.x = 0;
3933 ShowDiffLines(nNextLine);
3935 else
3936 ++ptCaretViewPos.x;
3938 SetCaretAndGoalViewPosition(ptCaretViewPos);
3939 return true;
3942 void CBaseView::UpdateGoalPos()
3944 m_nCaretGoalPos = CalculateActualOffset(GetCaretPosition());
3947 void CBaseView::OnCaretLeft()
3949 MoveCaretLeft();
3950 OnCaretMove(MOVELEFT);
3953 void CBaseView::OnCaretRight()
3955 MoveCaretRight();
3956 OnCaretMove(MOVERIGHT);
3959 void CBaseView::OnCaretUp()
3961 POINT ptCaretPos = GetCaretPosition();
3962 int nLine = ptCaretPos.y;
3963 if (nLine <= 0) // already at first line
3965 return;
3967 int nPrevLine = nLine - 1;
3969 POINT ptCaretViewPos = GetCaretViewPosition();
3970 int nViewLine = ptCaretViewPos.y;
3971 int nPrevViewLine = GetViewLineForScreen(nPrevLine);
3972 if (nPrevViewLine != nViewLine) // not on same view line
3974 // find previous suitable screen line
3975 while ((GetSubLineOffset(nPrevLine) >= CountMultiLines(nPrevViewLine)) || IsViewLineHidden(nPrevViewLine))
3977 if (nPrevLine <= 0)
3979 return;
3981 nPrevLine--;
3982 nPrevViewLine = GetViewLineForScreen(nPrevLine);
3985 ptCaretPos.y = nPrevLine;
3986 ptCaretPos.x = CalculateCharIndex(ptCaretPos.y, m_nCaretGoalPos);
3987 SetCaretPosition(ptCaretPos);
3988 OnCaretMove(MOVELEFT);
3989 ShowDiffLines(ptCaretPos.y);
3992 bool CBaseView::IsWordSeparator(const wchar_t ch) const
3994 return ch == ' ' || ch == '\t' || (m_sWordSeparators.Find(ch) >= 0);
3997 bool CBaseView::IsCaretAtWordBoundary()
3999 POINT ptViewCaret = GetCaretViewPosition();
4000 CString line = GetViewLineChars(ptViewCaret.y);
4001 if (line.IsEmpty())
4002 return false; // no boundary at the empty lines
4003 if (ptViewCaret.x == 0)
4004 return !IsWordSeparator(line.GetAt(ptViewCaret.x));
4005 if (ptViewCaret.x >= GetViewLineLength(ptViewCaret.y))
4006 return !IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4007 return
4008 IsWordSeparator(line.GetAt(ptViewCaret.x)) !=
4009 IsWordSeparator(line.GetAt(ptViewCaret.x - 1));
4012 void CBaseView::UpdateViewsCaretPosition()
4014 POINT ptCaretPos = GetCaretPosition();
4015 if (m_pwndBottom && m_pwndBottom!=this)
4016 m_pwndBottom->UpdateCaretPosition(ptCaretPos);
4017 if (m_pwndLeft && m_pwndLeft!=this)
4018 m_pwndLeft->UpdateCaretPosition(ptCaretPos);
4019 if (m_pwndRight && m_pwndRight!=this)
4020 m_pwndRight->UpdateCaretPosition(ptCaretPos);
4023 void CBaseView::OnCaretWordleft()
4025 MoveCaretWordLeft();
4026 OnCaretMove(MOVELEFT);
4029 void CBaseView::OnCaretWordright()
4031 MoveCaretWordRight();
4032 OnCaretMove(MOVERIGHT);
4035 void CBaseView::MoveCaretWordLeft()
4037 while (MoveCaretLeft() && !IsCaretAtWordBoundary())
4042 void CBaseView::MoveCaretWordRight()
4044 while (MoveCaretRight() && !IsCaretAtWordBoundary())
4049 void CBaseView::ClearCurrentSelection()
4051 m_ptSelectionViewPosStart = GetCaretViewPosition();
4052 m_ptSelectionViewPosEnd = m_ptSelectionViewPosStart;
4053 m_ptSelectionViewPosOrigin = m_ptSelectionViewPosStart;
4054 m_nSelViewBlockStart = -1;
4055 m_nSelViewBlockEnd = -1;
4056 Invalidate(FALSE);
4059 void CBaseView::ClearSelection()
4061 if (m_pwndLeft)
4062 m_pwndLeft->ClearCurrentSelection();
4063 if (m_pwndRight)
4064 m_pwndRight->ClearCurrentSelection();
4065 if (m_pwndBottom)
4066 m_pwndBottom->ClearCurrentSelection();
4069 void CBaseView::AdjustSelection(bool bMoveLeft)
4071 POINT ptCaretViewPos = GetCaretViewPosition();
4072 if (ArePointsSame(m_ptSelectionViewPosOrigin, SetupPoint(-1, -1)))
4074 // select all have been used recently update origin
4075 m_ptSelectionViewPosOrigin = bMoveLeft ? m_ptSelectionViewPosEnd : m_ptSelectionViewPosStart;
4077 if ((ptCaretViewPos.y < m_ptSelectionViewPosOrigin.y) ||
4078 (ptCaretViewPos.y == m_ptSelectionViewPosOrigin.y && ptCaretViewPos.x <= m_ptSelectionViewPosOrigin.x))
4080 m_ptSelectionViewPosStart = ptCaretViewPos;
4081 m_ptSelectionViewPosEnd = m_ptSelectionViewPosOrigin;
4083 else
4085 m_ptSelectionViewPosStart = m_ptSelectionViewPosOrigin;
4086 m_ptSelectionViewPosEnd = ptCaretViewPos;
4089 SetupAllViewSelection(m_ptSelectionViewPosStart.y, m_ptSelectionViewPosEnd.y);
4091 Invalidate(FALSE);
4094 void CBaseView::OnEditCut()
4096 if (IsWritable())
4098 OnEditCopy();
4099 RemoveSelectedText();
4103 void CBaseView::OnEditPaste()
4105 if (IsWritable())
4107 CUndo::GetInstance().BeginGrouping();
4108 RemoveSelectedText();
4109 PasteText();
4110 CUndo::GetInstance().EndGrouping();
4114 void CBaseView::DeleteFonts()
4116 for (int i=0; i<fontsCount; i++)
4118 if (m_apFonts[i] != NULL)
4120 m_apFonts[i]->DeleteObject();
4121 delete m_apFonts[i];
4122 m_apFonts[i] = NULL;
4127 void CBaseView::OnCaretMove(bool bMoveLeft)
4129 bool bShift = !!(GetKeyState(VK_SHIFT)&0x8000);
4130 OnCaretMove(bMoveLeft, bShift);
4133 void CBaseView::OnCaretMove(bool bMoveLeft, bool isShiftPressed)
4135 if(isShiftPressed)
4136 AdjustSelection(bMoveLeft);
4137 else
4138 ClearSelection();
4139 EnsureCaretVisible();
4140 UpdateCaret();
4143 void CBaseView::AddContextItems(CIconMenu& popup, DiffStates /*state*/)
4145 AddCutCopyAndPaste(popup);
4148 void CBaseView::AddCutCopyAndPaste(CIconMenu& popup)
4150 popup.AppendMenu(MF_SEPARATOR, NULL);
4151 CString temp;
4152 temp.LoadString(IDS_EDIT_COPY);
4153 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_COPY, temp);
4154 if (IsWritable())
4156 temp.LoadString(IDS_EDIT_CUT);
4157 popup.AppendMenu(MF_STRING | (HasTextSelection() ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_CUT, temp);
4158 temp.LoadString(IDS_EDIT_PASTE);
4159 popup.AppendMenu(MF_STRING | (CAppUtils::HasClipboardFormat(CF_UNICODETEXT)||CAppUtils::HasClipboardFormat(CF_TEXT) ? MF_ENABLED : MF_DISABLED|MF_GRAYED), ID_EDIT_PASTE, temp);
4163 void CBaseView::CompensateForKeyboard(CPoint& point)
4165 // if the context menu is invoked through the keyboard, we have to use
4166 // a calculated position on where to anchor the menu on
4167 if (ArePointsSame(point, SetupPoint(-1, -1)))
4169 CRect rect;
4170 GetWindowRect(&rect);
4171 point = rect.CenterPoint();
4175 HICON CBaseView::LoadIcon(WORD iconId)
4177 HANDLE icon = ::LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(iconId),
4178 IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR);
4179 return (HICON)icon;
4182 void CBaseView::ReleaseBitmap()
4184 if (m_pCacheBitmap != NULL)
4186 m_pCacheBitmap->DeleteObject();
4187 delete m_pCacheBitmap;
4188 m_pCacheBitmap = NULL;
4192 void CBaseView::BuildMarkedWordArray()
4194 int lineCount = GetLineCount();
4195 m_arMarkedWordLines.clear();
4196 m_arMarkedWordLines.reserve(lineCount);
4197 bool bDoit = !m_sMarkedWord.IsEmpty();
4198 for (int i = 0; i < lineCount; ++i)
4200 if (bDoit)
4202 CString line = GetLineChars(i);
4204 if (!line.IsEmpty())
4206 m_arMarkedWordLines.push_back(line.Find(m_sMarkedWord) != -1);
4208 else
4209 m_arMarkedWordLines.push_back(0);
4211 else
4212 m_arMarkedWordLines.push_back(0);
4216 void CBaseView::BuildFindStringArray()
4218 int lineCount = GetLineCount();
4219 m_arFindStringLines.clear();
4220 m_arFindStringLines.reserve(lineCount);
4221 bool bDoit = !m_sFindText.IsEmpty();
4222 int s = 0;
4223 int e = 0;
4224 for (int i = 0; i < lineCount; ++i)
4226 if (bDoit)
4228 CString line = GetLineChars(i);
4230 if (!line.IsEmpty())
4232 line = line.MakeLower();
4233 m_arFindStringLines.push_back(StringFound(line, SearchNext, s, e));
4235 else
4236 m_arFindStringLines.push_back(0);
4238 else
4239 m_arFindStringLines.push_back(0);
4241 UpdateLocator();
4244 bool CBaseView::GetInlineDiffPositions(int nViewLine, std::vector<inlineDiffPos>& positions)
4246 if (!m_bShowInlineDiff)
4247 return false;
4248 if ((m_pwndBottom != NULL) && !(m_pwndBottom->IsHidden()))
4249 return false;
4251 CString sLine = GetViewLineChars(nViewLine);
4252 if (sLine.IsEmpty())
4253 return false;
4255 CheckOtherView();
4256 if (!m_pOtherViewData)
4257 return false;
4259 CString sDiffLine = m_pOtherViewData->GetLine(nViewLine);
4260 if (sDiffLine.IsEmpty())
4261 return false;
4263 CString sLineExp = ExpandChars(sLine);
4264 CString sDiffLineExp = ExpandChars(sDiffLine);
4265 svn_diff_t * diff = NULL;
4266 m_svnlinediff.Diff(&diff, sLineExp, sLineExp.GetLength(), sDiffLineExp, sDiffLineExp.GetLength(), m_bInlineWordDiff);
4267 if (!diff || !SVNLineDiff::ShowInlineDiff(diff))
4268 return false;
4270 size_t lineoffset = 0;
4271 size_t position = 0;
4272 while (diff)
4274 apr_off_t len = diff->original_length;
4275 size_t oldpos = position;
4277 for (apr_off_t i = 0; i < len; ++i)
4279 position += m_svnlinediff.m_line1tokens[lineoffset].size();
4280 lineoffset++;
4283 if (diff->type == svn_diff__type_diff_modified)
4285 inlineDiffPos p;
4286 p.start = oldpos;
4287 p.end = position;
4288 positions.push_back(p);
4291 diff = diff->next;
4294 return !positions.empty();
4297 void CBaseView::OnNavigateNextinlinediff()
4299 int nX;
4300 if (GetNextInlineDiff(nX))
4302 POINT ptCaretViewPos = GetCaretViewPosition();
4303 ptCaretViewPos.x = nX;
4304 SetCaretAndGoalViewPosition(ptCaretViewPos);
4305 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4306 EnsureCaretVisible();
4310 void CBaseView::OnNavigatePrevinlinediff()
4312 int nX;
4313 if (GetPrevInlineDiff(nX))
4315 POINT ptCaretViewPos = GetCaretViewPosition();
4316 ptCaretViewPos.x = nX;
4317 SetCaretAndGoalViewPosition(ptCaretViewPos);
4318 m_ptSelectionViewPosOrigin = ptCaretViewPos;
4319 EnsureCaretVisible();
4323 bool CBaseView::HasNextInlineDiff()
4325 int nPos;
4326 return GetNextInlineDiff(nPos);
4329 bool CBaseView::GetNextInlineDiff(int & nPos)
4331 POINT ptCaretViewPos = GetCaretViewPosition();
4332 std::vector<inlineDiffPos> positions;
4333 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4335 for (auto it = positions.cbegin(); it != positions.cend(); ++it)
4337 if (it->start > ptCaretViewPos.x)
4339 nPos = (LONG)it->start;
4340 return true;
4342 if (it->end > ptCaretViewPos.x)
4344 nPos = (LONG)it->end;
4345 return true;
4349 return false;
4352 bool CBaseView::HasPrevInlineDiff()
4354 int nPos;
4355 return GetPrevInlineDiff(nPos);
4358 bool CBaseView::GetPrevInlineDiff(int & nPos)
4360 POINT ptCaretViewPos = GetCaretViewPosition();
4361 std::vector<inlineDiffPos> positions;
4362 if (GetInlineDiffPositions(ptCaretViewPos.y, positions))
4364 for (auto it = positions.crbegin(); it != positions.crend(); ++it)
4366 if ( it->end < ptCaretViewPos.x)
4368 nPos = (LONG)it->end;
4369 return true;
4371 if ( it->start < ptCaretViewPos.x)
4373 nPos = (LONG)it->start;
4374 return true;
4378 return false;
4381 CBaseView * CBaseView::GetFirstGoodView()
4383 if (IsViewGood(m_pwndLeft))
4384 return m_pwndLeft;
4385 if (IsViewGood(m_pwndRight))
4386 return m_pwndRight;
4387 if (IsViewGood(m_pwndBottom))
4388 return m_pwndBottom;
4389 return NULL;
4392 void CBaseView::BuildAllScreen2ViewVector()
4394 CBaseView * p_pwndView = GetFirstGoodView();
4395 if (p_pwndView)
4397 m_Screen2View.ScheduleFullRebuild(p_pwndView->m_pViewData);
4401 void CBaseView::BuildAllScreen2ViewVector(int nViewLine)
4403 BuildAllScreen2ViewVector(nViewLine, nViewLine);
4406 void CBaseView::BuildAllScreen2ViewVector(int nFirstViewLine, int nLastViewLine)
4408 CBaseView * p_pwndView = GetFirstGoodView();
4409 if (p_pwndView)
4411 m_Screen2View.ScheduleRangeRebuild(p_pwndView->m_pViewData, nFirstViewLine, nLastViewLine);
4415 void CBaseView::UpdateViewLineNumbers()
4417 int nLineNumber = 0;
4418 int nViewLineCount = GetViewCount();
4419 for (int nViewLine = 0; nViewLine < nViewLineCount; nViewLine++)
4421 int oldLine = (int)GetViewLineNumber(nViewLine);
4422 if (oldLine >= 0)
4423 SetViewLineNumber(nViewLine, nLineNumber++);
4425 m_nDigits = 0;
4428 int CBaseView::CleanEmptyLines()
4430 int nRemovedCount = 0;
4431 int nViewLineCount = GetViewCount();
4432 bool bCheckLeft = IsViewGood(m_pwndLeft);
4433 bool bCheckRight = IsViewGood(m_pwndRight);
4434 bool bCheckBottom = IsViewGood(m_pwndBottom);
4435 for (int nViewLine = 0; nViewLine < nViewLineCount; )
4437 bool bAllEmpty = true;
4438 bAllEmpty &= !bCheckLeft || IsStateEmpty(m_pwndLeft->GetViewState(nViewLine));
4439 bAllEmpty &= !bCheckRight || IsStateEmpty(m_pwndRight->GetViewState(nViewLine));
4440 bAllEmpty &= !bCheckBottom || IsStateEmpty(m_pwndBottom->GetViewState(nViewLine));
4441 if (bAllEmpty)
4443 if (bCheckLeft)
4445 m_pwndLeft->RemoveViewData(nViewLine);
4447 if (bCheckRight)
4449 m_pwndRight->RemoveViewData(nViewLine);
4451 if (bCheckBottom)
4453 m_pwndBottom->RemoveViewData(nViewLine);
4455 if (CUndo::GetInstance().IsGrouping()) // if use group undo -> ensure back adding goes in right (reversed) order
4457 SaveUndoStep();
4459 nViewLineCount--;
4460 nRemovedCount++;
4461 continue;
4463 nViewLine++;
4465 if (nRemovedCount != 0)
4467 if (bCheckLeft)
4469 m_pwndLeft->SetModified();
4471 if (bCheckRight)
4473 m_pwndRight->SetModified();
4475 if (bCheckBottom)
4477 m_pwndBottom->SetModified();
4480 return nRemovedCount;
4483 int CBaseView::FindScreenLineForViewLine( int viewLine )
4485 return m_Screen2View.FindScreenLineForViewLine(viewLine);
4488 int CBaseView::CountMultiLines( int nViewLine )
4490 if (m_ScreenedViewLine.empty())
4491 return 0; // in case the view is completely empty
4493 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4495 if (m_ScreenedViewLine[nViewLine].bSublinesSet)
4497 return (int)m_ScreenedViewLine[nViewLine].SubLines.size();
4500 CString multiline = CStringUtils::WordWrap(m_pViewData->GetLine(nViewLine), GetScreenChars()-1, false, true, GetTabSize()); // GetMultiLine(nLine);
4502 TScreenedViewLine oScreenedLine;
4503 // tokenize string
4504 int prevpos = 0;
4505 int pos = 0;
4506 while ((pos = multiline.Find('\n', pos)) >= 0)
4508 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos, pos-prevpos)); // WordWrap could return vector/list of lines instead of string
4509 pos++;
4510 prevpos = pos;
4512 oScreenedLine.SubLines.push_back(multiline.Mid(prevpos));
4513 oScreenedLine.bSublinesSet = true;
4514 m_ScreenedViewLine[nViewLine] = oScreenedLine;
4516 return CountMultiLines(nViewLine);
4519 /// prepare inline diff cache
4520 LineColors & CBaseView::GetLineColors(int nViewLine)
4522 ASSERT(nViewLine < (int)m_ScreenedViewLine.size());
4524 if (m_bWhitespaceInlineDiffs)
4526 if (m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace)
4527 return m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace;
4529 else
4531 if (m_ScreenedViewLine[nViewLine].bLineColorsSet)
4532 return m_ScreenedViewLine[nViewLine].lineColors;
4535 LineColors oLineColors;
4536 // set main line color
4537 COLORREF crBkgnd, crText;
4538 DiffStates diffState = m_pViewData->GetState(nViewLine);
4539 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4540 oLineColors.SetColor(0, crText, crBkgnd);
4542 do {
4543 if (!m_bShowInlineDiff)
4544 break;
4546 if ((diffState == DIFFSTATE_NORMAL)&&(!m_bWhitespaceInlineDiffs))
4547 break;
4549 CString sLine = GetViewLineChars(nViewLine);
4550 if (sLine.IsEmpty())
4551 break;
4552 if (!m_pOtherView)
4553 break;
4555 CString sDiffLine = m_pOtherView->GetViewLineChars(nViewLine);
4556 if (sDiffLine.IsEmpty())
4557 break;
4559 svn_diff_t * diff = NULL;
4560 if (sLine.GetLength() > (int)m_nInlineDiffMaxLineLength)
4561 break;
4562 m_svnlinediff.Diff(&diff, sLine, sLine.GetLength(), sDiffLine, sDiffLine.GetLength(), m_bInlineWordDiff);
4563 if (!diff || !SVNLineDiff::ShowInlineDiff(diff) || !diff->next)
4564 break;
4566 int lineoffset = 0;
4567 int nTextStartOffset = 0;
4568 std::map<int, COLORREF> removedPositions;
4569 while (diff)
4571 apr_off_t len = diff->original_length;
4573 CString s;
4574 for (int i = 0; i < len; ++i)
4576 s += m_svnlinediff.m_line1tokens[lineoffset].c_str();
4577 lineoffset++;
4579 bool bInlineDiff = (diff->type == svn_diff__type_diff_modified);
4580 int nTextLength = s.GetLength();
4582 CDiffColors::GetInstance().GetColors(diffState, crBkgnd, crText);
4583 if ((m_bShowInlineDiff)&&(bInlineDiff))
4585 crBkgnd = InlineViewLineDiffColor(nViewLine);
4587 else
4589 crBkgnd = m_ModifiedBk;
4592 if (len < diff->modified_length)
4594 removedPositions[nTextStartOffset] = m_InlineRemovedBk;
4596 oLineColors.SetColor(nTextStartOffset, crText, crBkgnd);
4598 nTextStartOffset += nTextLength;
4599 diff = diff->next;
4601 for (std::map<int, COLORREF>::const_iterator it = removedPositions.begin(); it != removedPositions.end(); ++it)
4603 oLineColors.AddShotColor(it->first, it->second);
4605 } while (false); // error catch
4607 if (!m_bWhitespaceInlineDiffs)
4609 m_ScreenedViewLine[nViewLine].lineColors = oLineColors;
4610 m_ScreenedViewLine[nViewLine].bLineColorsSet = true;
4612 else
4614 m_ScreenedViewLine[nViewLine].lineColorsWhiteSpace = oLineColors;
4615 m_ScreenedViewLine[nViewLine].bLineColorsSetWhiteSpace = true;
4618 return GetLineColors(nViewLine);
4621 void CBaseView::OnEditSelectall()
4623 if (m_pViewData == nullptr)
4624 return;
4625 int nLastViewLine = m_pViewData->GetCount()-1;
4626 if (nLastViewLine < 0)
4627 return;
4628 SetupAllViewSelection(0, nLastViewLine);
4630 CString sLine = GetViewLineChars(nLastViewLine);
4631 m_ptSelectionViewPosStart = SetupPoint(0, 0);
4632 m_ptSelectionViewPosEnd = SetupPoint(sLine.GetLength(), nLastViewLine);
4633 m_ptSelectionViewPosOrigin = SetupPoint(-1, -1);
4635 UpdateWindow();
4638 void CBaseView::FilterWhitespaces(CString& first, CString& second)
4640 FilterWhitespaces(first);
4641 FilterWhitespaces(second);
4644 void CBaseView::FilterWhitespaces(CString& line)
4646 line.Remove(' ');
4647 line.Remove('\t');
4648 line.Remove('\r');
4649 line.Remove('\n');
4652 int CBaseView::GetButtonEventLineIndex(const POINT& point)
4654 const int nLineFromTop = (point.y - HEADERHEIGHT) / GetLineHeight();
4655 int nEventLine = nLineFromTop + m_nTopLine;
4656 nEventLine--; //we need the index
4657 return nEventLine;
4661 BOOL CBaseView::PreTranslateMessage(MSG* pMsg)
4663 if (RelayTrippleClick(pMsg))
4664 return TRUE;
4665 return CView::PreTranslateMessage(pMsg);
4669 void CBaseView::ResetUndoStep()
4671 m_AllState.Clear();
4674 void CBaseView::SaveUndoStep()
4676 if (!m_AllState.IsEmpty())
4678 CUndo::GetInstance().AddState(m_AllState, GetCaretViewPosition());
4680 ResetUndoStep();
4683 void CBaseView::InsertViewData( int index, const CString& sLine, DiffStates state, int linenumber, EOL ending, HIDESTATE hide, int movedline )
4685 m_pState->addedlines.push_back(index);
4686 m_pViewData->InsertData(index, sLine, state, linenumber, ending, hide, movedline);
4689 void CBaseView::InsertViewData( int index, const viewdata& data )
4691 m_pState->addedlines.push_back(index);
4692 m_pViewData->InsertData(index, data);
4695 void CBaseView::RemoveViewData( int index )
4697 m_pState->removedlines[index] = m_pViewData->GetData(index);
4698 m_pViewData->RemoveData(index);
4701 void CBaseView::SetViewData( int index, const viewdata& data )
4703 m_pState->replacedlines[index] = m_pViewData->GetData(index);
4704 m_pViewData->SetData(index, data);
4707 void CBaseView::SetViewState( int index, DiffStates state )
4709 m_pState->linestates[index] = m_pViewData->GetState(index);
4710 m_pViewData->SetState(index, state);
4713 void CBaseView::SetViewLine( int index, const CString& sLine )
4715 m_pState->difflines[index] = m_pViewData->GetLine(index);
4716 m_pViewData->SetLine(index, sLine);
4719 void CBaseView::SetViewLineNumber( int index, int linenumber )
4721 int oldLineNumber = m_pViewData->GetLineNumber(index);
4722 if (oldLineNumber != linenumber) {
4723 m_pState->linelines[index] = oldLineNumber;
4724 m_pViewData->SetLineNumber(index, linenumber);
4728 void CBaseView::SetViewLineEnding( int index, EOL ending )
4730 m_pState->linesEOL[index] = m_pViewData->GetLineEnding(index);
4731 m_pViewData->SetLineEnding(index, ending);
4735 BOOL CBaseView::GetViewSelection( int& start, int& end ) const
4737 if (HasSelection())
4739 start = m_nSelViewBlockStart;
4740 end = m_nSelViewBlockEnd;
4741 return true;
4743 return false;
4746 int CBaseView::Screen2View::GetViewLineForScreen( int screenLine )
4748 RebuildIfNecessary();
4749 if (size() <= screenLine)
4750 return 0;
4751 return m_Screen2View[screenLine].nViewLine;
4754 int CBaseView::Screen2View::size()
4756 RebuildIfNecessary();
4757 return (int)m_Screen2View.size();
4760 int CBaseView::Screen2View::GetSubLineOffset( int screenLine )
4762 RebuildIfNecessary();
4763 if (size() <= screenLine)
4764 return 0;
4765 return m_Screen2View[screenLine].nViewSubLine;
4768 CBaseView::TScreenLineInfo CBaseView::Screen2View::GetScreenLineInfo( int screenLine )
4770 RebuildIfNecessary();
4771 return m_Screen2View[screenLine];
4775 doing partial rebuild, whole screen2view vector is built, but uses ScreenedViewLine cache to do it faster
4777 void CBaseView::Screen2View::RebuildIfNecessary()
4779 if (!m_pViewData)
4780 return; // rebuild not necessary
4782 FixScreenedCacheSize(m_pwndLeft);
4783 FixScreenedCacheSize(m_pwndRight);
4784 FixScreenedCacheSize(m_pwndBottom);
4785 if (!m_bFull)
4787 for (auto it = m_RebuildRanges.cbegin(); it != m_RebuildRanges.cend(); ++it)
4789 ResetScreenedViewLineCache(m_pwndLeft, *it);
4790 ResetScreenedViewLineCache(m_pwndRight, *it);
4791 ResetScreenedViewLineCache(m_pwndBottom, *it);
4794 else
4796 ResetScreenedViewLineCache(m_pwndLeft);
4797 ResetScreenedViewLineCache(m_pwndRight);
4798 ResetScreenedViewLineCache(m_pwndBottom);
4800 m_RebuildRanges.clear();
4801 m_bFull = false;
4803 size_t OldSize = m_Screen2View.size();
4804 m_Screen2View.clear();
4805 m_Screen2View.reserve(OldSize); // guess same size
4806 for (int i = 0; i < m_pViewData->GetCount(); ++i)
4808 if (m_pMainFrame->m_bCollapsed)
4810 while ((i < m_pViewData->GetCount())&&(m_pViewData->GetHideState(i) == HIDESTATE_HIDDEN))
4811 ++i;
4812 if (!(i < m_pViewData->GetCount()))
4813 break;
4815 TScreenLineInfo oLineInfo;
4816 oLineInfo.nViewLine = i;
4817 oLineInfo.nViewSubLine = -1; // no wrap
4818 if (m_pMainFrame->m_bWrapLines && !IsViewLineHidden(m_pViewData, i))
4820 int nMaxLines = 0;
4821 if (IsLeftViewGood())
4822 nMaxLines = std::max<int>(nMaxLines, m_pwndLeft->CountMultiLines(i));
4823 if (IsRightViewGood())
4824 nMaxLines = std::max<int>(nMaxLines, m_pwndRight->CountMultiLines(i));
4825 if (IsBottomViewGood())
4826 nMaxLines = std::max<int>(nMaxLines, m_pwndBottom->CountMultiLines(i));
4827 for (int l = 0; l < (nMaxLines-1); ++l)
4829 oLineInfo.nViewSubLine++;
4830 m_Screen2View.push_back(oLineInfo);
4832 oLineInfo.nViewSubLine++;
4834 m_Screen2View.push_back(oLineInfo);
4836 m_pViewData = NULL;
4838 if (IsLeftViewGood())
4839 m_pwndLeft->BuildMarkedWordArray();
4840 if (IsRightViewGood())
4841 m_pwndRight->BuildMarkedWordArray();
4842 if (IsBottomViewGood())
4843 m_pwndBottom->BuildMarkedWordArray();
4844 UpdateLocator();
4845 RecalcAllVertScrollBars();
4846 RecalcAllHorzScrollBars();
4849 int CBaseView::Screen2View::FindScreenLineForViewLine( int viewLine )
4851 RebuildIfNecessary();
4853 int nScreenLineCount = (int)m_Screen2View.size();
4855 int nPos = 0;
4856 if (nScreenLineCount>16)
4858 // for enough long data search for last screen
4859 // with viewline less than one we are looking for
4860 // use approximate method (based on) binary search using asymmetric start point
4861 // in form 2**n (determined as MSB of length) to go around division and rounding;
4862 // this effectively looks for bit values from MSB to LSB
4864 int nTestBit;
4865 //GetMostSignificantBitValue
4866 // note _BitScanReverse(&nTestBit, nScreenLineCount); can be used instead
4867 nTestBit = nScreenLineCount;
4868 nTestBit |= nTestBit>>1;
4869 nTestBit |= nTestBit>>2;
4870 nTestBit |= nTestBit>>4;
4871 nTestBit |= nTestBit>>8;
4872 nTestBit |= nTestBit>>16;
4873 nTestBit ^= (nTestBit>>1);
4875 while (nTestBit)
4877 int nTestPos = nPos | nTestBit;
4878 if (nTestPos < nScreenLineCount && m_Screen2View[nTestPos].nViewLine < viewLine)
4880 nPos = nTestPos;
4882 nTestBit >>= 1;
4885 while (nPos < nScreenLineCount && m_Screen2View[nPos].nViewLine < viewLine)
4887 nPos++;
4890 return nPos;
4893 void CBaseView::Screen2View::ScheduleFullRebuild(CViewData * pViewData) {
4894 m_bFull = true;
4896 m_pViewData = pViewData;
4899 void CBaseView::Screen2View::ScheduleRangeRebuild(CViewData * pViewData, int nFirstViewLine, int nLastViewLine)
4901 if (m_bFull)
4902 return;
4904 m_pViewData = pViewData;
4906 TRebuildRange Range;
4907 Range.FirstViewLine=nFirstViewLine;
4908 Range.LastViewLine=nLastViewLine;
4909 m_RebuildRanges.push_back(Range);
4912 bool CBaseView::Screen2View::FixScreenedCacheSize(CBaseView* pwndView)
4914 if (!IsViewGood(pwndView))
4916 return false;
4918 const int nOldSize = (int)pwndView->m_ScreenedViewLine.size();
4919 const int nViewCount = std::max<int>(pwndView->GetViewCount(), 0);
4920 if (nOldSize == nViewCount)
4922 return false;
4924 pwndView->m_ScreenedViewLine.resize(nViewCount);
4925 return true;
4928 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView)
4930 if (!IsViewGood(pwndView))
4932 return false;
4934 TRebuildRange Range={0, pwndView->GetViewCount()-1};
4935 ResetScreenedViewLineCache(pwndView, Range);
4936 return true;
4939 bool CBaseView::Screen2View::ResetScreenedViewLineCache(CBaseView* pwndView, const TRebuildRange& Range)
4941 if (!IsViewGood(pwndView))
4943 return false;
4945 if (Range.LastViewLine == -1)
4947 return false;
4949 ASSERT(Range.FirstViewLine >= 0);
4950 ASSERT(Range.LastViewLine < pwndView->GetViewCount());
4951 for (int i = Range.FirstViewLine; i <= Range.LastViewLine; i++)
4953 pwndView->m_ScreenedViewLine[i].Clear();
4955 return false;
4958 void CBaseView::WrapChanged()
4960 m_nMaxLineLength = -1;
4961 m_nOffsetChar = 0;
4964 void CBaseView::OnEditFind()
4966 if (m_pFindDialog)
4967 return;
4969 m_pFindDialog = new CFindDlg(this);
4970 m_pFindDialog->Create(this);
4972 m_pFindDialog->SetFindString(HasTextSelection() ? GetSelectedText() : L"");
4975 LRESULT CBaseView::OnFindDialogMessage(WPARAM /*wParam*/, LPARAM /*lParam*/)
4977 ASSERT(m_pFindDialog != NULL);
4979 if (m_pFindDialog->IsTerminating())
4981 // invalidate the handle identifying the dialog box.
4982 m_pFindDialog = NULL;
4983 return 0;
4986 if(m_pFindDialog->FindNext())
4988 //read data from dialog
4989 m_sFindText = m_pFindDialog->GetFindString();
4990 m_bMatchCase = (m_pFindDialog->MatchCase() == TRUE);
4991 m_bLimitToDiff = m_pFindDialog->LimitToDiffs();
4992 m_bWholeWord = m_pFindDialog->WholeWord();
4994 if (!m_bMatchCase)
4995 m_sFindText = m_sFindText.MakeLower();
4997 BuildFindStringArray();
4998 OnEditFindnext();
5001 return 0;
5004 void CBaseView::OnEditFindnextStart()
5006 if (m_pViewData == nullptr)
5007 return;
5008 if (HasTextSelection())
5010 m_sFindText = GetSelectedText();
5011 m_bMatchCase = false;
5012 m_bLimitToDiff = false;
5013 m_bWholeWord = false;
5014 m_sFindText = m_sFindText.MakeLower();
5016 BuildFindStringArray();
5017 OnEditFindnext();
5019 else
5021 m_sFindText.Empty();
5022 BuildFindStringArray();
5026 void CBaseView::OnEditFindprevStart()
5028 if (m_pViewData == nullptr)
5029 return;
5030 if (HasTextSelection())
5032 m_sFindText = GetSelectedText();
5033 m_bMatchCase = false;
5034 m_bLimitToDiff = false;
5035 m_bWholeWord = false;
5036 m_sFindText = m_sFindText.MakeLower();
5038 BuildFindStringArray();
5039 OnEditFindprev();
5041 else
5043 m_sFindText.Empty();
5044 BuildFindStringArray();
5048 bool CBaseView::StringFound(const CString& str, SearchDirection srchDir, int& start, int& end) const
5050 start = str.Find(m_sFindText);
5051 if ((srchDir==SearchPrevious)&&(start>=0))
5053 int laststart = start;
5056 start = laststart;
5057 laststart = str.Find(m_sFindText, laststart+1);
5058 } while (laststart >= 0);
5060 end = start + m_sFindText.GetLength();
5061 bool bStringFound = (start >= 0);
5062 if (bStringFound && m_bWholeWord)
5064 if (start)
5065 bStringFound = IsWordSeparator(str.Mid(start-1,1).GetAt(0));
5067 if (bStringFound)
5069 if (str.GetLength() > end)
5070 bStringFound = IsWordSeparator(str.Mid(end, 1).GetAt(0));
5073 return bStringFound;
5076 void CBaseView::OnEditFindprev()
5078 Search(SearchPrevious);
5081 void CBaseView::OnEditFindnext()
5083 Search(SearchNext);
5086 void CBaseView::Search(SearchDirection srchDir)
5088 if (m_sFindText.IsEmpty())
5089 return;
5090 if(!m_pViewData)
5091 return;
5093 POINT start = m_ptSelectionViewPosEnd;
5094 POINT end;
5095 end.y = m_pViewData->GetCount()-1;
5096 if (end.y < 0)
5097 return;
5099 if (srchDir==SearchNext)
5100 end.x = GetViewLineLength(end.y);
5101 else
5103 end.x = m_ptSelectionViewPosStart.x;
5104 start.x = 0;
5107 if (!HasTextSelection())
5109 start.y = m_ptCaretViewPos.y;
5110 if (srchDir==SearchNext)
5111 start.x = m_ptCaretViewPos.x;
5112 else
5114 start.x = 0;
5115 end.x = m_ptCaretViewPos.x;
5118 CString sSelectedText;
5119 int startline = -1;
5120 for (int nViewLine=start.y; ;srchDir==SearchNext ? nViewLine++ : nViewLine--)
5122 if (nViewLine < 0)
5124 nViewLine = m_pViewData->GetCount()-1;
5125 startline = start.y;
5127 if (nViewLine > end.y)
5129 nViewLine = 0;
5130 startline = start.y;
5132 if (startline >= 0)
5134 if (nViewLine == startline)
5135 break;
5137 switch (m_pViewData->GetState(nViewLine))
5139 case DIFFSTATE_EMPTY:
5140 break;
5141 case DIFFSTATE_UNKNOWN:
5142 case DIFFSTATE_NORMAL:
5143 if (m_bLimitToDiff)
5144 break;
5145 case DIFFSTATE_REMOVED:
5146 case DIFFSTATE_REMOVEDWHITESPACE:
5147 case DIFFSTATE_ADDED:
5148 case DIFFSTATE_ADDEDWHITESPACE:
5149 case DIFFSTATE_WHITESPACE:
5150 case DIFFSTATE_WHITESPACE_DIFF:
5151 case DIFFSTATE_CONFLICTED:
5152 case DIFFSTATE_CONFLICTED_IGNORED:
5153 case DIFFSTATE_CONFLICTADDED:
5154 case DIFFSTATE_CONFLICTEMPTY:
5155 case DIFFSTATE_CONFLICTRESOLVED:
5156 case DIFFSTATE_IDENTICALREMOVED:
5157 case DIFFSTATE_IDENTICALADDED:
5158 case DIFFSTATE_THEIRSREMOVED:
5159 case DIFFSTATE_THEIRSADDED:
5160 case DIFFSTATE_MOVED_FROM:
5161 case DIFFSTATE_MOVED_TO:
5162 case DIFFSTATE_YOURSREMOVED:
5163 case DIFFSTATE_YOURSADDED:
5164 case DIFFSTATE_EDITED:
5166 sSelectedText = GetViewLineChars(nViewLine);
5167 if (nViewLine==start.y)
5168 sSelectedText = srchDir==SearchNext ? sSelectedText.Mid(start.x) : sSelectedText.Left(start.x);
5169 if (!m_bMatchCase)
5170 sSelectedText = sSelectedText.MakeLower();
5171 int startfound = -1;
5172 int endfound = -1;
5173 if (StringFound(sSelectedText, srchDir, startfound, endfound))
5175 HighlightViewLines(nViewLine, nViewLine);
5176 m_ptSelectionViewPosStart.x = startfound;
5177 m_ptSelectionViewPosEnd.x = endfound;
5178 if (nViewLine==start.y)
5180 m_ptSelectionViewPosStart.x += start.x;
5181 m_ptSelectionViewPosEnd.x += start.x;
5183 m_ptSelectionViewPosEnd.x = m_ptSelectionViewPosStart.x + m_sFindText.GetLength();
5184 m_ptSelectionViewPosStart.y = nViewLine;
5185 m_ptSelectionViewPosEnd.y = nViewLine;
5186 m_ptCaretViewPos = m_ptSelectionViewPosStart;
5187 UpdateViewsCaretPosition();
5188 EnsureCaretVisible();
5189 Invalidate();
5190 return;
5193 break;
5196 m_pMainFrame->m_nMoveMovesToIgnore = MOVESTOIGNORE;
5199 CString CBaseView::GetSelectedText() const
5201 CString sSelectedText;
5202 POINT start = m_ptSelectionViewPosStart;
5203 POINT end = m_ptSelectionViewPosEnd;
5204 if (!HasTextSelection())
5206 if (!HasSelection())
5207 return sSelectedText;
5208 start.y = m_nSelViewBlockStart;
5209 start.x = 0;
5210 end.y = m_nSelViewBlockEnd;
5211 end.x = GetViewLineLength(m_nSelViewBlockEnd);
5213 // first store the selected lines in one CString
5214 for (int nViewLine=start.y; nViewLine<=end.y; nViewLine++)
5216 switch (m_pViewData->GetState(nViewLine))
5218 case DIFFSTATE_EMPTY:
5219 break;
5220 case DIFFSTATE_UNKNOWN:
5221 case DIFFSTATE_NORMAL:
5222 case DIFFSTATE_REMOVED:
5223 case DIFFSTATE_REMOVEDWHITESPACE:
5224 case DIFFSTATE_ADDED:
5225 case DIFFSTATE_ADDEDWHITESPACE:
5226 case DIFFSTATE_WHITESPACE:
5227 case DIFFSTATE_WHITESPACE_DIFF:
5228 case DIFFSTATE_CONFLICTED:
5229 case DIFFSTATE_CONFLICTED_IGNORED:
5230 case DIFFSTATE_CONFLICTADDED:
5231 case DIFFSTATE_CONFLICTEMPTY:
5232 case DIFFSTATE_CONFLICTRESOLVED:
5233 case DIFFSTATE_IDENTICALREMOVED:
5234 case DIFFSTATE_IDENTICALADDED:
5235 case DIFFSTATE_THEIRSREMOVED:
5236 case DIFFSTATE_THEIRSADDED:
5237 case DIFFSTATE_MOVED_FROM:
5238 case DIFFSTATE_MOVED_TO:
5239 case DIFFSTATE_YOURSREMOVED:
5240 case DIFFSTATE_YOURSADDED:
5241 case DIFFSTATE_EDITED:
5242 sSelectedText += GetViewLineChars(nViewLine);
5243 sSelectedText += _T("\r\n");
5244 break;
5247 // remove the non-selected chars from the first line, last line and last \r\n
5248 int nLeftCut = start.x;
5249 int nRightCut = GetViewLineChars(end.y).GetLength() - end.x + 2;
5250 sSelectedText = sSelectedText.Mid(nLeftCut, sSelectedText.GetLength()-nLeftCut-nRightCut);
5251 return sSelectedText;
5254 void CBaseView::OnEditGotoline()
5256 if (m_pViewData == NULL)
5257 return;
5258 // find the last and first line number
5259 int nViewLineCount = m_pViewData->GetCount();
5261 int nLastLineNumber = DIFF_EMPTYLINENUMBER;
5262 for (int nViewLine=nViewLineCount-1; nViewLine>=0; --nViewLine)
5264 nLastLineNumber = m_pViewData->GetLineNumber(nViewLine);
5265 if (nLastLineNumber!=DIFF_EMPTYLINENUMBER)
5267 break;
5270 if (nLastLineNumber==DIFF_EMPTYLINENUMBER || nLastLineNumber==0) // not numbered line foud or last one is first
5272 return;
5274 nLastLineNumber++;
5275 int nFirstLineNumber=1; // first is always 1
5277 CString sText;
5278 sText.Format(IDS_GOTOLINE, nFirstLineNumber, nLastLineNumber);
5280 CGotoLineDlg dlg(this);
5281 dlg.SetLabel(sText);
5282 dlg.SetLimits(nFirstLineNumber, nLastLineNumber);
5283 if (dlg.DoModal() == IDOK)
5285 for (int nViewLine = 0; nViewLine < nViewLineCount; ++nViewLine)
5287 if ((m_pViewData->GetLineNumber(nViewLine)+1) == dlg.GetLineNumber())
5289 HighlightViewLines(nViewLine, nViewLine);
5290 return;